.NET Assemblies Ins and Out : Part II

This article is the second part of a three-part series of articles covering the .Net assemblies. In Part 1, I covered what exactly an assembly is, and what an assembly contains. In this article, Part 2 of the series I will discuss both Private and Shared assemblies and how to create a "Shared Assembly". It is assumed that you are already familiar with creating apps or dll files. I will briefly mention some of the utilities available for working with assemblies. Part 3 will discuss in more detail than Part 2, the details of the available utilities for manipulating assemblies.

Assembly Accessibility

There are two types of assemblies when referring to accessibility: Private and Shared.

  • Private: Assemblies are private by default. Private assemblies must accompany all other files in the assembly. This keeps the assembly coherent as one assembled unit. This makes sense because its current compiled application domain can only use a private assembly. Private assemblies do not need to worry about namespace clashes since each compilation and deployment of a private assembly runs within its own application domain.
  • Shared: To share an assembly such as the DLL you've created, you must give the assembly a shared name. You may often see the shared assembly name referred to as a "strong name". Shared assemblies must reside in the Global Assembly Cache discussed in Part 1. A shared assembly must also have a unique namespace. The unique identity is derived through the use of public-key cryptography.

Why create a shared assembly?

The following are some reasons why you may want to create a shared assembly:

  • Code sharing or code reuse and version control are the more probable reasons. Grouping code according to security permissions is also another reason.
  • The benefits of creating a shared assembly are that through the use of the cryptography keys, you are guaranteed not to experience a namespace clash. Version control prevents someone from trying to add his or her own version of your code. (The smallest unit that can be controlled by versioning is the assembly, and an assembly must be shared in order to use versioning).
  • There are several reasons why you may want to create a shared assembly: Code sharing or code reuse and version control are the more probable reasons. Grouping code according to security permissions is also another reason.
  • The benefits of creating a shared assembly are that through the use of the cryptography keys, you are guaranteed not to experience a namespace clash. Version control prevents someone from trying to add his or her own version of your code. (The smallest unit that can be controlled by versioning is the assembly, and an assembly must be shared in order to use versioning). A shared assembly can consume fewer resources when loading a type that is referenced in multiple application domains. Resources are reduced because instead of loading the type in each application domain, it is loaded only once and then mapped to all other references.

Creating a Private Assembly

As mentioned earlier, the default access for an assembly is private. Unless you take measures to create a shared assembly, when you compile your application using the SDK-supplied compiler vbc.exe, a private assembly is created with the proper manifest information. This happens via the command line or by building from within Visual Studio. NET. Compiling some code as follows:

Bbc HelloWorld.vb

produces the application/private assembly

HelloWorld.exe

Creating a Shared Assembly

Creating a shared assembly is a little more involved. Microsoft has supplied all the tools needed to perform this task. The first item we will need is an assembly compiled in the normal way. We will create a module for this called Hello.dll that contains the following code:

namespace hello {
    using System;

    public class Class1 {
        public Class1() { }

        public void SayHello(string name) {
            Console.WriteLine("Hello " + name);
        }
    }
}

The first tool required is the "Strong Name" utility. Your SDK should contain a file named: sn.exe. Running the Strong Name utility will provide you with a file containing the public and private keys to use. These keys will later be used to give our assembly the guaranteed unique name. From the command line run the following command:

Sn -k myKeys.sink

The Strong Name utility will generate a file called myKeys. sink, where the sink extension stands for "strong name key". The -k designates the output file, that we have named myKeys.snk in this example. There are other switches that we will discuss in Part 3.

If you did not run the command from your project folder, then copy the file that was just created to your project folder.

Signing the Assembly

Now we have the keys but we still need to tell our program to use a key. Adding the key to the assembly is referred to as "signing" the assembly. To do this we need to open a file that is located in your project folder called AssemblyInfo.vb In Visual Studio .NET you can look in the Solution Explorer under the project node. Right-click on the AssemblyInfo file and select Open. If you are not using Visual Studio then any text editor should work. Scroll down toward the end of the file and look for the following line:

[assembly: AssemblyKeyFile("")]

Edit the file and place the name of your key file between the quotes in the parentheses. The line in your AssemblyInfo.vb file should now look like this:

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

We have now told the compiler to use our keys to digitally sign the assembly, thus creating a guaranteed unique namespace. However, you must re-compile the code in order to complete the process. Compile the program with the edited version of the AssemblyInfo.vb file.

Important: In order for the assembly to use your private signature (key file), you must recompile your program.

Sharing Your Code

In order for your code to be shared with other assemblies, you need to place your newly signed, shared assembly in the global section of the Assembly Cache. Unfortunately, you cannot just copy your code over to the global cache folder. You need to run another command-line utility that Microsoft has supplied called Assembly Linker. In your SDK should be a file named AL.exe.

The Assembly Linker will create the proper folder in the Global Assembly Cache. This folder's name will contain part of the assembly's unique identity. To run the AL program, go to a command line (make sure the current folder is the same folder your hello.dll file is in) and type in the following command:

AL /install:Hello.dll

The preceding command can also be shortened to AL /i:Hello.dll.

The Assembly Linker program has other uses that I will mention in Part 3 of this article series.

Now if you look in your Global Assembly Cache you should see a new folder where your assembly was placed for sharing. My system placed the assembly at:

C:\Windows\assembly\global\T2\LGRE\hello.dll

Your system will use something slightly different. Now we're ready to test the assembly to see if we can access the SayHello() method.

Using the Shared Assembly

Now let's create a small program that will call the SayHello() method that resides in our shared assembly. When you compile this program you will need to set a reference to the originally shared assembly. Make sure that the reference path points to where the hello.dll file resides and not the current project path.

namespace myHello {
    using System;
    using hello;

    public class HelloClass {
        public HelloClass() { }

        public static int Main(string[] args) {
            Class1 speak = new Class1();
            speak.SayHello("John");
            return 0;
        }
    }
}

After you compile the program you can place the myHello.exe file almost anywhere in your system's file path and it will locate the shared assembly hello.dll when you run the application. When you run the myHello.exe application the result will be:

Hello John

This works because the runtime will check specific search paths starting with the application's base path, and eventually the Assembly Cache. If we had not created the hello.dll as a shared assembly and then tried to run the application without the hello.dll in the applications base folder, then we would have received an error such as:

Exception occurred: System.TypeLoadException: Could not load class "Class1"...

Using shared assemblies can allow you to reuse code, maintain a unique

namespace, and maintain versioning.

Continue Reading