Friday, May 4, 2012

Assembly in .Net

An assembly is a fundamental unit of any .NET application. It contains the code that is executed by CLR (common language runtime). I would like to limit the details to what is required to create and use an assembly. For more information about all nitty gritties of an assembly, please refer to MSDN documentation.
However, it is important to know a few details about assemblies before we delve into creating and using it.
  • An assembly contains name, version, types (classes and others) created in it and details about other assemblies it references.
  • An assembly may be either an executable file - .EXE or a dynamic link library - .DLL
The following is the content of an assembly. Each assembly contains first three parts. Fourth part may not be present in all assemblies. It is used primarily for localization - using resources according to the country or region.
  • Assembly Metadata or Manifest
  • Type Metadata
  • MSIL Code
  • Resources
Assembly Metadata or Manifest
This contains information about the assembly. Remember, assemblies in .NET are self-describing. They contain all the information that .NET needs to use them. Assembly metadata contains the following details of an assembly:
  • Assembly name
  • Version number of the assembly, which has four numbers in the format
  • Culture - language assembly supports
  • Strong name - required only for global assemblies
  • List of files in the assembly. An assembly can be made up of multiple files
  • Type reference information - informs which type is in which file of the assembly
  • Information about referenced assemblies - Contains list of other assemblies referenced by this assembly. For each assembly referenced we have assembly name, version, culture and public key (if assembly is a global assembly)
Type metadata
This section of an assembly contains information about all classes, structure etc. created in the assembly.
MSIL code of the assembly is placed in third part of the assembly. This MSIL is converted to native code by CLR at runtime.
This section contains messages and pictures used by assembly.
How to create an assembly in C#
The following are the steps to create a private assembly (by default all assemblies are private) using Visual C# 2005 Express Edition.
  1. Select File->New Project
  2. From Templates, select Class Library
  3. Enter name CounterLibrary
  4. A class library is created using a single class Class1
  5. Rename class to Counter and add the following code.
6.  namespace CounterLibrary
7.  {
8.      public class Counter
9.      {
10.        protected int v = 0;
11.        public Counter(int v)
12.        {
13.            this.v = v;
14.        }
15.        public int Value
16.        {
17.            get
18.            {
19.                return v;
20.            }
21.        }
22.    }
  1. Save project using File->Save All. When prompted to enter location for project, select the folder where you want to save your project. I use c:\csharp. Do not select checkbox for Create directory for solution
  2. Build (not run) the project using Build->Build Solution
After the above process, we get CounterLibrary.dll assembly placed in c:\csharp\counterlibrary\bin\release directory.
The next step is to use this assembly in a console application. As a matter of fact, once a source program is converted to MSIL, it can be used anywhere in .NET, irrespective of language and type of application.
Using a private assembly in a console application developed in C#
Now, let us use the class library created in C# in a console application. Though I am using a console application in C#, you can use any language supported by .NET. 
  1. Start Visual C# 2005 Express Edition
  2. Create a new console application using File -> New Project
  3. From template select Console Application as type of project
  4. Give name UseCounter for application.
    A new application is created with a single class with Main() method.
  5. Go to Solution Explorer and select project
  6. Right click on it and select Add References from the context menu.
  7. From dialog box, select Browse tab and select c:\csharp\counterlibrary\bin\release\counterlibrary.dll
  8. Solution explorer displays counterlibrary as one of the references under references node in solution explorer
  9. Add the following code in Main() method of Program.cs
10.using System;
11.namespace UseCounter
13.    class Program
14.    {
15.        static void Main(string[] args)
16.        {
17.            counterlibrary.Counter c = new counterlibrary.Counter(100);
18.            c.Inc();
19.            Console.WriteLine(c.Value);
20.        }
21.    }
As you do the above, you can notice that a copy of counterlibrary.dll is copied into BIN directory of UseCounter application. This is the case with any private library. Whenever an application makes a reference to it, a copy of private assembly is copied into it's bin directory.

If you do not see .DLL file that is copied to BIN directory of console application (UseCounter), close the application and reopen it.

Making a private assembly a global assembly
A global assembly is a public assembly that is shared by multiple applications. Unlike private assembly, a global assembly is not copied to bin directory of each application that references it. Global assembly instead is placed in GAC (Global Assembly Cache) and it can be referenced anywhere within the system. So only one copy is stored, but many applications can use that single copy.
In order to convert a private assembly to global assembly, we have to take the following steps.
  • Create a strong name
  • Associate strong name with assembly
  • Place assembly in GAC
Creating a strong name
Any assembly that is to be placed in GAC, must have a strong name. Strong name is a combination of public key and private key. The relationship between public and private keys are such, given one you cannot get the other, but any data that is encrypted with private key can be decrypted only with the corresponding public key.
Take the following steps to invoke SN (Strong Name) tool to create strong name.
  1. Go to command prompt using Microsoft .NET Framework SDK v2.0 -> SDK Command prompt
  2. Go to c:\csharp\counterlibrary folder and enter the following command.
3.  sn -k srikanth.key
  1. The above command writes private and public key pair into srikanth.key file.
Associate strong name with assembly
Once private and public keys are generated using SN tool, use the following procedure to sign counterlibrary with the key file.
  1. Open counterlibrary project.
  2. Select project properties using Project -> counterlibrary properties
  3. Select Signing tab in project properties window
  4. Check Sign the assembly check box
  5. Select srikanth.key file using Choose a strong name key file combo box
  6. Close properties window
  7. Build the solution again using Build->Build Solution
Now, counterlibrary.dll is associated with a public key and also digitally signed with private key. This ensures no one can modify this assembly as any change to assembly should re-sign the assembly with private key of the user who created it first. This protects the assembly from getting tampered with by others. A global assembly needs this projection as it is placed in common place.
You can verify whether the assembly is associated with public key using ILDASM (IL Disassembler) program provided by .NET Framework.
  • Start ILDASM using .NET Framework SDK v2.0->Tools->MSIL Disassembler
  • Select counterlibrary.dll using File->Open
  • Once assembly is opened, double click on Manifest section of the assembly to see the public key associated with the assembly.
Place assembly in GAC
In order to make an assembly a global assembly, the assembly must be associated with a strong name and then placed in Global Assembly Cache (GAC).
GAC is a folder with name Assembly in windows folder of your system. So, place counterlibrary.dll in GAC using GACUTIL tool as follows.
c:\csharp\counterlibrary\bin\Release>gacutil -i counterlibrary.dll
After you install global assembly into GAC, you can see counterlibrary.dll in windows/assembly folder.
Once, you place an assembly in GAC, any reference to the assembly will not create a copy of the assembly in BIN directory of the application. Instead all application that reference the assembly use the same copy that is placed in GAC.