Web Design that taps into the haromny and vision of your dreams.

Assemblies in C#

By on in Coding

2,025 words, estimated reading time 10 minutes.

Introduction to Programming with C# Series
  1. Introduction to Programming with C# 7
  2. C# Programming Fundamentals
  3. Introduction to Object-Oriented Programming
  4. C# Object-Oriented Programming Part 2
  5. Flow Control and Control Structures in C#
  6. C# Data Types, Variables and Casting
  7. C# Collection Types (Array, List, Dictionary, Hash Table)
  8. C# Operators
  9. Using Data in C# 7 with ADO.Net & Entity Framework
  10. LINQ: .NET Language Integrated Query
  11. Error and Exception Handling in C#
  12. Advanced C# Programming Topics
  13. Reflection in C#
  14. What Are ASP.Net Webforms
  15. Introduction to ASP.Net MVC
  16. Windows Application Development
  17. Assemblies in C#
  18. Working with Resources Files, Culture & Regions
  19. Regular Expressions in C#
  20. Introduction to XML with C#
  21. Complete Guide to File Handling in C#

Assemblies in the .Net platform is a solution to a problem that has been around as long as libraries. The problem, known as "DLL Hell", can be caused by one application overwriting a shared library of another application, usually with a different version.

Imagine you write an application with a shared library and then years later create a new application with an updated library. In order not to break old programs, what do you do with the new library? You could give it a different name, store it in a different location, or overwrite the old ones?

Problems can also arise when a DLL is registered on a computer and for some reason, the information is corrupted or modified so that the reference no longer links to the library. Registering a DLL in the system registry stores information about that library in such a way that other applications can access the data from a central store.

.Net assemblies overcome these issues by storing information about themselves, rather than having that information stored in a central location. The assembly will hold information about itself such as version and vendor.

Microsoft has defined an assembly and its role in .NET as:

In the .NET Framework an assembly is a physical unit that can be executed, deployed, versioned, and secured. All .NET Framework applications contain one or more assemblies

Versioning

The platform will assist you by providing a number of features to ensure proper versioning between components in an application:

  • Enforce versioning rules so that an application that needs version 1.1.1.1 will not end up getting version 1.0.0.0.
  • Shared assemblies are signed with a strong name (which includes a public key, a simple name, a version, and a culture) to make them unique. Assemblies that are not shared do not require strong names.

The information that the assembly holds about itself is called the metadata which is stored in the manifest. The manifest is like the unique fingerprint of the assembly.

Assembly Deployment

Assemblies can be deployed in a variety of methods, including direct file copy (xcopy), Windows Installer setup projects and Merge Modules setup project.

To directly copy an assembly all you need to do is copy the file to the applications executable or bin folder and it is available to use, however, this can only be done with a private assembly. Shared assemblies need to be installed to the machine's Global Assembly Cache (GAC) using the gacutil tool or by copying the assembly to the GAC folder within Windows Explorer. Installation in the GAC results in a uniquely named folder being created and the assembly files being copied into that folder.

Windows installer and merge module projects will perform the installation for the user and are the recommended method for deployment.

What Is in the Assembly?

The assembly contains the intermediate code, resources, and the metadata for itself. We can have a look inside the assembly using the ildasm (Intermediate Language Disassembler) tool that comes as part of Visual Studio. To access it you need to open the Visual Studio Command Prompt and type ildasm.exe. This will launch a Windows application that you can use to explore any .Net application.

Once launched, you can open any .Net application and view information within the file, including the manifest, types, classes, namespaces, methods and source code.

Visual Studio provides easy methods for creating and using a .Net assembly. In this tutorial, we look at creating an assembly and how we can use different methods for incorporating it into our projects.

Creating a Private Assembly

We shall first start off with a blank Visual Studio C# solution. Add a new class library to the solution then build and run it. You will notice that although the project built successfully, you cannot run the application. This is because a class library does not have any entry points, unlike a console application. Class libraries are built into assemblies which merely hold classes, code and resources.

In the class library add a method called TestMethod that will show a message box saying "Hello World!". In this example, I have changed the default namespace and class name to better reflect the purpose of the assembly.

using System;
using System.Text;
using System.Windows.Forms;
 
namespace TestClassLibrary
{
  public static class TestClass
  {
    public static void TestMethod()
    {
      MessageBox.Show("Hello World");
    }
  }
}

If you look inside the bin/Debug folder for the project you will see a .dll file instead of the usual .exe. This is our new assembly.

In order to test our assembly, we need to create a project that is capable of running, for example, a console application, windows application or asp.net web application. Class libraries can also be used by other class libraries in exactly the same method as I describe below, but then we still need to create a console application to call that class library!

Add a console application project to the current solution and set it as the default start-up project. This will cause the console application to run when we click on play or launch the project. Let's try and use the method we just created:

using System;
using System.Text;
 
namespace ConsoleApplication1
{
  class Program
  {
    static void Main(string[] args)
    {
      TestClassLibrary.TestClass.TestMethod();
    }
  }
}

When we try and build the application you will notice that it failed with a message stating that

"The type or namespace name could not be found (are you missing a directive or an assembly reference?)"

We are seeing this message because we haven't told the console application about the class library (even though it is part of the same solution). To do this we need to add a reference to the class library. This can be done by right-clicking on the console application in solution explorer and selecting "Add Reference".

Add reference to projects
Add reference to projects

From this box, you can select an existing.Net assembly or browse to a file or project. Click the Projects tab and select our class library.

Finally, we will need to add the namespace to the using statements or get refactor to do it for us.

using System;
using System.Collections.Generic;
using System.Text;
using TestClassLibrary;
 
namespace ConsoleApplication1
{
  class Program
  {
    static void Main(string[] args)
    {
      TestClass.TestMethod();
    }
  }
}
 

Now when you build the project it should compile and run properly.

Using third-party Assemblies

To use a third party assembly you can use the same method as above, simply add a reference to the assembly and add in a using clause (or explicitly reference classes and data types).

Creating a Shared Assembly

The procedure for creating a shared assembly is the same as for a private assembly, except that there are a few extra steps involved before you can use it. All shared assemblies need to be strongly named before they can be installed into the global assembly cache. Strongly-named assemblies ensure that the assembly has a unique manifest consisting of versioning information and public key.

Satellite Assemblies

If you plan on localising your application (making your application customisable for different languages and cultures) you should use satellite assemblies and neutral code.

Neutral code does not contain any hard coding or language/culture specific code. For example, there is no hardcoded strings or symbols, no hardcoded date formatting and no region specific calculations e.g. VAT.

When creating a satellite assembly you should work with the naming convention suggested by Microsoft, The format of which is ..resource where the culture identifier consists of ISO language and culture strings. You can find a list of culture codes in this list List of .Net Culture Codes.

Satellite assemblies use a hub and spoke design, with your neutral code at the centre (hub) and various culture-specific satellite assemblies plugging into it (spokes). As a result, the runtime requires that you place resources in specific locations so that they can be easily located and used. 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. The compiler will first look in the global assembly cache for any localised satellite assemblies if it does not find any it will look in the following locations:

  1. The global assembly cache for an assembly matching the requested culture for the application
  2. The directory of the currently executing assembly for a directory matching the requested culture.
  3. The global assembly cache again, this time for the parent assembly of the requested resource.
  4. The runtime next checks the directory of the currently executing assembly to see if it contains a parent directory. If a parent directory exists, the runtime searches the directory for a valid satellite assembly for the parent culture.
  5. The runtime next searches parent assemblies, as in the previous step, through many potential levels.
  6. If a resource is not found then the default culture is used.

Creating Satellite Assemblies

By definition, satellite assemblies can only contain resources. They cannot contain any executable code. Satellite assemblies are compiled from resource files which can be added to a project from the add new item menu.

Resource files can be directly compiled into the executable, however, for the purposes of scalability, a satellite assembly allows new cultures and languages to be added without recompilation or distribution. Satellite assemblies can be used so that client downloads and installs a common executable and the just downloads and installs one satellite assembly for their location.

Satellite assemblies are created using the Assembly Linker (al.exe) command line tool.

Use the following command to create a satellite assembly for the application TestApp from the file strings.ja-JP.resources

static ResourceManager rm = new ResourceManager("strings", Assembly.GetExecutingAssembly());

This line of code will load the strings resource file from the assembly created above, assuming that ja-JP is the current system locale.

You can access or modify the current culture using the CultureInfo class:

CultureInfo ci = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentUICulture = en.US;

Creating Strong Named Assemblies in Microsoft .Net

Strong Names are a way of uniquely identifying assemblies written for the .Net platform. They are not, as commonly believed, a tool for enabling security.

Strong Names use cryptographic techniques (RSA, El Gamal etc...) together with a hashing algorithm (Md5, SHA, Blowfish etc...) to create a digital signature of an assembly. A strong name also consists of a public / private key pair used to encrypt/decrypt the assembly.

Strong Names allow assemblies to be versioned and authenticated. Signed Assemblies allow the assembly to have a unique identifier which allows multiple versions of an assembly to existing on a machine without colliding.

If you are developing assemblies that will be installed into the Global Assembly Cache (GAC) then the assembly must be strongly named.

The other benefit of creating strongly named assemblies is that the assembly can be authenticated. Because the strong name contains the private key of the author, you can be sure that the assembly was created by a particular author. A strongly named assembly can only reference other strongly named assemblies to ensure integrity.

Signing an Assembly

The first thing that you need to do is create a public/private key pair that will be used to encrypt the assembly.

To create a key/pair you can use the strong name utility:

sn -k <<em>file name</em>>

This will create the file containing the keys defaulting to RSA encryption. You should keep this file safe as you will need it for signing future assemblies if you wish to keep them authenticated.

Now, there are a few different methods for strongly naming an assembly, either from within Visual Studio or through the command line.

If you wish to give developers access to the private key you can simply add an attribute to the project. This code should go within the AssemblyInfo.cs file.

[assembly: AssemblyDelaySign(false)] 
[assembly: AssemblyKeyFile("c\:mykey.sn")]

If you do not wish to give your developers access to your private key you can delay signing the assembly.

[assembly: AssemblyDelaySign(true)] 
[assembly: AssemblyKeyFile("c:\mykey.sn")]

Then the assembly must have verification turned off otherwise the assembly will not load on the developer's machine:

c\:> sn -Vr myassembly.dll

Finally, when the code is ready to be released you can fully sign the assembly using the sn.exe tool.

c\:> sn -R myassembly.dll mykey.sn

Last updated on: Thursday 11th October 2018

 

Comments

Have a question or suggestion? Please leave a comment to start the discussion.

 

Leave a Reply

Please keep in mind that all comments are moderated according to our privacy policy, and all links are nofollow. Do NOT use keywords in the name field. Let's have a personal and meaningful conversation.

Your email address will not be published.