Team LiB
Previous Section Next Section

Installing .NET Applications

Most everyone understands that it will take a fair amount of time to develop a useful software application. However, it might take some convincing that it will also take a fair amount of time to develop an installation program for that application. After all, an installation program is just that: a software program. The only difference is that its sole purpose is to install your application. Unfortunately, not enough emphasis is placed upon developing the installation program and all too often it is rushed and lacks the necessary functionality. Fortunately, when it comes to the installation of .NET applications, there are many different solutions ranging from traditional installation programs and private deployments to Web deployment.

Understanding Assembly Deployment

Assemblies are the basic unit of deployment for any .NET application. It can be the main application (.exe) or a separate library file (.dll). When an application references an assembly, the Common Language Runtime must be able to locate and load the file in order for the application to function properly.

Locating the Correct Assembly to Load

Although you can override this behavior in the configuration file, by default the Common Language Runtime attempts to load and bind to an assembly with the exact version number as was built with the application. The Common Language Runtime determines the correct assembly by performing the following steps:

The Common Language Runtime determines the correct version of the assembly by checking the following configuration files (in the order specified here):

  • Application configuration file The Common Language Runtime checks the application configuration file for any information that might override the version information stored in the calling assembly's manifest.

  • Publisher policy file Next, the Common Language Runtime checks the publisher policy file, if one exists. The publisher policy file is used by component makers to override all applications to direct the use of an assembly to the newer version. This is useful when a publisher provides hot fixes of service packs.

  • Machine configuration file Finally, the Common Language Runtime checks the machine configuration file. This file is used to set local (machine-specific) policies. Any setting in this file overrides configurations set in the application or publisher configuration files.

The Common Language Runtime checks to see whether the desired assembly name has been previously bound. If so, it uses the previously loaded assembly.

Next, the Common Language Runtime checks the global assembly cache (GAC). In order for an assembly to be placed in the GAC, it must have a strong name.

Finally, the Common Language Runtime probes for the desired assembly. This is done by performing the following steps:

  • The directory specified by the codebase element specified in the configuration file is checked for the assembly.

  • If no codebase element is found in the configuration file, the Common Language Runtime probes for the desired assembly using the following criteria: application base, culture, name, and private bin path.

Placing Assemblies in the Global Assembly Cache

The global assembly cache (GAC) is used as a central repository to store shared assemblies for use by varying .NET applications. The GAC allows for multiple versions of the same assembly to be stored and used by a requesting .NET assembly. To add an assembly to the GAC, a utility named gacutil.exe is provided. Use the -i parameter to add an assembly to the GAC and the -r parameter to remove one. Listing 20.1 displays all the attributes that the gacutil.exe accepts. To display this list on your computer, simply type gacutil.exe at the command prompt.

Listing 20.1. Output of the gacutil.exe Default Execution
   Microsoft (R) .NET Global Assembly Cache Utility.  Version 1.1.4322.573
   Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.

   Usage: Gacutil <command> [ <options> ]
    /i <assembly_path> [ /r <...> ] [ /f ]
          Installs an assembly to the global assembly cache.
         /il <assembly_path_list_file> [ /r <...> ] [ /f ]
           Installs one or more assemblies to the global assembly cache.

    /u <assembly_display_name> [ /r <...> ]
          Uninstalls an assembly from the global assembly cache.

         /ul <assembly_display_name_list_file> [ /r <...> ]
           Uninstalls one or more assemblies from the global assembly cache.

         /ungen <assembly_name>
           Uninstalls a native image installed via the NGEN utility.

         /l [ <assembly_name> ]
           List the global assembly cache filtered by <assembly_name>

         /lr [ <assembly_name> ]
           List the global assembly cache with all traced references.

           Deletes the contents of the download cache

           Lists the contents of the download cache

           Displays a detailed help screen

         /r <reference_scheme> <reference_id> <description>
           Specifies a traced reference to install (/i, /il) or uninstall (/u, /ul).

           Forces reinstall of an assembly.

           Suppresses display of the logo banner

           Suppresses display of all output

Because the GAC is a common place to store shared assemblies and to allow for side-by-side versioning, the GAC requires that all assemblies have a strong name. Therefore, to add an assembly to the GAC, you must first give it a strong name and sign the assembly.


Although the GAC enables you to store common assemblies for use by running applications, you must still keep a private copy of this assembly for the compilation of any assemblies that reference this assembly. The compiler will not find any assembly that is in the GAC at compile time.

Creating a Strongly Named Assembly

A strongly named assembly consists of its test name, version number, culture information, public key, and digital signature. A strongly named assembly grantees name uniqueness. This is done by using unique key pairs.

Generating Public and Private Key Pairs

To create a strongly named assembly, you must first generate a private and public key pair. The private key is used to create a digital signature. The private key should be kept, as its name implies, private. The public key is used as part of the strong name and is used by the Common Language Runtime to validate the digital signature of the assembly. You can use the strong name utility (sn.exe) to create a private/public key pair. The following sample illustrates how to create a random key pair and store it in a file called keypair.snk:

sn.exe -k keypair.snk

To view the public key stored in the keypair.snk file, use the following command:

sn.exe -tp keypair.snk

Listing 20.2 shows the contents of this file.

Listing 20.2. Public Key Information Stored in the keypair.snk File
Public key is

Public key token is 7ba943661cd72246

Modifying the AssemblyInfo.cs File

The next step in creating a strong name for an assembly is specifying the version number, culture information and key pair information. To do this, you can modify the System.Reflection.AssemblyCulture and System.Reflection.AssemblyVersion attributes. If you are using Visual Studio .NET for your development, it automatically creates a file called AssemblyInfo.cs with these attributes already included. All you have to do is fill in the information. Listing 20.3 shows a default AssemblyInfo.cs file that was created for this sample project.

Listing 20.3. Default AssemblyInfo.cs File
   using System.Reflection;
   using System.Runtime.CompilerServices;

   // General Information about an assembly is controlled through the following 
   // set of attributes. Change these attribute values to modify the information
   // associated with an assembly.
   [assembly: AssemblyTitle("")]
   [assembly: AssemblyDescription("")]
   [assembly: AssemblyConfiguration("")]
   [assembly: AssemblyCompany("")]
   [assembly: AssemblyProduct("")]
   [assembly: AssemblyCopyright("")]
   [assembly: AssemblyTrademark("")]
   [assembly: AssemblyCulture("")]

   // Version information for an assembly consists of the following four values:
   //      Major Version
   //      Minor Version 
   //      Build Number
   //      Revision
   // You can specify all the values or you can default the Revision and Build Numbers 
   // by using the '*' as shown below:
   [assembly: AssemblyVersion("1.0.*")] 

   // In order to sign your assembly you must specify a key to use. Refer to the 
   // Microsoft .NET Framework documentation for more information on assembly signing.
   // Use the attributes below to control which key is used for signing. 
   // Notes: 
   //   (*) If no key is specified, the assembly is not signed.
   //   (*) KeyName refers to a key that has been installed in the Crypto Service
   //       Provider (CSP) on your machine. KeyFile refers to a file which contains
   //       a key.
   //   (*) If the KeyFile and the KeyName values are both specified, the 
   //       following processing occurs:
   //       (1) If the KeyName can be found in the CSP, that key is used.
   //       (2) If the KeyName does not exist and the KeyFile does exist, the key 
   //           in the KeyFile is installed into the CSP and used.
   //   (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
   //       When specifying the KeyFile, the location of the KeyFile should be
   //       relative to the project output directory which is
   //       %Project Directory%\obj\<configuration>. For example, if your KeyFile is
   //       located in the project directory, you would specify the AssemblyKeyFile 
   //       attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
   //   (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
   //       documentation for more information on this.
   [assembly: AssemblyDelaySign(false)]
   [assembly: AssemblyKeyFile("")]
   [assembly: AssemblyKeyName("")]

If you modify the AssemblyVersion attribute to

[assembly: AssemblyVersion("")]

you will set the version for this assembly to By leaving the culture to its default, you are File saying that this assembly is suitable for all cultures.

The next step is to set the AssemblyKeyFile attribute to the correct location. You will recall that in the previous section you created a key pair and stored it in the file keypair.snk. To tell the compiler to use this file, change the AssemblyKeyFile attribute as follows:

[assembly: AssemblyKeyFile("keypair.snk")]

Please note that the example this assumes that the keypair.snk file is in the search path for this project. If not, simply use a fully qualified path for the keypair.snk file. If you chose to store your keys in the Windows Cryptographic Service Provider (CSP) container, you should modify the AssemblyKeyName attribute instead.

With all the preceding changes made to a Visual Studio .NET project, when you next compile the project, the output will be a strongly named assembly.

Delayed Signing of an Assembly

For some organizations, usually larger ones, it is unacceptable to allow anyone access to the private key that is used to sign assemblies. In the previous example, this presents a problem when you need to sign the assembly. To overcome this challenge, .NET allows for the delayed signing of assemblies. This enables you to test your assembly and still have one person or group maintain ownership over the private key for the organization. In order for you to instruct the .NET compiler to delay the signing of an assembly, you must first change the AssemblyDelaySign attribute from false to true.

[assembly: AssemblyDelaySign(true)]

You must then specify, in the same manner as you did in the previous section, a file or CSP container that contains the public key. To extract the public key from the keypair.snk file to a separate file, you can use the strong name utility (sn.exe). The following command extracts the public key from the keypair.snk file and copies it to the publickey.snk file:

sn -p keypair.snk publickey.snk

Now that you have the public key stored in the publickey.snk file, modify the AssemblyKeyFile attribute to point to the publickey.snk file:

[assembly: AssemblyKeyFile("publickey.snk")]

Now, as before, simply compile the project. However, this time, the assembly does not have a strong name. It is simply ready to be signed by the person or group in your organization that has ownership of the private key. To sign the assembly now, simply use the -R argument with the strong name utility (sn.exe) as follows:

sn -R StrongNamedAssemblyExample.dll keypair.snk

This will create a strong name for this assembly.

Verify a Strongly Named Assembly

Sometimes just believing that a particular task was performed correctly is not enough. You or your QA department might want to verify this action. To verify that an assembly has a strong name, you can again use the strong name utility (sn.exe) along with the -v argument to accomplish the task. By typing the command

sn -v StrongNamedAssemblyExample.dll

you should receive an output similar to that in Listing 20.4.

Listing 20.4. Verifying the Strong Name of an Assembly
sn -v strongnamedassemblyexample.dll

Microsoft (R) .NET Framework Strong Name Utility  Version 1.1.4322.573
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.

Assembly 'strongnamedassemblyexample.dll' is valid

Private Installations

If your application does not require that you share assemblies, you can choose to perform a private installation. A private installation is simply taking your files (assemblies) from the distribution disk and placing them in a directory or directories on the designated machine. Because the .NET Framework does not require the registration of files, all you have to do is simply copy the files from one place and put them in another. Because it will automatically reproduce the correct directory structure, generally, you will use the XCopy command to perform this task.

    Team LiB
    Previous Section Next Section