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:
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> ] Commands: /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. /cdl Deletes the contents of the download cache /ldl Lists the contents of the download cache /? Displays a detailed help screen Options: /r <reference_scheme> <reference_id> <description> Specifies a traced reference to install (/i, /il) or uninstall (/u, /ul). /f Forces reinstall of an assembly. /nologo Suppresses display of the logo banner /silent 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
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 07020000002400005253413200040000010001007b4a659111bf02da73fb30902e75b88d03b5f7 9f94b26563a57b102ea9e794a254b67d8a0a576abddd2eadebf6f61d24c172e7ab7f10cce5fa64 766e4b9ef9b5b8449e1dbd860a765f3bb2f7c64f82513a2c6dc44b26dfd15ec51995e22e35c8d4 882662cc4450aee73eb3708de79d1764dba8af90f31581328d675a22a5c0a1a35992d60f8205b2 3a3563a6624960d8eca999b3f7129648aeb622a3b316339fedb3474ab7271af2d769274cfc8091 417b204ec1afe93c4be8f8c96762b315d14909a17f8014184ab8454d1bc8a8da9f8acb1b04b5b6 6f3803bb8eba979c3f2492072aebda258522264a30467ce7f46a8905b0359d8519b5a11aadf8d2 120cc6d184fb1d42f86e915fd583b2ba393ee855f0d2423019db0756f672154c0b37f7c965eaa6 6c05659b37001bf4dcc81253cad9eacdde44585ef667f234a4a26e7af117d9936225486396382f c335ba38fcc29dd548b0c2fe452bac6c1f34a3f05a15f52a528d56bec5952cdf2fef6507a40315 23e1b1df626bcc489042e27b5c5ad6cfb98702da24badd3afde117e014d6b8a29ba6073690113e f61b2872516d5633a037d56e8cb2d9f8f7154f0ed4f65bc821a233ac8a503b09b453a3f59f7ec3 6150984e929e126617e705aa5a6deb13abc8f23cc6039800a2df06e64b56d1ccb3742b23e62b4b ba16148f2cfbc8aeca8034a0cbffc01d929019a0803768cff828968575151b5933ecdb2b710817 6f8abcbe82c4de3a5f2a6face2837fc34dc96b60fe1e8960bc9f2ca77231c7cdf8c945670ae3b5 4f88b16d8871ba3d403853 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("")]
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:
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.
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.
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:
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
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
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.