.NET Assembly Programming
Abstract
In this series, we'll examine the core details of creating, deploying and configuring .NET assemblies and its advantage over existing COM technology. This article goes deeper in terms of understanding the role and format of .NET assembly and modules. You 'll explore assembly manifest and how exactly the .NET runtime resolve the location of assembly and you 'll also come to understand the assembly CIL code .This article will also state the distinction between single file and multi-file assemblies.
Problem with COM
Microsoft itself introduced the phrase "DLL Hell" to describe traditional problem with existing COM DLLs. Often old DLL's are replaced by a new version, and will break applications because a newly installed application overwrites a DLL that has also been used by another application. In fact, such problems occur due to improperly checked versions of DLL by the installation program, while new DLL should be backward compatible with the old version to keep the continuity of existing functionality. The side-by-side DLL installation feature provided by the existing the COM technology is unavailable. Various DLL incorporated functionality features are also referenced to another couple of locations but such functionality is terminated when the old version is replaced by a new functionality version.
You can install two different types of a single assembly in a side-by-side installation feature. Although, this can be applied with COM DLLs but a problem will arise in such a case. Literally, COM DLLs are not self-describing. The configuration of a COM component is stored in the registry, not in the Component DLL itself. So the configuration information is taken from the last version rather than two versions of a single DLL simultaneously.
Understanding Assembly
The .NET Framework overcomes the DLL Hell or Version issues with existing COM Technology by introducing assemblies. Assemblies are self-describing installation units, consisting of single or multiple files. Virtually, every file that is developed and executed under the .NET Common Language Runtime (CLR) is called, an assembly. One Assembly file contains metadata and could be an .EXE, DLL or Resource file. Now, let's discuss some of the comprehensive benefits provided by the assembly.
- Assemblies can be deployed as private or shared. Private assemblies reside in the same solution directory. Shared assemblies, on the other hand, are libraries intended to be consumed by numerous applications on a single machine, because they are deployed to a central repository called GAC.
- The .NET assemblies are assigned a special 4-digit number to concurrently run the multiple versions of an assembly. The 4-digit special number can be specified as "<major>.<minor>.<build>.<revision>".
- In assembly archives every external assembly reference must have access in order to function properly. However, assemblies are self-describing by documenting all the external references in the manifest. The comprehensive details of assemblies such as member function, variable name, base class, interface and constructors are placed in the metadata so that CLR does not need to consult the windows system registry to resolve its location.
- The .NET framework offers you to reuse types in a language-independent manner by not caring how a code library is packaged.
- Application isolation is ensured using application domains. A number of applications can run independently inside a single process with an application domain.
- Installation of an assembly can be as simple as copying all of its files. Unlike COM, there is no need to register them in a windows system registry.
Modules
Before delving into assembly types in detail, let's discuss the modules. An assembly is typically, composed of multiple modules. A module is a DLL without assembly attributes. To get a better understanding, we are creating a c# class library project as the following:
[plain]
public class test
{
public test() { }
public test(string fname, string lname)
{
this.FName = fname;
this.LName = lname;
}
public string FName
{
get;
set;
}
public string LName
{
get;
set;
}
public override string ToString()
{
return FName + " " +LName;
}
}
A module can be created by csc.exe with /module switch. The following command creates a module test.netmodule as;
csc /target:module test.cs
A module also has a manifest, but there isn't an .assembly entry inside the manifest because a module doesn't have a assembly attribute. We can view a module manifest using ildasm utility as following:
The main objective behind modules is that they can be used for faster startup of assemblies, because not all types are inside a single file. The modules are loaded when needed. Secondly, if you want to create an assembly with more than one programming language then one module could be in VB.NET and another in F#.NET. Finally, these two modules could be included in a single file.
Single file and Multi-file Assembly
Technically speaking, an assembly can be formed from a single file and multi-file. A single file assembly contains all the necessary elements such as CIL code, header files and manifests in a single *.exe or *.dll package.
A multi-file assembly, on the other hand, is a set of .NET modules that are deployed and versioned as a single unit. Formally speaking, these modules are termed as primary and secondary modules. The primary module contains an assembly-level manifest and secondary modules which having *.netmodule extension contains a module-level manifest. The major benefit of multi-file assembly is that they provide a very efficient way to download content.
Assembly Structure
An assembly is comprised of assembly metadata describing the complete assembly, type metadata unfolding the exported type and methods, MSIL code and resources. All these fragments can be inside of one file or spread across several files. Structurally speaking, an assembly is composed of the following elements:
CIL code
The CIL code is a CPU and platform-agnostic intermediate language. It can be considered the core back-bone of an assembly. Given this design, the .NET assemblies can indeed execute on a variety of devices, architectures and operating systems. At the runtime, the internal CIL is compiled using the Just0in-time (JIT) compiler, as per to platform and CPU specific instructions.
Understanding the grammar of CIL code can be helpful when you are building complex application but unfortunately most .NET developers don't need to be deeply concerned with the details of CIL code.
Windows File Header
The windows file header determines how the Windows family of operating systems can load and manipulate an assembly. The headers also identify the kind of application such as *.dll, console or GUI applications, to be hosted by windows. You can view the assembly header information using the dumpbin.exe utility as following:
Dumpbin /headers *.dll/*.exe
CLR File Header
The CLR header is a block of data that all .NET assemblies must support in order to be hosted by the CLR. They are typically defined as - numerous flags that enables the runtime to understand the layout of the managed code. We can view such diverse flags using again, dumpbin.exe /clrheader flag as the following:
Metadata
The .NET runtime practices metadata to resolve the location of types within the binary. An assembly metadata comprehensively describes the format of the contained types, as well as the format of external type references. If you press the Ctrl +M keystroke combination, idasm.exe display the metadata for each type within the DLL file assembly as shown below:
Manifest
The assembly manifest documents each module within the assembly, establishes the version and acknowledges the external reference assemblies with its dependencies. The Assembly manifest is a significant part of an assembly, and can be composed in the following parts as;
-
Identity
It includes version, name, culture and public key details.
-
Set of Permissions
This portion displays the necessary permissions to run an assembly.
-
List of Files
It lists all files belonging to a single file or multiple file assemblies.
-
External reference Assemblies
The manifest also documents the external reference files that are needed to run an assembly.
We can explore the assembly manifest using the ildasm.exe utility as following;
Now, open the CSharpTest.dll manifest by double –clicking the MANIFEST icon. The first code block specifies all external assemblies such as mscorlib.dll required by the current assembly to function correctly. Here, each .assembly external block is qualified by the .publickeytoken and .ver directive as following:
Typically, these setting can be configured manually which resides in the solution AssemblyInfo.cs file as:
[plain]
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyTitle("CsharpTest")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CsharpTest")]
[assembly: AssemblyCopyright("Copyright © 2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("2fcf6717-f595-4216-bb93-f6590e37b3e5")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
Resources
Finally, a .NET assembly may contain a number of embedded resources, such as picture files, application icons, sound file and culture information (satellite assemblies in order to build international software).
Summary
This article drilled down into the details of how the CLR resolves the location of external reference assemblies. We began by exploring the disadvantage of existing COM technology, and examined the content within an assembly such as CIL code, header, metadata, manifest and resources. We have also come to understand the distinction between the single file and multi-file assembly. This article also focuses the benefits of modules and assembly in depth. Later, we will also explore the more advance topics related to assembly.