Anatomy of an Assembly

Figure 3.1 shows the major parts of an assembly.

Figure 3.1. Parts of an assembly.


Let's look at each of these parts in detail.

Modules

Technically, an assembly is not limited to just one PE file; it may contain more than one PE file, each referred to as a module.

A module is uniquely identified by a GUID called the module version ID (MVID). This extra level of indirection makes it possible to change the module filename while keeping a record of the original filename. The MVID is automatically generated by the compiler.

It should be noted that the most common case for an assembly is to contain just a single module. In this case, the module and the assembly are one and the same.

Building multimodule assemblies is covered under advanced topics later in the chapter.

Metadata

Consider the following simple Hello User application, taken from the previous chapter:

// Project HelloUser

class MyApp
{
    public static void Main() {
      System.String userName = "Jay";
      System.Console.WriteLine("Hello " + userName);
    }
}

When this code is compiled, how does the compiler know that there exist classes called System.String and System.Console, and that System.Console provides a static method called WriteLine that takes a System.String type parameter?

Recall from the previous chapter that we had to reference assembly MSCorLib.dll while compiling this application. For your convenience, the command line is shown here once again:

csc.exe -t:exe -out:HelloUser.exe -r:MSCorLib.dll HelloUser.cs

As you might have guessed, the information on the available classes and methods is coming from MSCorLib.dll.

When a .NET compiler processes the source code and generates the module, not only does it generate the MSIL code, but it also generates the information about every type the source code contains. This information is referred to as the metadata (data about data). The metadata is necessary for the common language runtime to provide managed services to the code while it executes.

The metadata is stored in a binary form within the module. All .NET-compliant compilers are required to generate full metadata information about every class type in the compiled source code file. Among other things, this metadata contains a declaration for each type, the base class and the interfaces the type inherits from, the name and type (methods, properties, events, and fields) of all its members, and the signature of each of the methods.

Armed with the metadata information, the compiler can now verify if a class being accessed in the source code is present in the referenced assemblies and whether or not the parameters being passed to a method match the signature of the method.

Metadata versus Type Libraries

Readers who are familiar with COM may notice the conceptual similarity between the metadata and COM technologies such as type-libraries and IDL files. The important thing to note is that the metadata is far more complete and is always embedded in the same executable as the code. A type library, for example, need not always be embedded with the executable.


The fact that the metadata is embedded in the assembly makes for another interesting possibility: A client can explicitly load an assembly during runtime, instantiate a class stored in the assembly, and invoke a method on the instance. Such late-bound method invocation capability is provided by a .NET Framework subsystem called Reflection. We will see some examples of Reflection under advanced topics later in the chapter. Java language users may be aware that Java provides a similar functionality.

Viewing the Metadata

The .NET Framework provides a tool called the IL Disassembler (ildasm.exe) to disassemble a .NET module. This disassembler not only shows the MSIL code in the module but also lets you examine the embedded metadata.

Checking if a File Is a .NET Assembly

Here is a quick way to check if an EXE or DLL file is an assembly. Run ildasm.exe on the file. If the file is not a valid assembly file, ildasm.exe displays an error message.


Let's run the IL Disassembler on HelloUser.exe. Here is the command line:

ildasm.exe HelloUser.exe

Here is the partial output from ildasm.exe:

.assembly extern mscorlib
{
   .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
   .ver 1:0:3300:0
}

.module HelloUser.exe
// MVID: {90A8865C-4C5F-477D-A8CA-4D45A1CE9B65}

.class private auto ansi beforefieldinit MyApp
        extends [mscorlib]System.Object
{
   .method public hidebysig static void  Main() cil managed
   {
        ...
   } // end of method MyApp::Main

   .method public hidebysig specialname rtspecialname
            instance void  .ctor() cil managed
   {
        ...
   } // end of method MyApp::.ctor
} // end of class MyApp

As can be seen, HelloUser.exe implements MyApp, a private class that is inherited from System.Object, a class defined in the BCL. Recall from the previous chapter that every type under .NET is ultimately derived from System.Object (we cover System.Object in more detail in the next chapter). Furthermore, MyApp defines a public method Main and a public class constructor (ctor). The signature of the method Main indicates that it takes no parameters and has a void return type.

Class MyApp is marked with attributes such as auto, ansi, beforefieldinit, and so on. The SDK documentation explains these attributes under TypeAttributes enumeration. Likewise, method attributes such as hidebysig, specialname, and so on, can be found under MethodAttributes enumeration in the SDK documentation. It is not important to fully understand these attributes. We discuss them only on a need-to-know basis.

You may be wondering where the constructor code came from, even though MyApp doesn't define one. It just so happens that the C# compiler generates a default constructor if one is not explicitly defined. You can easily guess that the default constructor simply invokes the constructor for the base class, System.Object.

You can also see from the output that HelloUser.exe references an assembly named mscorlib version 1.0.3300.0. As an exercise, you can run ildasm on MsCorLib.dll to verify that there exist classes called System.String and System.Console, and that System.Console provides a static method called WriteLine that takes a System.String type parameter.

Disassembler for Advanced Users

The IL Disassembler provides an undocumented command-line switch -adv that makes it run in an advanced mode. In this mode, the Disassembler provides some additional metadata information about the disassembled file. For more information, type ildasm.exe -adv -? on the command line.


At this point, it is worth mentioning that a type is implicitly identified by the assembly it is defined in. For example, if you define a public class Foo in two different assemblies, they are treated as two different types, even though they share the same name. Hopefully, by scoping them in a different namespace, you can avoid any naming conflicts.

A namespace, on the other hand, can span multiple assemblies. However, it is important to keep in mind that if two assemblies define a type with the same name and the same namespace, the runtime still treats them as two different types. A type is always scoped within the assembly it is defined in.

Extending the Metadata—Attributes

An important aspect of the metadata under .NET is that it is fully extensible. Developers can add extra information on various parts of the code using attributes. Attributes can be applied to classes, structs, events, delegates, member fields, constructors, methods, method parameters, return values, and even assemblies. Unlike comments, which get stripped off when the code is compiled, compilers store the attributes as part of the metadata. They can later be examined either by the common language runtime or whoever else is interested, using Reflection API.

Attributes provide a flexible way to extend the behavior of a program entity. Earlier, we saw how attributes can be used to specify the version number on an assembly. As we go through the rest of the book, you will see that attribute-based programming is quite pervasive under the .NET Framework. The usefulness of attributes is limited only by your imagination.

Manifest

The manifest is a part of the assembly that stores some record-keeping information pertaining to the module. This information includes, among other things, the MVID and the list of referenced assemblies.

Assembly Paths

Note that the manifest does not store the file path of the referenced assemblies, just their display names. It is the responsibility of the assembly resolver to locate the referenced assemblies when the application is run.


Technically, the manifest is a part of the metadata. However, metadata generally is used to refer to type metadata, not to manifest metadata.

It is relevant to understand the distinction between the assembly manifest and the module manifest. Each module within an assembly contains the manifest. However, there is one module within the assembly with a manifest that contains some extra information pertaining to the assembly as a whole. The extra information includes, among other things, the identity of the assembly and the list of files that constitute the assembly. The module is called the prime module and the manifest it stores is referred to as the assembly manifest. In general, unless explicitly stated, a manifest refers to the assembly manifest.

Resources

Generally, it is not a good idea to hard-code strings in the source code. Storing strings in a separate file makes it easy to modify the strings without modifying the source code.

The .NET Framework makes it possible to embed resources such as strings and images stored in external files into an assembly. The resources can then be loaded programmatically using a BCL class ResourceManager (namespace System.Resources).

It should be noted that when dealing with localized resources (i.e., resources dealing with a specific culture), it is better to store them in a satellite assembly. We look at storing resources in an assembly as well as creating satellite assemblies later in the chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.6.114