……………………………………………….Expertise in .NET Technologies

Archive for February 19th, 2009

Assemblies in .NET

Posted by Ravi Varma Thumati on February 19, 2009

Assemblies are the building blocks of .NET Framework applications; they form the fundamental unit of deployment, version control, reuse, activation scoping, and security permissions. An assembly is a collection of types and resources that are built to work together and form a logical unit of functionality. An assembly provides the common language runtime with the information it needs to be aware of type implementations. To the runtime, a type does not exist outside the context of an assembly.

Assemblies can be static or dynamic. Static assemblies can include .NET Framework types (interfaces and classes), as well as resources for the assembly (bitmaps, JPEG files, resource files, and so on). Static assemblies are stored on disk in portable executable (PE) files. You can also use the .NET Framework to create dynamic assemblies, which are run directly from memory and are not saved to disk before execution. You can save dynamic assemblies to disk after they have executed.

There are several ways to create assemblies. You can use development tools, such as Visual Studio .NET, that you have used in the past to create .dll or .exe files. You can use tools provided in the .NET Framework SDK to create assemblies with modules created in other development environments. You can also use common language runtime APIs, such as Reflection.Emit, to create dynamic assemblies.

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.

Assembly Benefits

Assemblies are designed to simplify application deployment and to solve versioning problems that can occur with component-based applications.

Versioning Problems

Currently two versioning problems occur with Win32 applications:

  • Versioning rules cannot be expressed between pieces of an application and enforced by the operating system. The current approach relies on backward compatibility, which is often difficult to guarantee. Interface definitions must be static, once published, and a single piece of code must maintain backward compatibility with previous versions. Furthermore, code is typically designed so that only a single version of it can be present and executing on a computer at any given time.
  • There is no way to maintain consistency between sets of components that are built together and the set that is present at run time.

The Assembly Solution

To solve versioning problems, as well as the remaining problems that lead to DLL conflicts, the runtime uses assemblies to do the following:

  • Enable developers to specify version rules between different software components.
  • Provide the infrastructure to enforce versioning rules.
  • Provide the infrastructure to allow multiple versions of a component to be run simultaneously (called side-by-side execution).

Assembly Contents

In general, a static assembly can consist of four elements:

  • The assembly manifest, which contains assembly metadata.
  • Type metadata – Boundaries and scopes of methods, classes, properties, events, attributes.
  • Microsoft intermediate language (MSIL) code that implements the types.
  • A set of resources.
  • Assembly name – Aids in versioning and visibility scope.
  • Version information – The version number is integrated into the assembly’s identity.
  • 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.

Only the assembly manifest is required, but either types or resources are needed to give the assembly any meaningful functionality.

There are several ways to group these elements in an assembly. You can group all elements in a single physical file, which is shown in the following illustration.

Single-file assembly


Alternatively, the elements of an assembly can be contained in several files. These files can be modules of compiled code (.netmodule), resources (such as .bmp or .jpg files), or other files required by the application. Create a multifile assembly when you want to combine modules written in different languages and to optimize downloading an application by putting seldom used types in a module that is downloaded only when needed.

In the following illustration, the developer of a hypothetical application has chosen to separate some utility code into a different module and to keep a large resource file (in this case a .bmp image) in its original file. The .NET Framework downloads a file only when it is referenced; keeping infrequently referenced code in a separate file from the application optimizes code download.

Multifile assembly


Note   The files that make up a multifile assembly are not physically linked by the file system. Rather, they are linked through the assembly manifest and the common language runtime manages them as a unit.

In this illustration, all three files belong to an assembly, as described in the assembly manifest contained in MyAssembly.dll. To the file system, they are three separate files. Note that the file Util.netmodule was compiled as a module because it contains no assembly information. When the assembly was created, the assembly manifest was added to MyAssembly.dll, indicating its relationship with Util.netmodule and Graphic.bmp.

As you currently design your source code, you make explicit decisions about how to partition the functionality of your application into one or more files. When designing .NET Framework code, you will make similar decisions about how to partition the functionality into one or more assemblies.

 Types of Assemblies

1.    Private Assembly.

2.    Public / Shared Assembly

3.    Satellite Assembly

What is private and shared assembly?

The assembly which is used only by a single application is called as private assembly. Suppose you created a DLL which encapsulates your business logic. This DLL will be used by your client application only and not by any other application. In order to run the application properly your DLL must reside in the same folder in which the client application is installed. Thus the assembly is private to your application.

 Suppose that you are creating a general purpose DLL which provides functionality which will be used by variety of applications. Now, instead of each client application having its own copy of DLL you can place the DLL in ‘global assembly cache’. Such assemblies are called as shared assemblies.

Difference between a Private Assembly and a Shared Assembly

1.    Location and visibility: A private assembly is normally used by a single application, and is stored in the application’s directory, or a sub-directory beneath. A shared assembly is normally stored in the global assembly cache, which is a repository of assemblies maintained by the .NET runtime. Shared assemblies are usually libraries of code which many applications will find useful, e.g. the .NET framework classes.

2.    Versioning: The runtime enforces versioning constraints only on shared assemblies, not on private assemblies

How Can Assemblies Avoid DLL Hell?

Most assemblies are private. Hence, each client application refers assemblies from its own installation folder. So, even though there are multiple versions of the same assembly, they will not conflict with each other. Consider the following example:

 When you create a private assembly, an assembly is installed in a subdirectory of the application, so even if two assemblies have the same name there is no problem because an application will always refer to its own assembly. Now, consider the case when you develop a shared assembly. In this case, it is important to know how assemblies are versioned. All assemblies have a version number in this form:

If you change the original assembly, the changed version will be considered compatible with the existing one if the major and minor versions of both the assemblies match. When the client application requests an assembly, the requested version number is matched against available versions and the version matching the major and minor version numbers and having the latest build and revision numbers are supplied.

What is Global Assembly Cache?

Global assembly cache is nothing but a special disk folder where all the shared assemblies will be kept. It is located under <drive>:WinNTAssembly folder.

How Do I Create Shared Assemblies?

The following steps are involved in creating shared assemblies:

1.      Create your DLL/EXE source code.

2.      Generate a unique assembly name using SN utility.

3.      Sign your DLL/EXE with the private key by modifying the Assembly Info file.

4.      Compile your DLL/EXE.

5.      Place the resultant DLL/EXE in a global assembly cache by using the AL utility.

How Do I Create a Unique Assembly Name?

Microsoft now uses a public-private key pair to uniquely identify an assembly. These keys are generated by using a utility called SN.exe (SN stands for shared name). The most common syntax of it is:

sn -k mykeyfile.key

where k represents that you want to generate a key and the file name following is the file in which the keys will be stored.

How Do I Sign My DLL/EXE?

Before placing the assembly into a shared cache, you need to sign it by using the keys you just generated. You mention the signing information in a special file called AssemblyInfo. Open the file from VS.NET solution explorer and change it to include the following line:


Now, recompile the project and the assembly will be signed for you.

Note: You also can supply the key file information during a command line compilation via the /a.keyfile switch.

How Do I Place the Assembly in a Shared Cache?

Microsoft has provided a utility called AL.exe to actually place your assembly in shared cache:

AL /i:my_dll.dll

Now, the utility will place your DLL at the proper location.

Hands On…

Now that you understand the basics of assemblies, you can apply your knowledge by developing a simple shared assembly. In this example, you will create a C#.NET component called SampleGAC (GAC stands for Global Assembly Cache). You also will create a key file named sample.key. You will sign your component with this key file and place it in the Global Assembly Cache.

Step 1: Creating your sample component

Here is the code for the component. It just includes one method that returns a string.

using System;
namespace BAJComponents
   public class Sample
      public string GetData()
         return "hello world";

Step 2: Generate a key file

To generate the key file, issue the following command at the command prompt.

sn -k sample.key

This will generate the key file in the same folder.

Step 3: Sign your component with the key

Now, you will sign the assembly with the key file you just created.

csc sampleGAC.cs /t:library  /a.keyfile:sample.key

Step 4: Host the signed assembly in the Global Assembly Cache

You will use the AL utility to place the assembly in the Global Assembly Cache:

AL /i:sampleGAC.dll

After hosting, the assembly just goes to the WINNTAssembly folder and you will find your assembly listed there. Note how the assembly folder is treated differently than normal folders.

Step 5: Test that your assembly works

Now, create a sample client application that uses your shared assembly. Just create a sample code as listed below:

using System;
using BAJComponents;
public class SampleTest
   static void main()
      sample x= new sample();
      string s= x.getdata();

Compile the above code by using:

csc sampletest.cs /t:exe /r:<assembly_dll_path_here>

Now, copy the resulting EXE in any other folder and run it. It will display “Hello World”, indicating that it is using your shared assembly.

What Is a Satellite Assembly?

A definition from MSDN says something like this: “A .NET Framework assembly containing resources specific to a given language. Using satellite assemblies, you can place the resources for different languages in different assemblies, and the correct assembly is loaded into memory only if the user elects to view the application in that language.”

 This means that you develop your application in a default language and add flexibility to react with change in the locale. Say, for example, you developed your application in an en-US locale. Now, your application has multilingual support. When you deploy your code in, say, India, you want to show labels, messages shown in the national language which is other than English.

 Satellite assemblies give this flexibility. You create any simple text file with translated strings, create resources, and put them into the bindebug folder. That’s it. The next time, your code will read the CurrentCulture property of the current thread and accordingly load the appropriate resource.

 This is called the hub and spoke model. It requires that you place resources in specific locations so that they can be located and used easily. If you do not compile and name resources as expected, or if you do not place them in the correct locations, the common language runtime will not be able to locate them. As a result, the runtime uses the default resource set.

Creating a Satellite Assembly

1.      Create a folder with a specific culture name (for example, en-US) in the application’s bindebug folder.

2.      Create a .resx file in that folder. Place all translated strings into it.

3.      Create a .resources file by using the following command from the .NET command prompt. (localizationsample is the name of the application namespace. If your application uses a nested namespace structure like MyApp.YourApp.MyName.YourName as the type of namespace, just use the uppermost namespace for creating resources files—MyApp.)

4.             resgen Strings.en-US.resx LocalizationSample.
5.                Strings.en-US.resources
6.             al /embed:LocalizationSample.Strings.en-US.resources
7.                /out:LocalizationSample.resources.dll /c:en-US

The above step will create two files, LocalizationSample.Strings.en-US.resources and LocalizationSample.resources.dll. Here, LocalizationSample is the name space of the application.

8.      In the code, find the user’s language; for example, en-US. This is culture specific.

9.      Give the assembly name as the name of .resx file. In this case, it is Strings.

Using a Satellite Assembly

Follow these steps:

Thread.CurrentThread.CurrentCulture =
Thread.CurrentThread.CurrentUICulture =
   new CultureInfo(specCult);
ResourceManager resMgr =
   new ResourceManager(typeof(Form1).Namespace + "." +
                       asmName, this.GetType().Assembly);
btnTest.Text = resMgr.GetString("Jayant");

That’s it. See how simple is it to create a satellite assembly and use it in your code.

Here’s how to use a satellite assembly if your assembly is a strong named assembly. When you create an assembly with a string name, all the assemblies it refers to must have a strong name. This is true with a satellite assembly also. Here are the steps to create a strong named satellite assembly.

String Naming a Satellite Assembly


al /embed:ExploreDotNet2005.Strings.en-US.resources
   /out:ExploreDotNet2005.resources.dll /c:en-US
   /template:../ExploreDotNet2005.exe /keyfile:../../..

Remember that /template is very important because it inherits the parent assembly manifest, and the strong name key pair must be the same as that for the running assembly. I’ve tried using different strong names for satellite assembly and executing the assembly. but it throws an exception.


Posted in a Fundamentals.NET | Tagged: | Leave a Comment »