Deploying Shared Assemblies



Deploying Shared Assemblies

Implement versioning

Plan, configure and deploy side-by-side deployments and applications.

A shared assembly is shared among multiple applications on a machine. It is reasonable to store a shared assembly in a common place, which is well known to all the applications that use the assembly. However, if shared assemblies are identified by just their names—as the private assemblies are—then there is a problem because there might be a case when two software publishers might use the same name for an assembly, thereby overwriting files and causing applications using those assemblies to behave abnormally.

To resolve this problem, the .NET Framework requires all shared assemblies to have a strong name. A strong name uses four attributes to identify an assembly:

  • Simple name

  • Version number

  • Culture identity (optional)

  • Public key token

A regular Windows folder can differentiate files based on just their simple names and not their strong names. Therefore, to store strongly-named assemblies in a well-known shared location, you need a special type of storage. The .NET Framework provides this storage in the form of the global assembly cache (GAC). In addition to providing a shared location, the GAC also provides the following benefits for shared assemblies:

  • Integrity Check— When assemblies are installed in the GAC, the GAC applies a strong integrity check on the assembly. This check guarantees that the contents of the assembly have not been changed since it was built.

  • Security— The GAC allows only users with administrator privileges to modify its contents.

  • Side-by-side Versioning— Multiple assemblies with the same name but different version number can be maintained in the global assembly cache.

In this section, you'll learn the following about shared assemblies:

  • How to assign a strong name to an assembly

  • How to add an assembly to the GAC

  • How to reference an assembly from the GAC

  • What the binding policy is for shared assemblies

  • How the CLR binds to a shared assembly

  • How to delay sign a shared assembly

Assigning a Strong Name to an Assembly

To create a strong name, you need an assembly's simple name, version number, an optional culture identity, and a key pair. The key pair consists of two related pieces of binary data: a public key and a private key.

The public key represents the identity of a software publisher. When you create a strongly named assembly, the public key is stored in the assembly manifest, along with other identification information, such as name, version number, and culture. This scheme does not look foolproof because the public key is easily available from the assembly manifest and one can easily fake an assembly identity with some other company's public key. To verify that only the legitimate owner of the public key has created the assembly, an assembly is signed with the publisher's private key. The private key is assumed to be known only to the assembly's publisher. The process of signing an assembly and verifying its signature works like this:

  • Signing an assembly— When you sign an assembly, a cryptographic hash of the assembly's contents is computed. The hash is then encoded with the private key and is stored within the assembly. The public key is stored in the assembly manifest.

  • Verifying the signature— When the CLR verifies an assembly's identity, it reads the public key from the assembly manifest and uses it to decrypt the cryptographic hash that is stored in the assembly. It then recalculates the hash for the current contents of the assembly. If the two hashes match, this ensures two things: The contents of the assembly were not tampered with after the assembly was signed and only the party that has a private key associated with the public key stored in the assembly has signed the assembly.

You can easily generate public/private key pairs by using the Strong Name tool (sn.exe), which is available in the .NET Framework SDK.

NOTE

Signing a Multifile Assembly If an assembly consists of multiple files, just the file that contains the assembly manifest needs to be signed. The assembly manifest already contains file hashes for all the files that constitute the assembly implementation. The CLR can easily determine whether a file has been tampered with by matching its actual hash with what is stored in the assembly manifest.


Step-by-Step 10.5 shows you how to create a public/private key pair by using the Strong Name tool (sn.exe).

STEP BY STEP

10.5 Creating a Public/Private Key Pair by Using the Strong Name Tool (sn.exe)

  1. From the Visual Studio .NET program group in the Windows Start menu, launch the Visual Studio .NET command prompt.

  2. Navigate to the 320C10 solution folder and issue the following command to create a pair of public/private keys:

    
    sn –k 70320.snk
    
    
  3. Both the public and private keys are created and stored in a file named 70320.snk.

Step-by-Step 10.6 shows you how to create a strongly-named assembly. You use the key file generated in Step-by-Step 10.5 to assign a strong name to an assembly.

STEP BY STEP

10.6 Assigning a Strong Name to an Assembly

  1. Add a new Visual C# .NET Class Library project to the solution. Name the project RandomNumberGenerator.

  2. Add a Component Class to the project and name it RandomNumberGenerator.cs. Delete Class1.cs.

  3. Switch to the code view. Add the following lines of code just after the Component Designer–generated code section:

    
    //stores minValue and maxValue
    
    private int minValue=1, maxValue=100;
    
    
    
    public int MinValue
    
    {
    
        get
    
        {
    
            return minValue;
    
        }
    
        set
    
        {
    
            minValue = value;
    
        }
    
    }
    
    
    
    public int MaxValue
    
    {
    
        get
    
        {
    
            return maxValue;
    
        }
    
        set
    
        {
    
            maxValue = value;
    
        }
    
    }
    
    public int GetRandomNumber()
    
    {
    
        Random r = new Random();
    
        return r.Next(minValue, maxValue);
    
    }
    
    
  4. Open the AssemblyInfo.cs file of the RandomNumberGenerator project. Scroll down in the file and change the AssemblyVersion and AssemblyKeyFile attributes as follows:

    
    [assembly: AssemblyVersion("1.0")]
    
    [assembly: AssemblyKeyFile(@"..\..\..\70320.snk")]
    
    
  5. Build the project. RandomNumberGenerator.dll is generated, and a strong name is assigned to the file based on the specified key file.

In Step-by-Step 10.6 you change the AssemblyVersion attribute of the assembly from 1.0.* to 1.0. The assembly's version consists of up to four parts:


<major>.<minor>.<build>.<revision>

If you want to use a fixed version value, you can hard-code it. The default value of the version uses an asterisk in place of build and revision numbers; this changes the build and revision each time you modify and recompile the project. The build is calculated as the number of days since January 1, 2000, and the revision is calculated as the number of seconds since midnight divided by 2.

At runtime, the CLR uses version information to load a shared assembly. In the next few examples, you may change and compile your projects several times and as a result, you'll change the version of the assembly if you use the default version property. This will undesirably lead to multiple versions of the same assembly being installed in the GAC when you wanted only the most recent one.

To have control over version numbers in the examples in this chapter, I'll hardcode the version number. Shortly you will also learn how to install multiple versions of an assembly in the GAC and how to manage their side-by-side execution.

In Step-by-Step 10.6 you used Visual Studio .NET to attach a strong name to an assembly. If you want to do this by using a command-line tool, you can use the Assembly Generation tool (al.exe) with the –keyfile option.

Adding an Assembly to the GAC

After you have associated a strong name with an assembly, you can place the assembly in the GAC. There are several ways you can add an assembly to the GAC. Using the Windows Installer is the recommended approach, but there are some quick alternatives, too. However, you should use these quick approaches only for development purposes; they are not recommended for installing assemblies on the end user's computer.

Using Windows Installer to Add an Assembly to the GAC

Using Microsoft Windows Installer is the preferred way of adding assemblies to the GAC. Windows Installer maintains a reference count for assemblies in the GAC and provides uninstallation support. You will learn how to add assemblies using Windows Installer technology through the setup and deployment projects of Visual Studio .NET a little later in this chapter.

Using Windows Explorer to Add an Assembly to the GAC

The Assembly Cache Viewer Shell Extension (shfusion.dll) is installed as part of the .NET Framework. This extension allows you to view the complex structure of the GAC using Windows Explorer and allows administrators to install and uninstall assemblies using drag and drop and menu operations.

STEP BY STEP

10.7 Adding an Assembly to the GAC by Using Windows Explorer

  1. Open Windows Explorer. Navigate to the assembly cache folder. It is usually c:\WINNT\assembly or C:\Windows\assembly (see Figure).

    6. The Assembly Cache Viewer Shell Extension enables you to view and manage the contents of the assembly cache by using Windows Explorer.

    graphics/10fig06.jpg

  2. Using Windows Explorer, drag the RandomNumberGenerator.dll file created in Step-by-Step 10.6 (in the bin\Release folder of the project) and drop it in the assembly cache folder.

  3. In the assembly cache folder, right-click the RandomNumberGenerator.dll and select Properties from the shortcut menu. The Properties dialog box appears, as shown in Figure.

    Figure. You can view the properties of the RandomNumberGenerator assembly that is installed in the GAC.

    graphics/10fig07.jpg

If you want to remove a file from the GAC, you just delete it from Windows Explorer by selecting File, Delete or by selecting Delete from the assembly's shortcut menu.

NOTE

The Assembly Cache Folder The assembly cache folder actually contains two caches: the GAC and the native image cache. When you view the assembly cache folder by using Windows Explorer, the Assembly Cache Viewer Shell Extension shows you a combined list of both caches. You can determine whether an assembly is from the GAC or from the native image cache by looking at the Type field in the list. When you add an assembly to the assembly cache folder by using Windows Explorer, it is added to the GAC. To add an assembly to the native image cache, you need to use the Native Image Generation tool (ngen.exe). The ngen.exe tool pre-compiles assemblies into processor-specific code.


Using the .NET Framework Configuration Tool to Add an Assembly to the GAC

You can also use the .NET Framework Configuration tool (mscorcfg.msc) to manage an assembly in the GAC. Step-by-Step 10.8 guides you through the process of adding an assembly to the GAC by using the .NET Framework Configuration tool.

STEP BY STEP

10.8 Adding an Assembly to the GAC by Using the .NET Framework Configuration Tool

  1. Open the Microsoft .NET Framework Configuration tool from the Administrative Tools section of Windows Control Panel. Select the assembly cache folder on the left pane under the My Computer node.

  2. In the right pane, click the hyperlink Add an Assembly to the Assembly Cache. The Add an Assembly dialog box appears. Navigate to the RandomNumberGenerator.dll file in the MathLib project and click the OK button.

  3. Click the other hyperlink, View List of Assemblies in the Assembly Cache. A list of installed assemblies appears. Ensure that the RandomNumberGenerator assembly is in this list, as shown in Figure.

    8. You can add assemblies in the GAC by using the .NET Framework Configuration Tool.

    graphics/10fig08.jpg

To uninstall an assembly by using the .NET Framework Configuration tool, you just select Action, Delete, or select Delete from the assembly's shortcut menu.

In addition to helping you add or remove assemblies, the .NET Framework Configuration tool helps you configure assemblies and manage their runtime security policies.

Using the Global Assembly Cache Tool (gacutil.exe) to Add an Assembly to the GAC

GacUtil.exe is a command-line tool that is especially useful for adding and removing assemblies from the GAC via a program script or a batch file. Step-by-Step 10.9 shows you how to add an assembly to the GAC by using gacutil.exe.

STEP BY STEP

10.9 Adding an Assembly to the GAC by Using the Global Assembly Cache Tool

  1. From the Visual Studio .NET program group in the Windows Start menu, launch the Visual Studio .NET command prompt.

  2. Change the directory to the folder where the RandomNumberGenerator.dll file resides in the RandomNumberGenerator project—in this case, the project's bin\Release directory.

  3. Issue the following command to install the assembly to the GAC, as shown here:

    
    gacutil /i RandomNumberGenerator.dll
    
    

You can list all the assemblies in the GAC by using the gacutil.exe tool with the /l option. You can use the /u option with the name of the assembly (without the file extension) to uninstall the assembly from the GAC:


gacutil /u RandomNumberGenerator

You can also choose to uninstall an assembly of a specific version and specific culture from the GAC by specifying its version, culture, and public key, along with the name of the assembly:

gacutil /u RandomNumberGenerator,Version=1.0.0.0, Culture=neutral, graphics/ccc.gifPublicKeyToken=f26af4dbb33881b1

Referencing an Assembly from the GAC

Normally when you refer to an assembly in a Visual Studio .NET project, you can invoke the Add Reference dialog box and browse to the desired assembly. But after you have added an assembly to the GAC, this approach does not work because the GAC has a complex structure that cannot be directly enumerated by the Add Reference dialog box.

When you view the GAC by using the tools mentioned in the preceding section, you see an abstraction of its structure. If you instead switch to the command prompt and change the directory to the GAC folder, you see that the GAC is actually made up of various subdirectories, one for each assembly. Each of these directories further has subdirectories, whose names depend on the assemblies' versions and public keys. Each subdirectory stores the actual assembly file, along with some additional assembly information. Figure shows how the GAC entry is made for the RandomNumberGenerator component on my computer.

9. You can see how the assemblies are maintained in the GAC by exploring the GAC through the command window.

graphics/10fig09.jpg

A good practice is to keep a copy of the assemblies installed in the GAC somewhere outside the GAC, where they are easily accessible via a pathname. You can then easily reference these assemblies through the Add Reference dialog box by browsing to the correct path. In fact, the .NET Framework uses the same techniques for all its own assemblies stored in the GAC. The .NET Framework also stores copies of those assemblies in the folder where the .NET Framework is installed.

Although you can add a reference to an assembly by browsing to the folder where it is stored, the convenient way is to have its name directly displayed in the Add Reference dialog box so that you can just check it to select it.

Step-by-Step 10.10 shows how to instruct Visual Studio .NET to add assemblies that are stored in a custom folder to the Add Reference dialog box.

STEP BY STEP

10.10 Displaying an Assembly in the Add Reference Dialog Box

  1. Open the Registry Editor by launching regedit.exe from the Run dialog box, which you access by selecting Start, Run.

  2. In the Registry Editor, browse to the key named HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\AssemblyFolders.

  3. At this level create a new key and name it MyAssemblies.

  4. Double-click the (Default) value in the new key and change its value to the location of RandomNumberGenerator.dll in the bin\Release folder of the RandomNumberGenerator project, as shown in Figure.

    10. You can set the value of a Registry key in the Edit String dialog box.

    graphics/10fig10.jpg

  5. Close the Registry Editor. Close all instances of Visual Studio .NET.

In Step-by-Step 10.11 you create a small Windows application that, when executed, loads the components that are installed in the GAC. Before you begin Step-by-Step 10.11, make sure you have already installed the file RandomNumberGenerator.dll in the GAC.

STEP BY STEP

10.11 Creating a Windows Application That Uses the RandomNumberGenerator Component

  1. Add a new Windows application project to the solution and name it RandomNumberApp.

  2. In Solution Explorer, rename the Form1.cs file in the project to RandomNumberApp.cs. Switch to the code view of the form and modify all references to Form1 so that they refer to RandomNumberApp instead.

  3. Open the Add Reference dialog box. You should notice the RandomNumberGenerator component under the .NET tab. Add a reference to the RandomNumberGenerator component. Set the Copy Local property of the RandomNumberGenerator reference to false.

  4. Activate the toolbox and then select the Components tab. Right-click an empty area on the tab and select Customize Toolbox from the shortcut menu. Click the .NET Framework Components tab and select RandomNumberGenerator.dll.

  5. Drag the RandomNumberGenerator component from the toolbox and drop it on the form. Change its MinValue property to 500 and change MaxValue to 1000.

  6. Add a Label control (lblResult) and a Button control (btnGenerate) to the form. Empty the Label control's Text property. Set the Button control's Text property to Generate a Random Number!. Double-click the Button control to add an event handler for its Click event. Add the following code to the event handler:

    
    private void btnGenerate_Click(object sender,
    
        System.EventArgs e)
    
    {
    
        lblResult.Text = String.Format(
    
            "The next random number is: {0}",
    
             randomNumberGenerator1.GetRandomNumber());
    
    }
    
    
  7. Set the project RandomNumberApp as the startup project.

  8. Run the solution. Click the button. A random number between 500 and 1,000 appears every time you press the button, as shown in Figure.

    Figure. A form that generates a random number by using the RandomNumberGenerator component installed in the GAC.

    graphics/10fig11.jpg

  9. Copy the RandomNumberApp.exe from the bin\Release folder of the project to the C:\MyPrograms\RandomNumberApplication folder. Run the file from the new location and verify that the application works as expected.

In Step-by-Step 10.11 you add a reference to the assembly stored in the bin\Release folder of the RandomNumberGenerator project. However, the application loads the assembly from the GAC, rather than its copy in the bin\Release folder. To understand this, you need to understand how the CLR locates shared assemblies; this is discussed in the following section.

Binding Policy for Shared Assemblies

For shared assemblies, the binding policy identifies a set of rules that specify:

  • The directories in which the CLR should search for an assembly.

  • The version of an assembly for which the CLR should search.

When the CLR searches a shared assembly, it goes through three stages of binding policy resolution, as shown in Figure.

12. The CLR resolves the binding policy in three stages.

graphics/10fig12.gif

At each of these stages, you can specify binding rules in XML-based configuration files. The three stages, in order, are as follows:

  1. Application Policy Resolution— At this stage, the CLR looks for an application configuration file for the binding rules. This configuration file can be used to specify additional search paths for an assembly. In addition, you can also use this file to redirect the CLR to a specific version of an assembly. The application-specific binding rules are usually set either by the application developer or by an administrator.

  2. Publisher Policy Resolution— The next stage after the application policy resolution is the publisher policy resolution. The publisher of a shared assembly sets publisher policy to distribute service pack–like updates to customers. For example, take a scenario where a customer installs version 1.0. of an assembly from a vendor. Later, the vendor realizes that there are certain errors with this version and releases a service pack to fix those errors. In this case, the publisher distributes a new version 1.1 of the assembly, along with a publisher policy that redirects all previously installed applications to use the new version 1.1 of the assembly rather than the old version 1.0.

    The publisher policy is specified in an XML file just as the application configuration file is. But unlike the application configuration file, the publisher policy is complied into an assembly. In addition, the publisher policy only contains information about redirecting the CLR to a different version of the assembly.

    By default, the binding rules specified in the publisher policy override the application policy. If, however, you want an application to override the updates and continue using the existing versions, you can bypass the publisher policy file by specifying the <publisherPolicy apply="no"/> XML element in the application configuration file.

  3. Administrator Policy Resolution— This stage is the final stage for applying the binding rules. The binding rules at this stage are specified by the administrator in the machinewide configuration file named machine.config. This file is stored in the config subdirectory under the .NET Framework installation directory on a machine.

    The settings specified in the machine configuration file override any settings specified in the application policy and the publisher policy.

How the CLR Binds to a Shared Assembly

The CLR uses the following steps to locate a shared assembly:

  1. Determine the correct version of the assembly by examining the application configuration file, publisher policy file, and machine configuration file in the order mentioned in the preceding section.

  2. Check whether the assembly is already loaded. If the requested assembly has been loaded in one of the previous calls, then bind to the already loaded assembly and stop searching any further.

  3. Check the GAC. If the assembly is in the GAC, then load the assembly from there, bind to it, and stop searching any further.

  4. If a <codebase> element is specified in the configuration files, the CLR locates the assembly by using the paths specified in the <codebase> element. If the CLR finds the assembly, then the binding is successful; otherwise the binding request fails and the CLR stops searching any further.

  5. The CLR reads the application configuration file to check whether any private path hints are available in the <probing> element. If there are hints, the CLR will use these directory locations to search the assembly.

  6. If the referenced assembly has no culture information, the CLR uses the following locations in the given order to find the assembly:

    • ApplicationBase\AssemblyName.dll

    • ApplicationBase\AssemblyName\AssemblyName.dll

    • ApplicationBase\PrivatePath1\AssemblyName.dll

    • ApplicationBase\PrivatePath1\AssemblyName\AssemblyName.dll

    • ApplicationBase\PrivatePath2\AssemblyName.dll

    • ApplicationBase\PrivatePath2\AssemblyName\AssemblyName.dll

      .

      .

      .

    • ApplicationBase\AssemblyName.exe

    • ApplicationBase\AssemblyName\AssemblyName.exe

    • ApplicationBase\PrivatePath1\AssemblyName.exe

    • ApplicationBase\PrivatePath1\AssemblyName\AssemblyName.exe

    • ApplicationBase\PrivatePath2\AssemblyName.exe

    • ApplicationBase\PrivatePath2\AssemblyName\AssemblyName.exe

      .

      .

      .

    Here ApplicationBase is the directory where the requesting application is installed; AssemblyName is the name of the assembly to search; and PrivatePath1 and PrivatePath2 are the hints provided in the <probing> element of the application configuration file. Note that the assembly name does not contain the extension; therefore the CLR searches for both DLL as well as EXE files. If the assembly is found in any of these locations, the CLR binds to the assembly and does not search any further.

  7. If the referenced assembly has culture information, the following directories are searched:

    • ApplicationBase\Culture\AssemblyName.dll

    • ApplicationBase\Culture\AssemblyName\AssemblyName.dll

    • ApplicationBase\PrivatePath1\Culture\AssemblyName.dll

    • ApplicationBase\PrivatePath1\Culture\AssemblyName\AssemblyName.dll

    • ApplicationBase\PrivatePath2\Culture\AssemblyName.dll

    • ApplicationBase\PrivatePath2\Culture\AssemblyName\AssemblyName.dll

      .

      .

      .

    • ApplicationBase\Culture\AssemblyName.exe

    • ApplicationBase\Culture\AssemblyName\AssemblyName.exe

    • ApplicationBase\PrivatePath1\Culture\AssemblyName.exe

    • ApplicationBase\PrivatePath1\Culture\AssemblyName\AssemblyName.exe

    • ApplicationBase\PrivatePath2\Culture\AssemblyName.exe

    • ApplicationBase\PrivatePath2\Culture\AssemblyName\AssemblyName.exe

      .

      .

      .

    Here Culture is a culture code corresponding to the assembly. If the assembly is found in any of these locations, the CLR binds to the assembly and does not search any further.

  8. If the CLR cannot locate the assembly after following the preceding steps, assembly binding fails.

Side-by-Side Execution of Shared Assemblies

From what you have learned, multiple versions of an assembly can exist in the GAC. The following are two common scenarios in which you might end up having multiple versions of an assembly:

  • A feature upgrade of an assembly is released.

  • A bug-fix service pack update of an assembly is released.

An application binds only to an assembly with the same identity with which the application was originally compiled. So if you release a new version of the assembly and remove the old one, existing applications will break because they still will be looking for an older version of the assembly.

Fortunately, the application, the machine, and the publisher configuration files give you a mechanism to redirect the binding requests to a different version of the assembly without any need to recompile already deployed applications.

Side-by-Side Execution in a Feature Upgrade Scenario

Consider a scenario where you have an application named RandomNumberApp that uses version 1.0 of a shared component named RandomNumberGenerator. When a new version 1.1 of the RandomNumberGenerator component is released, you install it in the GAC. Some of the applications on your server still use version 1.0 of the component. You want the already deployed RandomNumberApp to start using version 1.1 of the component. You do not want to recompile the RandomNumberApp and also do not want to affect the installation of other applications on the server.

The best policy in this scenario is to modify the application configuration file of RandomNumberApp to redirect any request for the version 1.0 of RandomNumberGenerator to the version 1.1 of that assembly. Modifying configuration files does not require the application to be recompiled, and if you modify the application configuration file, it affects only the application to which it belongs.

To demonstrate the scenario, Step-by-Step 10.12 installs version 1.1 of the RandomNumberGenerator assembly in the GAC.

STEP BY STEP

10.12 Installing Multiple Versions of an Assembly in the GAC

  1. Open the RandomNumberGenerator.cs file in the RandomNumberGenerator project.

  2. Modify the GetRandomNumber() method as follows:

    
    public int GetRandomNumber()
    
    {
    
        Random r = new Random();
    
        int rand = r.Next(minValue, maxValue);
    
        EventLog eventLog = new EventLog();
    
        eventLog.Source = "RandomNumberGenerator 1.1";
    
        eventLog.WriteEntry(
    
            "Random Number Generated:" + rand);
    
        return rand;
    
    }
    
    
  3. Open the AssemblyInfo.cs file. Scroll down in the file and change the AssemblyVersion as shown here:

    
    [assembly: AssemblyVersion("1.1")]
    
    
  4. Build the RandomNumberGenerator project. You should see the RandomNumberGenerator.dll file generated with version 1.1.

  5. Copy the strongly-named assembly RandomNumberGenerator.dll to the GAC. You should be able to see that the GAC now has two versions of the RandomNumberGenerator assembly, as shown in Figure.

    13. You can perform side-by-side versioning of shared assemblies in the GAC.

    graphics/10fig13.jpg

  6. Run the RandomNumberApp.exe file from the C:\MyPrograms folder. You should notice that the file still uses the 1.0 version of the RandomNumberGenerator component (with which it was compiled) and does not create an entry in the event log.

From Step-by-Step 10.12, note that the RandomNumberApp is still using the older version of the component because that's what is stored in its assembly manifest.

Step-by-Step 10.13 shows how to configure the application configuration file for RandomNumberApp.exe to use version 1.1 of the RandomNumberGenerator component.

STEP BY STEP

10.13 Using the .NET Framework Configuration Tool to Configure Application-Level Binding Policy for an Assembly

  1. Open the Microsoft .NET Framework Configuration tool from the Administrative Tools section of the Windows Control Panel.

  2. Click the Applications node in the tree view.

  3. Click the Add an Application to Configure link.

  4. In the Configure an Application dialog box, click the Other button and browse to the C:\MyPrograms\RandomNumberApplication folder to select RandomNumberApp.exe. Select this application and click OK.

  5. Expand the new node in the tree view and click the Configured Assemblies child node.

  6. Click the Configure an Assembly link.

  7. In the Configure an Assembly dialog box, select the option button to choose an assembly from the list of assemblies that this application uses. Click the Choose Assembly button. Select the RandomNumberGenerator assembly, click Select, and then click Finish.

  8. In the RandomNumberGenerator Properties dialog box, select the Binding Policy tab. Enter 1.0.0.0 as the requested version and 1.1.0.0 as the new version, as shown in Figure.

    14. You can create a configured assembly and set its binding policy and other settings with the help of the Microsoft .NET Framework Configuration tool.

    graphics/10fig14.jpg

  9. Click OK to save the configured assembly information.

    WARNING

    No Validation The .NET Framework Configuration tool performs no validation to determine whether the specified new version of the assembly even exists.

  10. View the C:\MyPrograms\RandomNumberApplication folder in Windows Explorer. You should find a RandomNumberApp.exe.config application configuration file added to the folder with the following contents:

    
    <?xml version="1.0"?>
    
    <configuration>
    
      <runtime>
    
        <assemblyBinding
    
            xmlns="urn:schemas-microsoft-com:asm.v1">
    
          <dependentAssembly>
    
            <assemblyIdentity
    
                name="RandomNumberGenerator"
    
                publicKeyToken="4c9fe2db3ce3261d" />
    
            <bindingRedirect oldVersion="1.0.0.0"
    
                newVersion="1.1.0.0" />
    
          </dependentAssembly>
    
        </assemblyBinding>
    
      </runtime>
    
    </configuration>
    
    
  11. Double-click the RandomNumberApp.exe file from the C:\MyPrograms\RandomNumberApplication folder in Windows Explorer. Click the button and check the entries in the Application event log to see that version 1.1.0.0 of the RandomNumberGenerator component is executed.

In Step-by-Step 10.13, you used the .NET Framework configuration tool to create an application configuration file. You can also create the application configuration file manually, although in that case you need to know that you have to use the <assemblyBinding> element and its sub-elements to redirect the version numbers.

Note that in the <bindingRedirect> element the values for the attribute oldVersion and newVersion need not be an old version and a new version respectively. You can, in fact, redirect an application using a newer version of an assembly to an older version also (provided, of course, that the changes are non-breaking).

When you use the application configuration file, you can configure only a single application, but what if you need to configure all the applications on a machine that uses the old version of RandomNumberGenerator to use its new version? In this case, it will be tedious to configure an assembly for each application. Further, you will also need to take care that the new applications installed also bind to the new version of the component. To ease this, you can add the <assemblyBinding> element in the machine.config, machine configuration file. However, you need Administrator privileges to edit the machine.config file. Step-by-Step 10.14 shows you how to configure an assembly for a machine using the .NET Framework Configuration tool.

STEP BY STEP

10.14 Using the .NET Framework Configuration Tool to Configure Machine-Level Binding Policy for an Assembly

  1. Open the Microsoft .NET Framework Configuration tool from the Administrative Tools section of the Windows Control Panel.

  2. Right-click the Configured Assemblies node in the tree view and select Add from its context menu.

  3. In the Configure an Assembly dialog box, select the option button to choose an assembly from the assembly cache. Click the Choose Assembly button. Select the RandomNumberGenerator assembly version 1.0.0.0, click Select, and then click Finish.

  4. In the RandomNumberGenerator Properties dialog box, select the Binding Policy tab. Enter 1.0.0.0 as the requested version and 1.1.0.0 as the new version.

  5. Click OK to save the configured assembly information.

  6. Open the machine.config file from the config folder of the Microsoft .NET Framework installation folder. Navigate to the <assemblyBinding> element and verify that binding policy for the RandomNumberGenerator component is added as shown here:

    
    <configuration>
    
    ...
    
      <runtime>
    
        <assemblyBinding
    
            xmlns="urn:schemas-microsoft-com:asm.v1">
    
          ...
    
          <dependentAssembly>
    
            <assemblyIdentity name="RandomNumberGenerator"
    
              publicKeyToken="4c9fe2db3ce3261d" />
    
            <bindingRedirect oldVersion="1.0.0.0"
    
                newVersion="1.1.0.0" />
    
          </dependentAssembly>
    
        </assemblyBinding>
    
      </runtime>
    
    </configuration>
    
    
  7. Delete the RandomNumberApp.exe.config application configuration file from the C:\MyPrograms\RandomNumberApplication folder in Windows Explorer.

  8. Double-click the RandomNumberApp.exe file from the C:\MyPrograms\RandomNumberApplication folder in Windows Explorer. Click the button and check the entries in the Application event log to see that version 1.1.0.0 of the RandomNumberGenerator component is executed.

Configuration performed in Step-by-Step 10.14 affects not only the RandomNumberApp, but in fact all applications that use the RandomNumberGenerator components on the given machine.

Side-by-Side Execution in a Service Pack Update Scenario

Consider another scenario in which you may end up having multiple versions of an assembly in the GAC. In this scenario, you are the company that publishes the RandomNumberGenerator component. You release version 1.0 of the component. Your customers develop applications, such as RandomNumberApp, that use the component. The customers report that there is a bug in the component that needs to be fixed at high priority. You plan to release a service pack of the component. The new version of the component is version 1.1. You want to deploy the component on the customers' computers in such a way that all existing applications that refer to version 1.0 of the RandomNumberGenerator component should now be redirected to version 1.1. You do not want extra configuration steps to have to be performed by customers' administrators.

The .NET Framework provides publisher policy for these very scenarios. To configure a publisher policy, use the publisher policy configuration file, which uses a format similar to that of the application configuration files. But unlike the application configuration file, a publisher policy file needs to be compiled into an assembly and placed in the GAC.

The steps involved in creating a publisher policy are as follows:

  1. Create a publisher policy file.

  2. Use the Assembly Generation tool (al.exe) to convert the publisher policy file into a strongly-named assembly. The name of the assembly should be of the format policy.majorNumber.minorNumber.AssemblyName.dll. Here, majorNumber and minorNumber are the major and minor version numbers of the existing assembly for which you want to redirect the binding requests. AssemblyName is the name of the assembly.

  3. Install the publisher policy assembly to the GAC.

Step-by-Step 10.15 shows you how to create a publisher policy by using the .NET Framework Configuration tool.

STEP BY STEP

10.15 Configuring Publisher Policy for an Assembly

  1. Create a new publisher configuration file named policy.1.0.RandomNumberGenerator.config in the folder where the original RandomNumberGenerator.dll assembly resides with the following code:

    
    <configuration>
    
       <runtime>
    
          <assemblyBinding
    
            xmlns="urn:schemas-microsoft-com:asm.v1">
    
           <dependentAssembly>
    
             <assemblyIdentity
    
                name="RandomNumberGenerator"
    
                publicKeyToken="4c9fe2db3ce3261d"
    
                                />
    
             <bindingRedirect oldVersion="1.0.0.0"
    
                newVersion="1.1.0.0"/>
    
           </dependentAssembly>
    
          </assemblyBinding>
    
       </runtime>
    
    </configuration>
    
    

    You need to change the publicKeyToken attribute to the appropriate public key for your assembly (which you can locate in the configuration file that you built in Step-by-Step 10.14).

  2. Launch the Visual Studio .NET prompt. Navigate to the folder where the publisher configuration file resides and run the following command:

    al /link:policy.1.0.RandomNumberGenerator.config/out:policy.1.0. graphics/ccc.gifRandomNumberGenerator.dll/keyfile:..\..\..\70320.snk

    You should see a publisher policy assembly named policy.1.0.RandomNumberGenerator.dll being generated.

  3. Add the publisher policy assembly to the GAC.

  4. Open the Microsoft .NET Framework Configuration tool from the Administrative Tools section of the Windows Control Panel.

  5. Select the Configured Assemblies. In the pane on the right side, delete the RandomNumberGenerator assembly added in Step-by-Step 10.14. Now the assembly binding entries from the machine.config file are removed.

  6. Double-click the RandomNumberApp.exe file from the C:\MyPrograms\RandomNumberApplication folder in Windows Explorer. Click the button and check the entries in the Application event log to see that version 1.1.0.0 of the RandomNumberGenerator component is executed. The version 1.1.0.0 assembly is selected due to the assembly bindings in the publisher policy assembly.

Binding redirections specified in the machine.config override those specified in the publisher policy. The publisher policy overrides the setting specified in the application configuration file. But an application can ignore the publisher policy altogether if you set the apply attribute of the <publisherPolicy> element to "no" in the <assemblyBinding> element of the application configuration file:


<publisherPolicy apply="no"/>

Delay Signing an Assembly

In Step-by-Step 10.6, when you signed an assembly, you used a key file that contained both the public key and a private key for a company. As discussed earlier in the chapter, the private key ensures that the assembly is signed only by its advertised publisher. Thus, in most companies, the private key is stored securely, and only a few people have access to it.

If the key is highly protected, it might be difficult to frequently access the key when multiple developers of a company are building assemblies several times a day. To solve this problem, the .NET Framework uses the delay signing technique for assemblies.

When you use delay signing, you use only the public key to build an assembly. Associating public keys with an assembly allows you to place the assembly in the GAC and complete most of the development and testing tasks with the assembly. Later, when you are ready to package the assembly, someone who is authorized signs the assembly with the private key. Signing with the private key ensures that the CLR will provide tamper protection for the assembly. The following list summarizes the steps involved with delay signing:

  1. Extract a public key from the public/private key pair— To extract the public key from a file that is storing the public/private key pair, you use the Strong Name tool as follows:

    
    sn.exe –p 70320.snk 70320PublicKey.snk
    
    

    At this stage, the 70320PublicKey.snk file can be freely distributed to the development team, and the 70320.snk file that contains both the private and public keys can be stored securely, possibly on a hardware device such as a smart card.

  2. Use Visual Studio .NET to delay sign an assembly— To use delay signing in a Visual Studio .NET project, you need to modify the following two attributes of the project's AssemblyInfo.cs file and build the assembly:

    
    [assembly: AssemblyDelaySign(true)]
    
    [assembly: AssemblyKeyFile("70320PublicKey.snk")]
    
    
  3. Turn off verification for an assembly in the GAC— By default, the GAC verifies the strong name of each assembly. If the private key is not used to sign the assembly, this verification fails. So for development and testing purposes, you can relax this verification for an assembly by issuing the following command:

    
    sn.exe –Vr RandomNumberGenerator.dll
    
    

    If you execute this command, the GAC always skips the verification for this assembly in the future.

  4. Sign a delay-signed assembly with the private key— When you are ready to deploy a delay-signed assembly, you need to sign it with the company's private key:

    
    sn.exe –R RandomNumberGenerator.dll 70320.snk
    
    
  5. Turn on verification for an assembly in the GAC— Finally, you can instruct the GAC to turn on verification for an assembly by issuing the following command:

    
    sn.exe –Vu RandomNumberGenerator.dll
    
    
Using the Assembly Generation Tool for Delay Signing

The Assembly Generation tool (al.exe) generates an assembly with an assembly manifest from the given modules or resource files. A module is a Microsoft Intermediate Language (MSIL) file without an assembly manifest.

While generating an assembly, you can also instruct the Assembly Generation tool to sign or delay sign an assembly with the given public/private key file. When you use al.exe for delay signing, you also use the arguments listed in Figure.

Figure Arguments Passed to al.exe for Delay Signing

Argument

Description

<sourcefiles>

You replace <sourcefiles> with the names of one or more complied modules that will be the parts of the resulting assembly.

/delay[sign][+|-]

You can use either the delay argument or the delay[sign] argument for delay signing. The option + is used to delay sign the assembly by storing just the public key manifest in the assembly manifest.

The option is used to fully sign an assembly with both public and private keys.

If you do not use either + or , the default value of is assumed.

/keyf[ile]:<filename>

You can use either keyf or keyfile to specify the key file. You replace <filename> with the name of the file that stores the key(s).

/out:<filename>

You replace <filename> with the desired name of the output assembly file.

Assume that you want to create an assembly by linking two modules, Sample1.netmodule and Sample2.netmodule. The public key file is SamplePublicKey.snk, and the desired output assembly is SignedSample.exe. You would use the al.exe command as follows:

al.exe Sample1.netmodule,Sample2.netmodule/delaysign+/keyfile:SamplePublicKey.snk/out: graphics/ccc.gifSignedSample.exe

REVIEW BREAK

  • Shared assemblies are used by multiple applications on a machine. Shared assemblies are placed in the GAC and they enjoy special privileges such as file security, shared location, and side-by-side versioning.

  • You generate a public/private key pair by using the Strong Name tool (sn.exe). This pair can be used to sign assemblies with a strong name.

  • You can add a shared assembly to the GAC by using Windows Explorer, the .NET Framework Configuration tool, the Global Assembly Cache tool, and the Windows Installer.

  • The best way to add an assembly in the GAC during deployment is to use Microsoft Windows Installer. The Windows Installer provides assembly reference-counting features and manages removal of assemblies at the time of uninstallation.

  • When viewed in Windows Explorer, the assembly cache folder in the System folder displays assemblies from the GAC and native image cache.

  • The CLR first searches the GAC to locate assemblies, and then it looks into the files and folders where the assembly is installed. Thus, loading shared assemblies from the GAC is efficient because the CLR does not engage itself in looking into the <codebase> and <probing> elements of the applicable configuration files.

  • Different versions of an assembly can be deployed in the GAC for side-by-side execution. You can implement side-by-side execution of an assembly by configuring the binding policy in the application configuration file, the publisher policy file, or the machine configuration file.

  • The publisher policy file can be used to deploy service packs, such as bug-fix updates, for the assemblies. The publisher policy file is an XML file that is converted into a strongly-named assembly and installed in the GAC.

  • Delay signing allows you to place a shared assembly in the GAC by signing the assembly with just the public key. This allows the assembly to be signed with the private key at a later stage, when the development process is complete and the component or assembly is ready to be deployed. This process enables developers to work with shared assemblies as if they were strongly named, and it secures the private key of the signature from being accessed at different stages of development.