Assemblies

.Assemblies
Assembly Class | Assembly Signing | Assembly Deployment | Assembly Resources

"Assemblies are the basic unit of deployment, version control, reuse, activation scoping, and security permissions for a .NET-based application. Assemblies are the building blocks of the .NET Framework and take the form of an executable (.exe) or dynamic link library (.dll) file."

.Assembly

Windows has suffered from past application deployment problems. The old 16-bit versions of Windows did not support memory constraints, or separation of process memory. This allowed only one version of a particular dll to run at one time. Problems arose when incompatible versions of these dll's were shared between applications. It was possible to install a new application that would overwrite an existing dll. If the new dll was not compatible with all the already installed programs which used the shared dll, the old programs could break. Other problems with Windows also contributed to this "dependency hell" and were addressed in the .NET framework. .NET introduced the concept of an assembly which are self contained units which include all the information needed for execution. Assemblies are only loaded into memory if they are required making them an efficient way to manage resources in larger projects. You can programmatically obtain information about an assembly by using reflection. Despite the fact the .NET assemblies have the same file extensions (*.exe, *.dll) as previous Windows binaries, their structure and functionality are very different. A .NET assembly consists of the following components:

  1. Windows Header - contains information for Windows on how to load and execute the assembly.
  2. CLR Header - contains information for Common Language runtime on how to load and execute the assembly.
  3. CIL Code - Common Intermediate Language code is a platform agnostic intermediate language.
  4. Metadata for Types - contains metadata about types for use by CLR.
  5. Manifest - contains identity information for each module, permissions, list of all files in assembly, list of eternal reference files.
  6. Optional Resources - data embedded in dll (images, text, etc).
.Windows Header File
Windows Header (dumpbin /headers Assembly2.exe)


.CLR Header
CLR Header (dumpbin /clrheader Assembly2.exe)


.CIL Code
Common Intermediate Language Code (from ildasm.exe)


.Metadata
Metadata (from ildasm.exe(View, MetaInfo, Show))


.Manifest
Manifest (from ildasm.exe(View, MetaInfo, Show))


using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("AssemblyExample2")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AssemblyExample2")]
[assembly: AssemblyCopyright("Copyright ©  2014")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components.  If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("dcb764ee-5813-4add-bdd5-102bb11db02a")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Assembly Information in assembly.cs

Four pieces of information are used to identify an assembly:

  1. Simple Name - is from the name of the originally compiled file, minus the file extension. For example the simple name of System.Xml.dll is "System.dll".

  2. Version - in the format: major.minor.bulld.revision. Can be specified with the AssemblyVersion attribute [AssemblyVersion ("1.4.3.8")] or under the project's properties in Visual Studio.

  3. Culture - contains "neutral" if not a satellite assembly

  4. Public Key Token - is null if not strongly named. See more information about strong names under "Assembly Signing".



Assembly Modules - Cross Language Inheritance

Assemblies contain intermediate containers called modules. Typically there is one module (containing the manifest, IL Code, Metadata, and resources) in an assembly. However when building an assembly containing a mixture of programming languages, additional modules are required. Multi-file assemblies must be compiled from the command line as they are not supported in Visual Studio. Multi-file assemble enable such features as Cross-Language Inheritance, where a base class might be defined in C# and then a VB program derives objects from the C# base class.



Configuration Files - Controlling the Running of Apps

C# uses configuration files to control how an application runs on particular computers. There are machine configuration files, security configuration files, and application configuration files. The application configuration file app.config, is used to customize how the CLR locates and loads assemblies. By default, the runtime attempts to bind with the exact version of an assembly that the application was built with. This default behavior can be overridden by configuration file settings. More information about configuration files can be found at the Configuration Files site and instructions for setting up an app configuration file can be found at How to: Add an Application Configuration File to a C# Project.




Assembly Class

The Assembly class is located in System.Reflection and can be used to load assemblies, explore the metadata and parts of assemblies, discover the types contained in assemblies, and create instances of those types. There are four ways to obtain an assembly object:

  1. Assembly a = typeof (Program).Assembly - by using the type's Assembly property.
  2. GetExecutingAssembly - Gets the assembly that contains the code that is currently executing.
  3. GetCallingAssembly - Returns the Assembly of the method that invoked the currently executing method.
  4. GetEntryAssembly - Gets the assembly defining the applications's original entry method.

Another useful related class is the AssemblyName class which provides access to assembly information with the following properties and methods:

  1. GetPublicKey(), SetPublicKey() - get the public key used in signing the assembly.
  2. FullName - Fully Qualified Assembly Name
  3. Name - Simple Name of Assembly
  4. Version - Assembly Version
  5. CultureInfo - Culture information used with satellite assemblies
  6. CodeBase - Location of executables
Assembly Class Example

using System;
using System.Reflection;

namespace AssemblyExample
{
    class Program
    {
        static void Main()
        {
            Assembly myAssembly = Assembly.GetExecutingAssembly();
           
            AssemblyName assemName = myAssembly.GetName();
            Console.WriteLine("Name: {0}", assemName.Name);
            Console.WriteLine("Version: {0}.{1}\n",
                assemName.Version.Major, assemName.Version.Minor);

            Console.WriteLine("Location: {0}", myAssembly.CodeBase);           
        }
    }
}

Top



Assembly Signing

"Authenticode and strong names are two ways to sign an assembly and prevent tampering with an assembly. Beyond that, they differ in their purpose as Authenticode signing uniquely identifies the assembly publisher while strong names are used to uniquely identify an assembly."

Microsoft provides two ways to sign assemblies. The first signing method was Authenticode signing which used a digital certificate issued by a certificate authority. The strong name signing method was introduced in the .NET Framework. The two signing methods both provide a means to prevent tampering with an assembly through the use of symmetric encryption (public/private keys). Beyond that however, authenticode signatures and strong names were designed to solve different problems. A strong name was designed to uniquely identify an assembly, while an authenticode signature was designed to uniquely identify a code publisher. Microsoft discourages reliance on strong name signing as a replacement for Authenticode security.

Authenticodes are frequently used when downloading programs from the Internet as they prove the program came from the certified authority and were not tampered with in transit. Without the Authenticode, an "Unknown Publisher" warning is issued when you start the install process or run the application for the first time. Applying Authenticodes to assemblies is a requirement for the Windows Logo Program. Autheticodes can be applied to managed code, unmanaged code, ActiveX Controls and .msi deployment files. However, strong names can only be applied to managed code.

Methods for working with Authenticodes and strong names are different. To obtain a digital certificate to be used with Authenticode requires payment to a certification authority who will perform a background check on the publisher's identity before assigning a time-limited digital certificate. Strong name signing does not require any additional expense, nor does it have time limitations, as it is integrated into the .NET framework. Both signing methods can be used on an assembly, but when they are it is important to sign with the strong name first, otherwise the Authenticode signature will be invalidated when you sign with the strong name.

More comparisons between Strong Name Signing and Authenticode are given below:

Authenticode and Strong Name Comparison
Feature Strong Name Authenticode
Uniquely Identifies Assembly Publisher
Checks Assembly Integrity Yes Yes
Public Key Can be Revoked No Yes
Versioning Yes No
User Input for Trust Decisions No Yes
Uses Managed Code Only Unmanaged and Managed code, ActiveX Controls, .msi installers

.NET security can be configured so that unsigned assemblies are not permitted to run. Security can also be configured to allow unsigned assemblies to run, but only after the user responds to a message indicating the application was not signed". This security is very important when the programs are coming from unknown sources, such as the Internet. For the most part, determining whether to sign an assembly is up to the publisher. However there are some situations where an assembly must have a strong name. Such as when putting an assembly in the Global Assembly Cache (GAC). Also, strongly named assemblies are not able to reference weakly named assemblies.

Signing with a Strong Name

Signing an assembly with a strong name requires the generation of a public/private key pair. This can be done with the strong name utility as shown:

sn.exe -k TheKeyPair.snk

and then compiled with the progam using the /keyfile switch:

csc.exe /keyfile:TheKeyPair.snk Program.cs

or the key pair can be easily created using the project properties in Visual Studio:

.Visual Studio Key Generation
Generating a Key in Visual Studio for Strong Name Signing


Generating password protected key will create a key file with a .pfk extension, while a non-password protected key will be stored in a file with a .snk extension. You can verify an assembly has been signed by using the sn.exe utility:

sn.exe -v AssemblyExample2.exe

or you can use ildasm.exe and look in the manifest to see if ".publickey" contains a value (not to be confused with the .publickeytoken).

Keep the generated .snk or .pfk file in a safe place. If you lose the file you will no longer be able to sign the file assembly with the same identity. If the file is stolen then the integrity of the assembly could be compromised.

Delay Signing

Some large organizations keep control over strong name signing by taking the private key out of the key pair file and only using the file with both public and private keys during the production build. This technique is called Delay Signing and it allows developers to compile and test assemblies with their correct identity. The strong name utility can be used to extract just the public key into a .pk file:

sn.exe -p TheKeyPair.snk ThePublicKeyOnly.pk

then the file "TheKeyPair.snk" is kept in a secure location and the file "ThePublicKeyOnly.pk" is freely distributed.

Another method uses assembly attributes to delay sign the assembly. This is done by putting the following two attributes in the source code:

[assembly:AssemblyKeyFileAttribute("myKey.snk")][assembly:AssemblyDelaySignAttribute(true)]

More information on delay signing can be found at Delay Signing an Assembly

The delay signing creates a place in the assembly for the signature, but puts in a null value. When the assembly is signed prior to deployment it does not need to be recompiled as the null value is replaced with real signature. Assigning the real signature to a delay signed assembly can be accomplished with the strong name utility:

sn.exe -R  Program.exe TheKeyPair.snk



Top



Assembly Deployment

.Global Assembly Cache
Contents of GAC (gacutil -l)


There are two ways to deploy an application. The most common way is to deploy the application with a private assembly where all the assembly components are installed in one folder and is used exclusively by the application. In the few situations where an assembly needs to be shared by multiple applications, they are stored in a central repository known as the Global Assembly Cache (GAC). All assemblies deployed in the global assembly cache must have a strong name. Access to the GAC usually requires administrative permission. There are two ways to deploy an assembly to the GAC:

  1. Windows Installer 2.0 - Windows OS tool (Production).

  2. gacutil.exe - the GAC tool from the SDK (Development).
(gacutil -l) - list assemblies in GAC
(gacutil -i) - install an assembly in GAC
(gacutil -u) - remove an assembly from GAC

A major reason to consider using the GAC is when there are multiple copies of a shared assembly which need to be machine-centralized. For example if an app and a plug-in each require a different version of an assembly, it become difficult to coordinate as the host will typically only want to load one version of the assembly at a time. If both assemblies are put into the GAC it will control allocating the correct version of the assembly. The GAC ensures the CLR always makes straightforward and consistent assembly resolution choices. Also, this side-by-side execution prevents the problem of breaking the app when a shared assembly is updated for another app.

Top




Assembly Resources

External resources, such as images, text, and XML can be provided to assemblies through resource files. Resource files are ultimately named byte streams. Resources files can externally accompany assembly files or they can be embedded within the assembly files. When the resource files are outside the assembly, they load on demand and result in faster initial program load time, but will take longer to obtain the resource when it is requested. Additionally, external resource files may not require the application to be recompiled when the resource changes. In Visual Studio you configure where a resource is to be external or internal by setting the build action value:

  1. Resource - embed the resource file in the assembly.
  2. Content - include the resource file in the application package without embedding into the assembly.
  3. None - do not include the resource file in the application nor embed it in the assembly.

Resource files have different formats:

  1. .res - contains binary data such as images, audio, and video. Only .res files should be embedded in a runtime executable or compiled into a satellite assembly.

  2. .restext - is the same format as .txt and can only contain text.

  3. .resx- consists of XML entries which specify binary data and text inside XML tags. Aside from the binary information, a .resx is readable and maintainable with a text editor.

Resgen.exe (Resource File Generator) is an SDK utility that converts resource files between the different formats. Culture data is stored in .resx files and then the resource manager provides access to the culture-specific resources at runtime. When accessing culture data the fall-back culture is put in the main assembly and the other culture data is put into Satellite Assemblies. Resource files are not secure and should not be used to store passwords or sensitive data.

Top



Reference Articles

Top