Assembly Structure

 
Chapter 8 - Assemblies
bySimon Robinsonet al.
Wrox Press 2002
  

An assembly consists of assembly metadata describing the complete assembly, type metadata describing the exported types and methods , MSIL code, and resources. All these parts can be inside one file or spread across several files.

In this example represented in the figure on the right, the assembly metadata, type metadata, MSIL Code, and resources are all in one file Component.dll . The assembly consists of a single file.

The second example below shows a single assembly spread across three files. Component.dll has assembly metadata, type metadata, and MSIL code, but no resources. The assembly uses a picture from picture.jpeg that is not embedded inside Component.dll , but is referenced from within the assembly metadata. The assembly metadata also references a module called util.netmodule , which itself includes only type metadata and MSIL code for a class. A module has no assembly metadata. Thus the module itself has no version information; it also cannot be installed separately. All three files in this example make up a single assembly. The assembly is the installation unit. It would also be possible to put the manifest in a different file:

click to expand

Assembly Manifests

An important part of an assembly is a manifest , which is part of the metadata. It describes the assembly with all the information that's needed to reference it, and lists all its dependencies. The parts of the manifest are:

  • Identity ( name , version, culture, and public key. )

  • A list of files belonging to this assembly. A single assembly must have at least one file, but may contain a number of files.

  • A list of referenced assemblies . Documented inside the manifest are all assemblies that are used from the assembly, including the version number, and the public key. The public key is used to uniquely identify assemblies. We will disuss the public key later.

  • A set of permission requests . These are the permissions needed to run this assembly. We will not talk about permissions in this chapter. More information can be found in Chapter 23.

  • Exported types are not part of the manifest, unless the types are included from a module. A module is a unit of reuse. The type description is stored as metadata inside the assembly. We can get the structures and classes with the properties and methods from the metadata. This replaces the type library that was used with COM to describe the types. For the use of COM clients it's easy to generate a type-library out of the manifest. The reflection mechanism uses the information about the exported types for late binding to classes. See Chapter 5 for more about reflection.

Namespaces, Assemblies, and Components

Maybe you're now confused by the meanings of namespaces, types, assemblies, and components. How does a namespace fit into the assembly concept? The namespace is completely independent of an assembly. You can have different namespaces in a single assembly, but the same namespace can be spread across assemblies. The namespace is just an extension of the type name it belongs to the name of the type. Thus, the real name of the class Class1 we used before is Wrox.ProCSharp.Assemblies.AppDomains.Class1.

The following diagram should help to make this concept clearer. It shows three assemblies, which we will build later in this chapter an assembly written with Managed C++, one with Visual Basic .NET, and one with C#. All these assemblies have classes in the same namespace: Wrox.ProCSharp.Assemblies.CrossLanguage . The assembly HelloCSharp in addition, has a class Math that's in the namespace Wrox.Utils .

click to expand

Now another question often arises: what is a component in the .NET language is an assembly a component? The answer is "no". A component is the binary form of a class. A single assembly can have a lot of components.

Private and Shared Assemblies

Assemblies can be shared or private. A private assembly is found either in the same directory as the application, or within one of its subdirectories. With a private assembly, it's not necessary to think about naming conflicts with other classes or versioning problems. The assemblies that are referenced during the build process are copied to the application directory. Private assemblies are the normal way to build assemblies, especially when applications and components are built within the same company.

When using shared assemblies , we have to be aware of some rules. The assembly must be unique, and therefore have a unique name called a strong name . Part of the strong name is a mandatory version number. Shared assemblies will mostly be used when a vendor, different from that of the application, builds the component, or where a large application is split into sub-projects.

Viewing Assemblies

Assemblies can be viewed using the command-line utility ildasm , the MSIL disassembler. An assembly can be opened by starting ildasm from the command line, with the assembly as argument or by selecting the File Open menu.

The screenshot overleaf shows ildasm opening the example that we are about to build, HelloCSharp.exe . ildasm shows the manifest, and the HelloCSharp type in the Wrox.ProCSharp.Assemblies.CrossLanguage namespace. Opening the manifest, we can see the version number, and the assembly attributes as well as the referenced assemblies and their versions. Opening the methods of the class, we can see the MSIL code:

click to expand

ildasm Symbols

The symbols that are used with ildasm are listed here:

Symbol

Description

Represents a namespace.

Represents a reference type, a class. Similar symbols are used by value types (structs) that have a light color , delegates that are real classes with the MSIL code, interfaces that have an "I" in the graphic, and enumerations with an "E".

Represents a method and get and set accessors of a property; an "S" in the graphic means that this method is static.

Represents a field

Represents an event.

Represents a property.

This means that more information is available, for example. manifest information, or information about a class declaration.

Building Assemblies

Now we have learned what assemblies are, we are going to build some. Of course, we have already built assemblies as we have progressed through the book, because a .NET executable is an assembly anyway, but now we will have a look at special options for assemblies.

Creating Modules and Assemblies

All C# project types in Visual Studio .NET create an assembly. Whether you choose a DLL or EXE project type, an assembly is always created. With the command-line C# compiler csc , it's also possible to create modules. A module is a DLL without assembly attributes (so it's not an assembly, but it can be added to assemblies at a later time). The command

  csc /target:module hello.cs  

creates a module hello.netmodule . It's possible to view this module using ildasm .

A module also has a manifest, but there is no .assembly entry inside the manifest (except for the external assemblies that are referenced), since a module has no assembly attributes. It's not possible to configure versions or permissions with modules; that's only possible at the assembly scope. In the manifest of the module, references to assemblies can be found. With the /addmodule option of csc , it's possible to add modules to existing assemblies.

To compare modules to assemblies, I'm generating a simple class A and compiling it using:

  csc /target:module A.cs  

The compiler generates the file A.netmodule , which doesn't include assembly information (as we can see using ildasm looking at the manifest information). The manifest of the module shows the referenced assembly mscorlib and the .module entry:

click to expand

Next, I'm generating an assembly B that includes the module A.netmodule . It's not necessary to have a source file to generate this assembly. The command to build the assembly is:

  csc /target:library /addmodule:A.netmodule /out:B.dll  

When looking at the assembly using ildasm , only a manifest can be found. In the manifest, the assembly mscorlib is referenced. Next we see the assembly section with a hash algorithm and the version. The number of the algorithm defines the type of the algorithm that was used to create the hash code of the assembly. When creating an assembly programmatically it is possible to select the algorithm. Part of the manifest is a list of all modules belonging to the assembly. Here we see .module A.netmodule that belongs to the assembly. Classes exported from modules are part of the assembly manifest; classes exported from the assembly itself are not:

click to expand

What's the purpose of modules? Modules can be used for faster startup of assemblies because not all types are inside a single file. The modules are only loaded when needed. Another reason to use modules is if you want to create an assembly with more than one programming language; one module could be written using VB.NET, another module using C#, and these two modules can be included in a single assembly.

Creating Assemblies Using Visual Studio .NET

As already mentioned, all project types in Visual Studio .NET create assemblies. With Visual Studio .NET 7.0 there's no support for creating modules directly.

When creating a Visual Studio .NET project, the source file AssemblyInfo.cs is generated automatically. We can use using the normal sourcecode editor to configure the assembly attributes in this file. This is the file generated from the wizard:

   using System.Reflection;     using System.Runtime.CompilerServices;     //     // 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("")]     [assembly: AssemblyDescription("")]     [assembly: AssemblyConfiguration("")]     [assembly: AssemblyCompany("")]     [assembly: AssemblyProduct("")]     [assembly: AssemblyCopyright("")]     [assembly: AssemblyTrademark("")]     [assembly: AssemblyCulture("")]     //     // 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 Revision and Build     // Numbers by using the '*' as shown below:     [assembly: AssemblyVersion("1.0.*")]     //     // In order to sign your assembly you must specify a key to use. Refer to     // the Microsoft .NET Framework documentation for more information on     // assembly signing.     //     // Use the attributes below to control which key is used for signing.     //     // Notes:     //   (*) If no key is specified - the assembly cannot be signed.     //   (*) KeyName refers to a key that has been installed in the Crypto     //       Service Provider (CSP) on your machine.     //   (*) If the key file and a key name attributes are both specified, the     //       following processing occurs:     //       (1) If the KeyName can be found in the CSP - that key is used.     //       (2) If the KeyName does not exist and the KeyFile does exist, the     //           key in the file is installed into the CSP and used.     //   (*) Delay Signing is an advanced option - see the Microsoft .NET     //       Framework documentation for more information on this.     //     [assembly: AssemblyDelaySign(false)]     [assembly: AssemblyKeyFile("")]     [assembly: AssemblyKeyName("")]   

This file is used for configuration of the assembly manifest. The compiler reads the assembly attributes to inject the specific information into the manifest.

[assembly] , and [module] are global attributes. Global attributes are, in contrast to the other attributes, not attached to a specific language element. The arguments that can be used for the assembly attribute are classes of the namespaces System.Reflection , System.Runtime.CompilerServices , and System.Runtime.InteropServices .

You can read more about attributes and how to create custom attributes in Chapter 4.

Here's a list of all assembly attributes corresponding to classes in the System.Reflection namespace.

Assembly Attribute

Description

AssemblyCompany

Specifies the company name.

AssemblyConfiguration

Specifies build information such as retail or debugging information.

AssemblyCopyright and AssemblyTrademark

Hold the copyright and trademark information.

AssemblyDefaultAlias

Can be used if the assembly name is not easily readable (such as a GUID when the assembly name is created dynamically). With this attribute an alias name can be specified.

AssemblyDescription

Describes the assembly or the product. Looking at the properties of the executable file this value shows up as Comments.

AssemblyProduct

Specifies the name of the product where the assembly belongs.

AssemblyInformationalVersion

This attribute isn't used for version checking when assemblies are referenced, it is for information only. It is very useful to specify the version of an application that uses multiple assemblies. Opening the properties of the executable we can see this value as the Product Version.

AssemblyTitle

Used to give the assembly a friendly name. The friendly name can include spaces. With the file properties we can see this value as Description.

Here's an example of how these attributes might be configured:

   [assembly: AssemblyTitle("Professional C#")]     [assembly: AssemblyDescription("")]     [assembly: AssemblyConfiguration("Retail version")]     [assembly: AssemblyCompany("Wrox Press")]     [assembly: AssemblyProduct("Wrox Professional Series")]     [assembly: AssemblyCopyright("Copyright (C) Wrox Press 2002")]     [assembly: AssemblyTrademark("Wrox is a registered trademark of Wrox Press Ltd")]     [assembly: AssemblyCulture("en-US")]   

The following attributes correspond to classes in the System.Runtime.CompilerServices namespace:

  • AssemblyCulture tells about the culture of the assembly. We will talk about the culture when covering localization.

  • AssemblyDelaySign , AssemblyKeyFile , and AssemblyKeyName are used to create strong names for shared assemblies.

  • AssemblyVersion specifies the version number of the assembly. Versioning plays an important part for shared assemblies.

    Additional COM interoperability attributes within the System.Runtime.InteropServices namespace can be used to make .NET types visible to COM, to specify application-ID's for example. COM interoperability is the subject of Chapter 17.

  


Professional C#. 2nd Edition
Performance Consulting: A Practical Guide for HR and Learning Professionals
ISBN: 1576754359
EAN: 2147483647
Year: 2002
Pages: 244

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net