Introduction to the Assembly Concept

What does the term "assembly" mean?

 
An assembly is a portable executable file that contains a compiled portion of code. The assembly is specially designed for deployment purposes such as the *.JAR files that are used in the java context so that developers or intermediate users can use and/or reuse them to build their applications as rapidly as possible. Indeed, a developer can use an already existing assembly to build his application rather than wasting his time on developing custom codes. Assemblies' capacities can also be enhanced by creating new versions with extended capacities and functionalities; moreover, since the component object model COM has been introduced, there are no more frontiers between assemblies and client applications. That means assemblies are used regardless of the language that they are developed with. In fact, this advantage is guaranteed by the common language runtime CLR. Each language supported by the .NET framework is compiled to MSIL language or CIL language pronounced "cil or even kill", and that enables more interaction between components and assemblies written with different .Net languages such as  Visual C#, Visual C++.Net, Visual Basic.Net or Delphi.Net.
 
In contrast to the java environment, where only one kind of package exists which is the *.JAR file, the .NET platform provides two kinds of assemblies which are the *.exe executables files and the *.dll library files.
 
How assemblies are built?
 
An assembly contains a code but is not written with a high-level programming language, in fact, when a programmer achieves a block of code, he builds the project and at that moment begins the assembly coming to the world. When we try to see what is inside of this magic box, or the assembly, we find a block of MSIL "Microsoft Intermediate Language", this last one as his name indicates, can be positioned in the middle between the machine language and the high-level language such as C#. It is possible to discover what is inside of a *.dll or *.exe. The .NET framework provides the MSIL Disassembler (ildasm.exe), which is a tool that enables us to see behind the dll or exe file in terms of code.
 
Suppose an assembly called myAssembly.dll is built. This assembly is developed using C# and here are details:
  1. using System;  
  2. using System.Windows.Forms;  
  3. namespace myAssembly  
  4. {  
  5.     public class Class1  
  6.     {  
  7.         public Class1() { MessageBox.Show("You are using ClassLibrary1!!!"); }  
  8.     }  
To disassemble it, open the SDK command prompt and type ildasm then type the assembly file path. Press enter and a new window will be opened as figure 1 shows:
 
1.gif
 
Figure1
 
The result of the disassembled file is a *.il file which I will describe its elements one by one:
 
The assembly manifest is the technical description that gives information about:
 
Name The name of the assembly
Version The version of the assembly e.g. 2.0.0.0
Culture Used when we want to indicate that assembly is satellite, otherwise, the value-neutral is set
Public key information The public key used to decrypt information when we take security issues into consideration, otherwise the value null is set or if we won't share the assembly with other developers    
The files list located in the assembly The hash code  and the file name of each element located in the assembly
Type reference information Information about all types exported from the assembly
Referenced assemblies The list of all other assemblies referenced by the present assembly
 
This is the class 1 compilation result
  1. // Metadata version: v2.0    
  2. .assembly extern mscorlib {    
  3.   .publickeytoken = (B7 7 B 5 C 56 20 34 E0 89) // .z\V.4..    
  4.     .ver 2: 0: 0: 0    
  5. }    
  6. .assembly extern System.Windows.Forms {    
  7.     .publickeytoken = (B7 7 B 5 C 56 20 34 E0 89) // .z\V.4..    
  8.       .ver 2: 0: 0: 0    
  9.   }    
  10.   .assembly myAssembly {    
  11.     .custom instance void[mscorlib] System.Resources.NeutralResourcesLanguageAttribute::.ctor(string) = (01 00 05 65 6 E 2 D 55 53 00 00) // ...en-US..    
  12.       .custom instance void[mscorlib] System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = (01 00 07 31 2 E 30 2 E 30 2 E 30 00 00) // ...1.0.0.0..    
  13.       .custom instance void[mscorlib] System.Runtime.InteropServices.GuidAttribute::.ctor(string) = (01 00 24 31 34 66 46 32 39 34 38 2 D 64 36 35 66 // ..$14fe2948-d65f    
  14.         2 D 34 35 63 66 2 D 61 65 36 61 2 D 33 65 62 39 63 // -45cf-ae6a-3eb9c    
  15.         31 37 36 64 61 39 37 00 00) // 176da97..    
  16.       .custom instance void[mscorlib] System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = (01 00 01 00 00)    
  17.       .custom instance void[mscorlib] System.Reflection.AssemblyTrademarkAttribute::.ctor(string) = (01 00 00 00 00)    
  18.       .custom instance void[mscorlib] System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = (01 00 13 43 6 F 70 79 72 69 67 68 74 20 C2 A9 20 // ...Copyright ..    
  19.         2 E 20 32 30 30 37 00 00) // . 2007..    
  20.       .custom instance void[mscorlib] System.Reflection.AssemblyProductAttribute::.ctor(string) = (01 00 0 A 6 D 79 41 73 73 65 6 D 62 6 C 79 00 00) // ...myAssembly..    
  21.       .custom instance void[mscorlib] System.Reflection.AssemblyCompanyAttribute::.ctor(string) = (01 00 0 D 47 52 45 41 54 20 4 E 55 4 D 49 44 49 41 // ...GREAT NUMIDIA    
  22.         00 00)    
  23.       .custom instance void[mscorlib] System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = (01 00 00 00 00)    
  24.       .custom instance void[mscorlib] System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = (01 00 1 A 54 68 69 13 20 69 73 30 6 F 6 E 6 C 79 20 // ...This is only    
  25.         66 6 F 72 20 74 72 69 61 6 C 20 75 73 65 00 00) // for trial use..    
  26.       .custom instance void[mscorlib] System.Reflection.AssemblyTitleAttribute::.ctor(string) = (01 00 0 A 6 D 79 41 73 73 65 6 D 62 6 C 79 00 00) // ...myAssembly..    
  27.     
  28.       // --- The following custom attribute is added automatically, do not uncomment -------    
  29.       //  .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 )    
  30.     
  31.       .custom instance void[mscorlib] System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = (01 00 08 00 00 00 00 00)    
  32.       .custom instance void[mscorlib] System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = (01 00 01 00 54 02 16 57 72 61 70 4 E 6 F 6 E 45 78 // ....T..WrapNonEx    
  33.         63 65 70 74 69 6 F 6 E 54 68 72 6 F 77 73 01) // ceptionThrows.    
  34.       .publickey = (00 24 00 00 04 80 00 00 94 00 00 00 06 02 00 00 // .$..............    
  35.         00 24 00 00 52 53 41 31 00 04 00 00 01 00 01 00 // .$..RSA1........    
  36.         45 47 5 A 50 E9 58 C8 F7 5 E F8 84 A0 61 52 57 B7 // EGRP.X..^...aRW.    
  37.         69 76 35 29 CA A7 5 C B7 69 E9 B9 52 11 6 A D5 93 // iv5)..\.i..R.j..    
  38.         27 8 A 94 F8 E2 21 67 98 4 C 5 C D3 80 EB BE 4 D 03 // '....!g.L\....M.    
  39.         E4 85 3 B 3 D F8 E3 86 AF 2 F 18 C4 37 E2 04 65 D4 // ..;=..../..7..e.    
  40.         4 C 41 DC F0 52 38 52 79 33 CD 38 0 C F5 C1 09 24 // LA..R8Ry3.8....$    
  41.         39 E5 1 C F2 8 B 5 C 56 9 B 3 E A4 E2 0 F 40 B9 95 53 // 9....\V.>...@..S    
  42.         7 E A4 2 E 5 E 12 01 DD 4 E 08 55 67 C4 68 DD D7 C7 // ~..^...N.Ug.h...    
  43.         06 33 36 16 69 3 C 2 A 09 44 D5 A0 C1 C1 C0 9 D EE) // .37.i<*.D.......    
  44.       .hash algorithm 0x00002004    
  45.       .ver 1: 0: 0: 0    
  46.   }    
  47.   .module myAssembly.dll    
  48.   // MVID: 806F89E4-D689-418E-AB07-98C43BFF229B    
  49.   .imagebase 0x00400000    
  50.   .file alignment 0x00001000    
  51.   .stackreserve 0x00100000    
  52.   .subsystem 0x0003 // WINDOWS_CUI    
  53.   .corflags 0x00000009 //  ILONLY    
  54. // Image base: 0x00EC0000   
  1. .class public auto ansi beforefieldinit myAssembly.Class1  
  2. extends[mscorlib] System.Object {} // end of class myAssembly.Class1 
This is the class1 constructor compilation result:
  1. .method public hidebysig specialname rtspecialname instance void.ctor() cil managed {  
  2.   // Code size       21 (0x15)  
  3.   .maxstack 8  
  4.   IL_0000: ldarg .0  
  5.   IL_0001: call instance void[mscorlib] System.Object::.ctor()  
  6.   IL_0006: nop  
  7.   IL_0007: nop  
  8.   IL_0008: ldstr "You are using the old version!!!"  
  9.   IL_000d: call valuetype[System.Windows.Forms] System.Windows.Forms.DialogResult[System.Windows.Forms] System.Windows.Forms.MessageBox::Show(string)  
  10.   IL_0012: pop  
  11.   IL_0013: nop  
  12.   IL_0014: ret  
  13. // end of method Class1::.ctor 
It is possible to apply some options such as /text, /rtf or /html to stock the disassembled result in a file with previous formats. To discover other utilities provided by this tool, type ildasm /help and press enter.
 
What does it mean a strong-named assembly?
 
A strong-named assembly is an assembly that has a full name. A full name is composed of four elements, namely, the assembly name such as "myAssembly". The version for e.g. 2.0.0.0, as shown, is composed of 4 parts: major.minor.build.revision. The version helps other applications reference the required assembly when an instance of this last one is enhanced.  The culture, indicated by "en-US", provides the language supported by the assembly. It is used in the assembly's satellites case; otherwise, its value is set as neutral and finally. Public Key Token is used when security measures are suggested. A public key token is a 64 bit hash of the Public key that corresponds to a private key given by the assembly developer to sign its assembly. This private key will be generated when the assembly is deployed and not at design time. The developer must keep his private key hidden, otherwise, spoofing attacks done by crackers cannot be avoided. The public key is also used as an assembly identifier. In fact, the windows file system recognizes the portable files only by their names; therefore, it uses the public key to distinguish assemblies that have the same name.
 
What does it mean an assembly satellite?
 
Satellites' assemblies are employed in a multi-language context such as an application dedicated to a wide number of people issued from different cultures. To deal with this divergence in terms of languages, we use satellite assemblies with culture as "en-US" or "fr-FR". Satellite assemblies are wrapped around the main assembly that is considered as a core and with which they work side by side. After the deployment, each satellite assembly should be located in its own specific subdirectory, for example, a satellite assembly that supports the French language should be installed in %root%\ProgramFiles\Application Directory\French\Assembly. dll. Satellite assemblies can contain only resources, thus, they can not contain executable code. In other words, they can have only a .dll extension.
 
How an assembly is structured?
  
2.gif
 
Figure 2
 
As the above representation shows, an assembly is composed of several elements including:
 
1. Namespace:
 
A namespace plays the role of the container for blocks of codes that are used for one type of service.
 
2. Module:
 
It is also known as a portable executable unit. It contains a number of classes and interfaces and it is possible for a given module to span namespaces too. The only difference between the assembly and the module is that the last one cannot be used directly by the client application. It must be contained in an assembly; then it can be used, thus, the module represents for an assembly what represents a library component object or a user control for a windows application or a web application. The file module has .netmodule as the extension.
 
Generally, an assembly is composed of one unit or blocks in a C# or Visual Basic.Net context, but an assembly composed of multi-files also exists in Visual C++.Net context, furthermore, we can use the assembly linker (al.exe) or C sharp compiler (csc.exe) provided by the framework to assemble several *.netmodule files in one unit in a C# or Visual Basic.Net context.
 
3. Type:
 
A type can be represented as a class, an interface, a generic, an array, a string, a collection, an enumeration.
 
It can be simple such as double, integer, string, etc., or a composite too and that means that it can be composed of multiple other types. Classes and interfaces are composite types. The .Net framework provides the System.Reflexion namespace to get information about a given type.
 
How practically assemblies are built? 
 
It is very important to answer this question because it is not sufficient to take only theory into consideration and neglect the practical side of the issue. Therefore here is an example of how one can build an assembly using only the .Net framework and a simple block note.
 
Example 1:   
 
Let assume that we have to develop a *.exe assembly called "Arithmetic" and which treats arithmetic operations +,-,*, and /. Here's the assembly structure
 
new3.gif
 
Figure 3
 
To begin, create a folder under the root and call it Arithmetic. Then open a new block notes and paste this block of code.
 

  1. using System;  
  2.   
  3. namespace Arithmetic {  
  4.     public class Program {  
  5.         static void Main(string[] args) {  
  6.             try {  
  7.                 Console.WriteLine("You are welcome to the Arithmetic assembly!!!");  
  8.                 Console.WriteLine("Please, enter the first number");  
  9.                 string FirstNumber = Console.ReadLine();  
  10.                 Console.WriteLine("Now, enter a second number");  
  11.                 string SecondNumber = Console.ReadLine();  
  12.                 Plus.operation(FirstNumber, SecondNumber);  
  13.                 Minus.operation(FirstNumber, SecondNumber);  
  14.                 Multiply.operation(FirstNumber, SecondNumber);  
  15.                 Divide.operation(FirstNumber, SecondNumber);  
  16.                 Rest.operation(FirstNumber, SecondNumber);  
  17.                 Console.Read();  
  18.             } catch (FormatException caught) { Console.Clear();  
  19.                 Console.WriteLine("Please enter character with only numeric format");  
  20.                 Console.Read(); }  
  21.         }  
  22.     }  
  23.     public class Plus {  
  24.         public Plus() {}  
  25.         public static void operation(string Element1, string Element2) {  
  26.             double Number1 = Convert.ToDouble(Element1);  
  27.             double Number2 = Convert.ToDouble(Element2);  
  28.             double Result = Number1 + Number2;  
  29.             Console.WriteLine("The result of addition is :");  
  30.             Console.WriteLine(Convert.ToString(Result));  
  31.   
  32.         }  
  33.     }  
  34.     public class Minus {  
  35.         public Minus() {}  
  36.         public static void operation(string Element1, string Element2) {  
  37.             double Number1 = Convert.ToDouble(Element1);  
  38.             double Number2 = Convert.ToDouble(Element2);  
  39.             double Result = Number1 - Number2;  
  40.             Console.WriteLine("The result of soustration is :");  
  41.             Console.WriteLine(Convert.ToString(Result));  
  42.         }  
  43.     }  
  44.     public class Multiply {  
  45.         public Multiply() {}  
  46.         public static void operation(string Element1, string Element2) {  
  47.             double Number1 = Convert.ToDouble(Element1);  
  48.             double Number2 = Convert.ToDouble(Element2);  
  49.             double Result = Number1 * Number2;  
  50.             Console.WriteLine("The result of Multiplication is :");  
  51.             Console.WriteLine(Convert.ToString(Result));  
  52.         }  
  53.     }  
  54.     public class Divide {  
  55.         public Divide() {}  
  56.         public static void operation(string Element1, string Element2) {  
  57.             try {  
  58.                 double Number1 = Convert.ToDouble(Element1);  
  59.                 double Number2 = Convert.ToDouble(Element2);  
  60.                 if (Number2 == 0) throw new DivideByZeroException();  
  61.                 double Result = Number1 / Number2;  
  62.                 Console.WriteLine("The result of division is :");  
  63.                 Console.WriteLine(Convert.ToString(Result));  
  64.             } catch (DivideByZeroException caught) { Console.Clear();  
  65.                 Console.WriteLine("Division by zero happened!");  
  66.                 Console.Read(); }  
  67.         }  
  68.     }  
  69.     public class Rest {  
  70.         public Rest() {}  
  71.         public static void operation(string Element1, string Element2) {  
  72.             try {  
  73.                 double Number1 = Convert.ToDouble(Element1);  
  74.                 double Number2 = Convert.ToDouble(Element2);  
  75.                 if (Number2 == 0) throw new DivideByZeroException();  
  76.                 double Result = Number1 % Number2;  
  77.                 Console.WriteLine("The rest of division between " + Element1 + " and " + Element2 + "  is :");  
  78.                 Console.WriteLine(Convert.ToString(Result));  
  79.             } catch (DivideByZeroException caught) { Console.Clear();  
  80.                 Console.WriteLine("Division by zero happened!");  
  81.                 Console.Read(); }  
  82.         }  
  83.     }  

Now, save the content file as "Arithmetic.cs". A new file with cs extension will be created. To compile it, open the SDK command prompt[1], and type the command as follow:
 
4.gif
 
Figure4
 
Browse to the %root%:\Program Files\Microsoft Visual Studio 8\SDK\v2.0, there you find the assembly Arithmetic.exe with as icon. 
 
Example 2:
 
It is possible to create a multi-file assembly using modules. Suppose that we want to create a dll assembly that treats arithmetic operations. For doing that, try to copy and paste each of the previous example classes in separate bloc-notes except the Program classes. After that, save each bloc note content as cs file. Five classes are obtained; I mean, "Plus.cs", "Minus.cs", "Multiply.cs", "Divide.cs" and "Reste.cs".  
 
5.gif
 
Figure 5
 
Now, we try to transform them into modules by opening the SDK command prompt and taping those commands.
 
part2.gif
 
Figure 5
 
To locate the generated modules, browse to the following folder %root%:\Program Files\Microsoft Visual Studio 8\SDK\v2.0. There one can find all generated modules.
 
11.gif
 
Figure 6
 
After generating modules, we use the assembly linker (al.exe) provided by the .Net framework to assemble them in one unit, namely, the assembly Arithmetic.dll. To do that, open the SDK command prompt and type this command:
 
12.gif
 
Figure 6
 
Now, browse to %root%\Arithmetic and there the dll assembly is found.
 
To disassemble the Arithmetic.dll, type this command:
 
13.gif
 
Figure 7
 
The assembly manifest informs us about the Arithmetic.dll content which is:
  1. // Metadata version: v2.0.50727.42  
  2. .assembly extern mscorlib {  
  3.   .publickeytoken = (B7 7 A 5 C 56 19 34 E0 89) // .z\V.4..  
  4.     .ver 1: 0: 0: 0  
  5. }  
  6. .assembly Arithmetic {  
  7.   .custom instance void[mscorlib] System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = (01 00 01 00 54 02 16 57 72 61 70 4 E 6 F 6 E 45 78 // ....T..WrapNonEx  
  8.       63 65 70 74 69 6 F 6 E 54 68 72 6 F 77 73 01) // ceptionThrows.  
  9.     .hash algorithm 0x00008004  
  10.     .ver 0: 0: 0: 0  
  11. }  
  12. .file Plus.netmodule  
  13.   .hash = (90 1 C 06 EE F3 AA 29 6 D 51 44 28 E8 1 D 2 E A1 36 // ......)mQD(....6  
  14.     BB D1 12 08)  
  15.   .file Minus.netmodule  
  16.   .hash = (B1 BA D9 1 C 46 BB 04 30 40 67 B2 B4 33 32 F3 37 // ....F..0@g..32.7  
  17.     F3 14 86 63) // ...c  
  18.   .file Multiply.netmodule  
  19.   .hash = (29 DE 05 80 14 D4 9 F 12 84 7 A 1 A B0 C7 0 D 90 92 // )........z......  
  20.     D4 A3 B0 9 E)  
  21.   .file Divide.netmodule  
  22.   .hash = (A7 94 EB 2 B 1 E 6 B 56 40 D1 05 E1 86 42 F8 0B 84 // ...+.kV@....B...  
  23.     87 5 E 13 DC) // .^..  
  24.   .file Rest.netmodule  
  25.   .hash = (7 C E1 1 F B4 DB 12 42 F7 72 8 B CA 41 88 0B 01 2 F // |.....B.r..A.../  
  26.     96 49 5 D EB) // .I].  
  27.   .class extern public Plus {  
  28.     .file Plus.netmodule  
  29.       .class 0x02000002  
  30.   }  
  31.   .class extern public Minus {  
  32.     .file Minus.netmodule  
  33.       .class 0x02000002  
  34.   }  
  35.   .class extern public Multiply {  
  36.     .file Multiply.netmodule  
  37.       .class 0x02000002  
  38.   }  
  39.   .class extern public Divide {  
  40.     .file Divide.netmodule  
  41.       .class 0x02000002  
  42.   }  
  43.   .class extern public Rest {  
  44.     .file Rest.netmodule  
  45.       .class 0x02000002  
  46.   }  
  47.   .module Arithmetic.dll  
  48.   // MVID: {DE9D1E19-B607-4F14-9CD7-29F5B63726B2}  
  49.   .imagebase 0x00400000  
  50.   .file alignment 0x00000200  
  51.   .stackreserve 0x00100000  
  52.   .subsystem 0x0003 // WINDOWS_CUI  
  53.   .corflags 0x00000001 //  ILONLY  
  54. // Image base: 0x00F40000 
The current assembly manifest enables us to set an image of the assembly structure 
 
new8.gif
 
Figure 8
 
What does it mean to sign an assembly and what for?
 
It is very important to sign an assembly because it helps us to protect our work, to keep it out of the reverse engineering operations, and to enable us to have the possibility to grant or not grant the assembly used for tiers.
 
The .Net provides us tools that enable the generation of a key pair. I mean a private and a public key and to associate those two objects to a given assembly.
 
Example:
 
Suppose that we won't sign the previously generated assembly "Arithmetic.dll". Let us begin by creating a pair of Public/Private key files with *.snk as an extension. To do that, open the SDK command prompt and type:
 
15.gif
 
Figure 9
 
If all things are all right, the shell will give us as output the following message:
 
16.gif
 
Figure 10
 
The second step is critical; we proceed to sign the "Arithmetic.dll" assembly. To do that, open a new prompt and type:
 
17.gif
 
Figure 11
 
Finally, the job is done and the assembly is signed. But the private key must be kept hidden and strong-named assemblies should consume only other strong-named assembly's services; otherwise, the assembly privacy would be compromised.

Recommended Ebook

Diving Into Microsoft .NET Entity Framework

Download Now!
Similar Articles