Part I
Overview: This article is the first part of a three part series of articles covering the .Net assemblies. In Part 1, I will cover what exactly an assembly is, and what an assembly contains. Part 2 of the series will discuss both Private and Shared assemblies and how to create a "Shared Assembly". 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.
What is a .NET Assembly?
An assembly is a core part of the runtime. An assembly is the collection of all information required by the runtime to execute your application. This information is referred to as the Metadata. An assembly can be a DLL file or a Portable Executable (PE) file. The Common Language Runtime (CLR) can only execute code in assemblies and the assembly must include a manifest or reference a manifest contained in another file.
An assembly that has been compiled into a portable executable (PE) file has an extension of AppName.exe. A common mistake made by new comers to the .NET Framework is they think this *.exe file is the same as a standalone executable created by many other languages such as a C++ compiler. There are very big differences between a C++ executable file and a .NET PE file. The PE assembly consists of code in the form of Intermediate Language (IL). This IL code requires the .NET platform be installed on the host in order to run.
What is the purpose of an Assembly?
An assembly controls many aspects of an application. The assembly handles versioning, type and class scope, security permissions, as well as other metadata including references to other assemblies and resources. The rules described in an assembly are enforced at runtime.
Contents of an Assembly
- Manifest - The metadata describing the information below.
- Assembly name - Aids in versioning and visibility scope.
- Version information - The version number is integrated into the assembly's identity.
- Types - Boundaries and scopes of methods, classes, properties, events, attributes.
- Locale - Information describing language/culture.
- Cryptographic Hash - Public key encoded hash acting as version/security check.
- Security Permissions - The permissions within the assembly determine the permissions that can be granted for all aspects of the assembly contents.
The Assembly Menifest
All assemblies have a manifest. This manifest may be immediately contained within the assembly as in a single file assembly, or may be referenced in a separate file as in multi file assemblies. The manifest contains information about all portions considered as part of the assembly itself. The information in the manifest is known as the Metadata.
The manifest contains information that determines which components are visible beyond the assembly's namespace and also which components are to remain private to the assembly (Scope).
Other optional information can be found in the manifest such as configuration information, information about your company or copyright.
The manifest contains so much information that usually an assembly can simply just be copied as a whole from one .NET framework to another without having to register with the destination operating system.
The Cryptographic Hash
This hash contains information such as references to all separate files that are referenced by the assembly, and references to other assemblies that are part of this assembly. The hash is encrypted using public key cryptography. This will be covered more in Part 2 of this article series. The hash is used at runtime to verify that all related files and assemblies have not been modified since the assembly was compiled. This helps prevent unwanted tampering.
What is the Assembly Cache?
When the .NET SDK is installed, the framework creates a folder on the system. This folder is known as the Assembly Cache. The cache contains a Private section as well as a Global section. The global cache is home to assemblies that are shared. I will cover "Shared Assemblies in Part 2. Specific restricted files for an application may reside in the private section. Files that your application downloads at runtime are also stored in the private section of the cache. There are several utilities for working with assemblies and the cache area. These utilities are covered in the next series of articles (Part 2 -- 3).
All assemblies in the global cache must be shared and must contain unique namespaces. The folder names themselves contain part of the assembly's unique identity. The Assembly Cache can be found in your Windows directory in a subfolder called assembly.
Check either C:\WINNT\Assembly or C:\Windows\assembly
Part II
This article is the second part of a three part series of articles covering the .Net assemblies. In Part 1, I coved 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 in its own application domain.
- Shared - To share an assembly such as 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.
Here 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:
vbc 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:
Imports System
Namespace hello
Public Class Class1
Public Sub New()
End Sub 'New
Public Sub SayHello(ByVal name As String)
Console.WriteLine(("Hello " + name))
End Sub 'SayHello
End Class 'Class1
End Namespace 'hello
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.snk
The Strong Name utility will generate a file called myKeys.snk, where the snk extention stands for "strong name key". The -k designates the output file, which 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 just created to your projects 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, any text editor should work. Scroll down to 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 above 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 were ready to test the assembly to see if we can access the SayHello() method.
Using the Shared Assembly
Now lets 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 original shared assembly. Make sure that the reference path points to where the hello.dll file resides and not the current project path.
Imports System
Imports hello
Namespace myHello
Public Class HelloClass
Public Sub New()
End Sub 'New
'Entry point which delegates to C-style main Private Function
Public Overloads Shared Sub Main()
System.Environment.ExitCode = Main(System.Environment.GetCommandLineArgs())
End Sub
Public Overloads Shared Function Main(ByVal args() As String) As Integer
Dim speak As New Class1
speak.SayHello("John")
Return 0
End Function 'Main
End Class 'HelloClass
End Namespace 'myHello
After you compile the program you can place the myHello.exe file most anywhere in your system's file path and it will locate the 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.
Part III
This article is the third 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. Part 2 of the series I discussed both Private and Shared assemblies and how to create a "Shared Assembly". In part 2 I briefly mentioned some of the utilities available for working with assemblies. In this part I will discuss in more detail than Part 2, the available utilities for manipulating assemblies.
Assembly Linker Utility
The Assembly Linker is a command line utility named AL.exe. Ran in the form of AL sources options.
It can be used for installing shared assemblies into the Global Assembly Cache.
It can also be used to create a manifest for IL code that does not already contain one.
It can be used to add resources to an assembly.
Some interesting options that can be used with the utility are…
AL moduleName.dll /out: newName.exe /main: methodName
If you have a multi-file assembly, which includes more than one Main method, you can specify to the application domain which type contains the Main method that you want to use as the entry point for your program.
AL moduleName.dll /out: newName /version: major.minor.revision.build or use /v:
Adds your specified version number to the manifest. You can verify your version was inserted using the ildasm.exe program.
AL moduleName.dll /out: newName /win32icon: iconFileName
Adds an icon file to the assembly so that when Windows Explorer displays your app, the specified icon is shown.
Many of the fields that can be added using the AL.exe utility such as Company, copyright, etc., can be viewed using the Assembly Cache Viewer/properties.
Strong Name Utility
The Strong Name utility is a command line utility named Sn.exe. Ran in the form of Sn sources options.
It can be used to create strong names or shared names for assemblies.
It can be used to create public/private key pairs.
Some useful options that can be used with the utility are…
Sn -k keyFile.snk
This is the same command that we used in Part 2 to create a set of keys.
Sn -R assemblyName keyFile.snk
Re-signs a previously signed assembly.
Sn -v hello.dll
This verifies that the assembly is a strong named assembly.
Assembly 'hello.dll' is valid
Assembly Cache Viewer
The Assembly Cache Viewer is a Windows Shell extension named Shfusion.dll .
This extension is integrated into Windows Explorer when the SDK is installed so that you may view assemblies in the Assembly Cache.
It can be used to view properties of assemblies, add assemblies to the cache or even remove assemblies from the cache.
Use Explorer to navigate to your assembly directory: C:\Windows\assembly. Right-click on an assembly, and select properties. You will see properties such as version, name trademark, copyright, and others. This method can also be a quick way to verify that fields you set using the Assembly Linker utility are as you expected.
To delete an assembly from the cache, right-click on the assembly and select Delete.
To add an assembly to the assembly cache, drag and drop the assembly (which must be a shared name assembly) into the Cache.
NOTE: Adding an assembly this way does not perform the install as does the AL.exe utility. I used the above method to copy and delete assemblies from the cache and it did not create or remove the parent folders of the shared assembly.
Global Assembly Cache Utility
The Global Assembly Cache Utility performs the same operations on assemblies as the above Windows shell extension. The Global Assembly Cache Utility is the command line version and is included with the SDK as a file named Gacutil.exe.
Syntax and usage is as follows:
Gacutil -l
The -l (ell, not number one) option will report the number of shared assemblies in the global cache and will list each shared assembly along with it's version and strong name.
Gacutil -i assemblyName
This installs the assembly to the global cache and does create the needed directories just as the AL.exe utility did when we installed the assembly in PART 2 of the series.
I tried using the -u option to remove my assembly but this option did not properly work. The Gacutil -u assemblyName, ver=versionNum command ran and said that it successfully removed my assembly, but when I viewed the cache the assembly and it's parent folders were still there.
Microsoft Intermediate Language Disassembler
The IL Disassembler is a utility named ildasm.exe.
It can be used for viewing the internals of an assembly and can also generate a text file that can be edited and then recompiled using the Microsoft Intermediate Language Assembler (ilasm.exe).
ildasm
This produces a dialog box that allows you to navigate the assemblies internals. Below is a list of the symbols used to identify the parts of your assembly.

ildasm /TEXT
This causes the ildasm output to be displayed in a console window instead of the dialog box.
ildasm /OUT=fileName
This causes the ildasm output to be saved to the specified file instead of the dialog box.
There are many other utilities provided with the SDK. For a list of available utilities visit: Framework Tools