The Global Assembly Cache

The GAC is a storage area where the CLR can locate shared assemblies; shared assemblies don’t have to be installed in the GAC to be used by applications, but doing so can ease the burden of system administration; see the Assembly Versions section later in this appendix for more details of how the GAC can be used.

The Global Assembly Cache Tool (gacutil.exe) is used to install shared assemblies in the GAC; the following command installs the StringPrinter assembly:

gacutil -i StringPrinter.dll

The .NET Framework includes a Windows Explorer extension that is used to view the GAC. Navigate to the assembly subdirectory of the Windows directory (usually either C:WINNTassembly or C:Windowsassembly). The list of shared assemblies should include StringPrinter.

To remove the shared assembly, use the following command:

gacutil -u StringPrinter

Unfortunately, shared assemblies installed in the GAC can’t be referenced when source files are compiled. The GAC is intended as a deployment tool and not an aid to development; a local copy of a shared assembly must be available during development.

The following example demonstrates how to make use of a GAC shared assembly; the file HelloWorld.cs contains the following statements:

class HelloWorld {
    public static void Main(string[] args) {
        StringPrinter myPrinter = new StringPrinter();
        myPrinter.printString("Hello World!");
    }
}

This class depends on the StringPrinter type that we have installed in the GAC. To compile this class, a copy of the StringPrinter.dll file needs to be in the same directory as the HelloWorld.cs file. We compile the application with the following command:

csc /target:exe HelloWorld.cs /reference:StringPrinter.dll

Once the application has been compiled, deleting the StringPrinter.dll file won’t affect the execution of the program because a copy of the assembly has been installed in the GAC. (Before you try this, you’ll need to reinstall StringPrinter.dll in the GAC if you’ve already uninstalled it.) Executing the program produces the following output:

Message: Hello World!

Assembly Versions

Assembly versioning is a solution to the DLL Hell problem, wherein applications relied on a shared copy of a DLL; installing a new version of a DLL that wasn’t backward-compatible often caused problems with older applications.

When a .NET application uses a shared assembly, part of the reference stored in the metadata specifies the version required; by default, the .NET Framework won’t execute an application if the correct versions of shared assemblies can’t be located.

Side-by-Side Execution

One of the most interesting assembly features is the support for different versions of an assembly being used by different applications. Part of the identity for an assembly with a strong name is the version number, and the GAC can store multiple versions of an assembly without conflict. When an application depends on a strong-named assembly, the reference in the metadata specifies which version of the assembly is required. To demonstrate this, we’ll create a new version of the StringPrinter assembly and a new application that depends on it.

Assuming that the first version of StringPrinter.dll is installed in the GAC, we’ll update the source files to include slightly different functionality and a new version number. The contents of the AssemblyInfo.cs file are as follows:

using System.Reflection;
using System.Runtime.CompilerServices;

[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyKeyFile("myKey.snk")]

The changed statement, shown in boldface, indicates that the new assembly will be version 2.0.0.0. The StringPrinter.cs file is now as follows:

public class StringPrinter {
    public void printString(string messageString) {
        System.Console.WriteLine("Message version 2.0: "
            + messageString);
    }
}

We’ll define a new client application in a file named NewClient.cs with the following statements:

class NewClient {
    public static void Main(string[] args) {
        StringPrinter myPrinter = new StringPrinter();
        myPrinter.printString("Hello World!");
    }
}

The following statements build the new version of the shared assembly, install it in the GAC, build the new client, and delete the local copy of StringPrinter.dll:

csc /target:library StringPrinter.cs AssemblyInfo.cs
gacutil -i StringPrinter.dll
csc /target:exe NewClient.cs /reference:StringPrinter.dll
del StringPrinter.dll

The build directory should now contain the application from the previous section (HelloWorld.exe) and the new application (NewClient.exe). Executing HelloWorld.exe produces the following output:

Message: Hello World!

Executing NewClient.exe results in the following:

Message version 2.0: Hello World!

Assembly Version Policies

Although multiple versions of an assembly can coexist, the .NET Framework provides a mechanism for forcing applications built using one version of an assembly to use another version, in effect forcing an upgrade.

The publisher of an assembly can issue a policy file that, when installed in the GAC, causes requests for one version of an assembly to be satisfied with another version. It’s important that the newer version of the assembly be compatible with the older version; if it isn’t, applications might not function as expected (reintroducing the DLL Hell problem).

Creating a publisher policy file

A policy file contains the strong name details of the original assembly version and the version of the new assembly to use. The following file shows a policy file that states that version 1.0.0.0 of the StringPrinter assembly, shown earlier, should be satisfied by version 2.0.0.0:

<configuration>
    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <dependentAssembly>
                <assemblyIdentity name="StringPrinter"
                    publicKeyToken="539a6301c167d487"
                    culture="" />
                <!-- Redirect to version 2.0.0.0 of the assembly -->
                <bindingRedirect oldVersion="1.0.0.0"
                    newVersion="2.0.0.0"/>
            </dependentAssembly>
        </assemblyBinding>
    </runtime>
</configuration>

The assemblyIdentity element specifies the name, key signature, and culture of the assembly that the policy relates to. The bindingRedirect element specifies the old and new versions of the assembly that will be used to redirect requests. The details of the strong name can be obtained either from the GAC or by using the Strong Name Tool (sn.exe).

Creating and installing a publisher policy assembly

The policy file must be linked into an assembly before it can be used. The following statement demonstrates the usage form of the Assembly Linker Tool (al.exe) to achieve this; Table B-1 provides details of the command options.

al /link:publisherPolicyFile /out:publisherPolicyAssemblyFile
    /keyfile:keyPairFile

Table B-1. Assembly Linker Options for Creating Policy Assemblies

Assembly Linker Switch

Description

/link:publisherPolicyFile

The policy file containing the assembly version information.

/out:publisherPolicyAssemblyFile

The name of the output file.

/keyfile:keyPairFile

The key file, created using the sn.exe tool. This must be the same key that was used to sign the assemblies.

The name of the assembly is based on the assembly version being redirected and must adhere to the following form:

policy.majorNumber.minorNumber.mainAssemblyName.dll

For the example just shown, the name would be

policy.1.0.StringPrinter.dll

Assuming that the policy file was named policyfile.xml, the command to assemble the example is

al /link:policyfile.xml /keyfile:myKey.snk
    /out:policy.1.0.StringPrinter.dll

The policy assembly must be installed in the GAC before the policy will take effect. Policy assemblies are installed the same way as code assemblies, using the gacutil.exe tool. The following command demonstrates how to install the sample assembly:

gacutil /i policy.1.0.StringPrinter.dll
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset