Friday, December 26, 2003

Assemblies and Metadata 2

I made an error regarding modules. I said they only contain metadata and IL code and NO manifest, but it seems that they do contain a manifest. But I'm sure they can't be executed by the CLR unless referenced by some assembly.

Anyway, I had included code last time, but no real representation of the manifest and metadata. There is a utility application that ships with the Framework SDK called ildasm which stands for "Intermediate Language Disassembler". I suppose the name is a bit misleading since the app allows you to disassemble the manifest and metadata in addition to the IL.

So here it is...


// Album.cs

namespace Albums
{
   public interface IAlbum
   {
      // Properties

      string Name
      {
            get; // Read only
      }

      int NumberOfSongs
      {
            get; // Read only
      }



      // Methods

      string BestSong();
   }
}

Here's Album.dll's metadata...

In the metadata treeview you will notice the "Albums" namespace at the top. Under that is the one interface "IAlbum" we defined. Under that are the properties and methods the interface defines.

And here's its manifest...

The manifest lists all the assemblies it references starting with the external ones. "mscorlib"
is the System assembly that defines all the basic types that ALL .NET applications require and is implicitly referenced. I guess this is similar to java.lang.

It also lists the publictoken and the version number of the assembly. "mscorlib" is a shared assembly in that ALL .NET apps refer to this ONE assembly. All shared assemblies require a public token to uniquely identify them along with the version number. Next comes this assembly - "Album". Notice no "extern" keyword. Since this is NOT a shared assembly, there is no public token associated with it. There is a hash and a version number (which we did not define).

All the other stuff is used by the CLR at runtime.


// DM.cs

namespace Albums
{
   public class DM : IAlbum
   {
      // Fields

      private string name;

      private int numberOfSongs;



      // Properties

      public string Name
      {
         get
         {
            return this.name;
         }
      }

      public int NumberOfSongs
      {
         get
         {
            return this.numberOfSongs;
         }
      }



      // Methods

      public DM()
      {
         this.name = "Definitely Maybe";

         this.numberOfSongs = 11;
      }

      public string BestSong()
      {
         return "Live Forever";
      }
   }
}

Here's DM.netmodule's metadata...

The metadata is very straightforward. There's the "Albums" namespace followed by the class "DM" followed by all its members.

And here's its manifest...

The manifest is similar to Album.dll's manifest. Here, in addition to referencing "mscorlib", it also has a reference to "Album.dll" since it uses the type "IAlbum" in its code. But notice there is no entry in the manifest for an assembly called "DM". This is because it is a module.

Here's DM's constructor's IL code...

All the numbers in between /* */ are tokens defined in member tables of the metadata. As I said before, they act as pointers. The CLR will look at this and index into the tables to get the method defs etc... The IL is a stack based implementation. This code is fairly straightforward but it gets more complicated.

WTSMG.netmodule is essentially the same.

We created "Albums.dll" by just referencing the two modules (DM and WTSMG)...

csc /target:library /addmodule:DM.netmodule;WTSMG.netmodule /out:Albums.dll

Here is Albums.dll's metadata...

Notice there really isn't any metadata there. "Albums.dll" itself didn't define any types itself.
It is a multi file assembly that references the two .netmodules.

Here is its manifest...

Here we have it referencing "mscorlib", "Album" and two external files "DM.netmodule"
along with "WTSMG.netmodule".

// Oasis.cs

using Albums;

namespace Bands
{
   public class Oasis
   {
      // Fields

      private IAlbum firstAlbum;

      private IAlbum secondAlbum;


      // Properties

      public bool IsBestBandEver
      {
         get
         {
            return true;
         }
      }

      public IAlbum FirstAlbum
      {
         get
         {
            return this.firstAlbum;
         }
      }

      public IAlbum SecondAlbum
      {
         get
         {
            return this.secondAlbum;
         }
      }


      // Methods

      public Oasis()
      {
         this.firstAlbum = new DM();

         this.secondAlbum = new WTSMG();
      }
   }
}

Oasis.dll Metadata...

The manifest is similar to the others.

Here's some property IL code... Pretty straightforward.


// App.cs

using System;
using Bands;
using Albums;

namespace Assemblies
{
   public class App
   {
      public static void Main()
      {
         Oasis o = new Oasis();

         Console.WriteLine();
         Console.WriteLine( "Is Best Band Ever? {0}", o.IsBestBandEver );
         Console.WriteLine();

         IAlbum album;

         album = o.FirstAlbum;

         Console.WriteLine( "First Album:" );
         Console.WriteLine( " Name: {0}", album.Name );
         Console.WriteLine( " Number of Songs: {0}", album.NumberOfSongs );
         Console.WriteLine( " Best Song: {0}", album.BestSong() );
         Console.WriteLine();

         album = o.SecondAlbum;

         Console.WriteLine( "Second Album:" );
         Console.WriteLine( " Name: {0}", album.Name );
         Console.WriteLine( " Number of Songs: {0}", album.NumberOfSongs );
         Console.WriteLine( " Best Song: {0}", album.BestSong() );
      }
   }
}

App metadata...

Just two methods in there... constructor and Main.

App manifest...

Same as others.

Part of IL code for App's Main method

Notice the ".entrypoint" directive in there. The CLR looks for this and will execute from this
point.

So hope this provides a better (graphical) view of the metadata and what the manifest contains.

No comments: