You can create assemblies that can be shared by other applications. You might want to do this if you have written a generic control or a class that might be used by other developers. If you want to share your assembly, it must meet certain stringent requirements.
First, your assembly must have a
strong
name
. Strong names are globally unique.
No one else can generate the same strong name as you because an assembly generated with one private key is guaranteed to have a different name than any assembly generated with another private key.
Second, your shared assembly must be protected against newer versions trampling over it, and so it must have version control.
Finally, to share your assembly, you will place it in the
Global Assembly Cache (GAC)
(pronounced GACK).
This is an area of the file system set aside by the
Common Language Runtime (CLR) to hold
shared assemblies.
Assemblies mark the end of DLL Hell. You will remember this scenario: you install Application A on your machine, and it loads a number of DLLs into your Windows directory. It works great for months. You then install Application B on your machine, and suddenly, unexpectedly, Application A breaks. Application B is in no way related to Application A. So what happened? It turns out, you later learn, that Application B replaced a DLL that Application A needed, and suddenly Application A begins to stagger about, blind and senseless.
When DLLs were invented, disk space was at a premium and reusing DLLs seemed like a good idea. The theory was that DLLs would be backward-compatible, so automatically upgrading to the new DLL would be painless and safe. As my old boss Pat Johnson used to say, “In theory, theory and practice are the same. But in practice, they never are.”
When the new DLL was added to the computer, the old application, which was happily minding its own business in another corner of your machine, suddenly linked to a DLL that was incompatible with its expectations and hey! Presto! It went into the dance of death. This phenomenon led customers to be justifiably leery of installing new software, or even of upgrading existing programs, and it is one of the reasons Windows machines are perceived to be unstable. With assemblies, this entire nightmare goes away.
S hared assemblies in .NET are uniquely identified by their names and their versions. The GAC allows for “side-by-side” versions in which an older version of an assembly is available alongside a newer version. This allows particular applications to say “give me the newest” or “give me the latest build of Version 2,” or even “give me only the version I was built with.”
Side-by-side versioning applies only to items in the GAC. Private assemblies do not need this feature and do not have it.
A version number for an assembly might look like this:
1:0:2204:21
, four numbers, separated by colons.
The first two numbers (1:0
) are the major and
minor version. The third number (2204
) is the
build, and the fourth (21
) is the revision.
When two assemblies have different major or minor numbers, they are considered to be incompatible. When they have different build numbers, they might or might not be compatible, and when they have different revision numbers, they are considered definitely compatible with each other.
Revision numbers are intended for bug fixes. If you fix a bug and are
prepared to certify that your DLL is fully backward-compatible with
the existing version, you should increment the revision. When an
application loads an assembly, it specifies the major and minor
version that it wants, and the
AssemblyResolver
finds the
highest build and revision numbers.
In order to use a shared assembly, you need to meet three requirements:
You need to be able to specify the exact assembly you want to load. Therefore, you need a globally unique name for the shared assembly.
You need to ensure that the assembly has not been tampered with. That is, you need a digital signature for the assembly when it is built.
You need to ensure that the assembly you are loading is the one authored by the actual creator of the assembly. You therefore need to record the identity of the originator.
All these requirements are met by strong
names
. Strong names must be globally
unique and use public key encryption to ensure that the assembly
hasn’t been tampered with and was written by the creator. A
strong name is a string of hexadecimal digits and is not meant to be
human-readable.
To create a strong name, a public-private key pair is generated for
the assembly. A hash is taken of the names and contents of the files
in the assembly. The hash is then encrypted with the private key for
the assembly and placed in the manifest. This is known as
signing the assembly
. The public key is incorporated
into the strong name of the assembly.
When an application loads the assembly, the CLR uses the public key to decode the hash of the files in the assembly to ensure that they have not been tampered with. This also protects against name clashes.
You can create a strong name with the sn
utility:
sn -k c:myStrongName.snk
The -k
flag indicates that you want a new key pair
written to the specified file. You can call the file anything you
like. Remember, a strong name is a string of hexadecimal digits and
is not meant to be human-readable.
You associate this strong name with your assembly by using an attribute:
using System.Runtime.CompilerServices [assembly: AssemblyKeyFile("c:myStrongName.key")]
Attributes are covered in detail in Chapter 19. For now, you can just put this code at the top of your file to associate the strong name you generated with your assembly.
Once
you’ve created your strong
name and associated it with your assembly, all that remains is to
place the assembly in the GAC, a reserved system directory. You can
do that with the gacutil
utility:
gacutil /i:MySharedAssembly.dll
Or you can open your File Explorer and drag your assembly into the GAC. To see the GAC, open the File Explorer and navigate to winNTassembly; Explorer turns into a GAC utility.
The best
way to understand shared assemblies is to build one. Let’s
return to the earlier multi-module project (see Examples 17-1 through
17-4) and navigate to the directory that contains the files
calc.cs
and fraction.cs
.
Try this experiment. Locate the bin
directory for
the driver program and make sure that you do not have a local copy of
the MySharedAssembly
DLL files.
Run the program. It should fail with an exception saying it cannot load the assembly:
Exception occurred: System.TypeLoadException: Could not load class 'ProgCS.myCalc' because the module containing it failed to load.
Now copy the DLLs into the driver program’s directory tree, run it again, and this time you should find that it works fine.
Let’s make the MySharedAssembly
into a
shared assembly. You do so in two steps. First, you create a strong
name for the assembly, and then you put the assembly into the GAC.
Create a key pair by opening a command window and entering:
sn -k keyFile.snk
Now open the AssemblyInfo.cs
file in the project
for the MySharedAssembly.dll
and modify this
line:
[assembly: AssemblyKeyFile("")]
as follows:
[assembly: AssemblyKeyFile(".\keyFile.snk")]
This sets the key file for the assembly. Rebuild with the same
make
file as earlier, and then open the resulting
DLL in ILDasm
and open the manifest. You should
see a public key, as shown in Figure 17-8.
By adding the strong name, you have signed this assembly (your exact values will be different). You now need to get the strong name from the DLL. To do this, navigate to the directory with the DLL and enter the following at a command prompt:
sn -T MySharedAssembly.dll
The response should be something like this:
Public key token is 01fad8e0f0941a4d
This value is an abbreviated version of the assembly’s public key, called the public key token .
Remove the DLLs from the test program’s directory structure and run it again. It should fail again. Although you’ve given this assembly a strong name, you’ve not yet registered it in the GAC.
The next step is to drag the library into the GAC. To do so, open an Explorer window and navigate to the WinNT directory or its equivalent. When you double-click the Assembly subdirectory, Explorer will turn into a GAC viewer.
You can drag and drop into the GAC viewer, or you can invoke this command-line utility:
Gacutil -i mySharedAssembly
In either case, be sure to check that your assembly was loaded into
the GAC, and that the originator value shown in the GAC viewer
matches the value you got back from sn
:
Public key token is 01fad8e0f0941a4d
as illustrated in Figure 17-9.
Once this is done, you have a shared assembly that can be accessed by any client. Refresh the client by building it again and look at its manifest, as shown in Figure 17-10.
There’s MySharedAssembly
, listed as an
external assembly, and the public key now matches the value shown in
the GAC. Very nice, time to try it.
Close ILDasm
and compile and run your code. It
should work fine, even though there are no DLLs for this library in
its immediate path. You have just created
and used
a
shared assembly.
18.119.138.202