Reflection (part 6, professional), Emit

Reflection can discover information about objects at runtime and execute against those objects. The namespace System.Reflection.Emit also allows you to build assemblies and create types at runtime. Only a few programmers will come across Emit. You have to be a kind of Rambo sewing yourself while fighting … and definitely not Alice in Wonderland.

Emit generates IL code in memory. The source code for such is quite complex and difficult. We are nearly back to Assembler style. And if you remember well, crashes and difficult debugging were the order of the day. There are tools like the EmitHelper class out there to make Emit easier. I am going to show the fundamentals today, this does not cover tools.

Today’s word list:

Domain
Application domains aid security, separating applications from each other and each other’s data. A single process can run several application domains, with the same level of isolation that would exist in separate processes. Running multiple applications within a single process increases server scalability.
Faults in one application domain cannot affect other code running in another application domain.

Module
A module is a portable executable file, such as type.dll or application.exe, consisting of one or more classes and interfaces. There may be multiple namespaces contained in a single module, and a namespace may span multiple modules.
One or more modules deployed as a unit compose an assembly.

The hierarchy is: domain => assemblies => modules => classes => functions


Ildasm.exe

A disassembler for MSIL code.

The first step is to create an executable file from:

using System;
using System.Reflection;

namespace HelloWorld {
    public class Program {
        static void Main(string[] args) {
        } //
        

        public string Test() {
            return string.Format("DateTime is {0:dd MMM yyyy  HH:mm:ss}", DateTime.Now);
        } //

        public string Test2() {
            return DateTime.UtcNow.ToString();
        } //
        
        public string Test3() {
            return Assembly.GetExecutingAssembly().ToString();
        } //

        public void Test4() {
            Console.WriteLine("hello world !");
        } //

    } // class
} // namespace

On the windows taskbar, click Start, click All Programs, click Visual Studio, click Visual Studio Tools, and then click Visual Studio Command Prompt.
-or-
If you have the Windows SDK installed on your computer: On the taskbar, click Start, click All Programs, click the folder for the Windows SDK, and then click Command Prompt (or CMD Shell).

Change to the right folder and enter “ildasm HelloWorld.exe /output:HelloWorld.il”.

ILDASM

In your folder you should see something like this:

SnapShot

Open the HelloWorld.il file in a text editor and delve into the following sections:

method Test()

  .method public hidebysig instance string 
          Test() cil managed
  {
    // Code size       21 (0x15)
    .maxstack  8
    IL_0000:  ldstr      "DateTime is {0:dd MMM yyyy  HH:mm:ss}"
    IL_0005:  call       valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Now()
    IL_000a:  box        [mscorlib]System.DateTime
    IL_000f:  call       string [mscorlib]System.String::Format(string,
                                                                object)
    IL_0014:  ret
  } // end of method Program::Test

method Test3()

  .method public hidebysig instance string 
          Test2() cil managed
  {
    // Code size       20 (0x14)
    .maxstack  1
    .locals init ([0] valuetype [mscorlib]System.DateTime CS$0$0000)
    IL_0000:  call       valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::get_UtcNow()
    IL_0005:  stloc.0
    IL_0006:  ldloca.s   CS$0$0000
    IL_0008:  constrained. [mscorlib]System.DateTime
    IL_000e:  callvirt   instance string [mscorlib]System.Object::ToString()
    IL_0013:  ret
  } // end of method Program::Test2

method Test3()

  .method public hidebysig instance string 
          Test3() cil managed
  {
    // Code size       11 (0xb)
    .maxstack  8
    IL_0000:  call       class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::GetExecutingAssembly()
    IL_0005:  callvirt   instance string [mscorlib]System.Object::ToString()
    IL_000a:  ret
  } // end of method Program::Test3

method Test4()

  .method public hidebysig instance void 
          Test4() cil managed
  {
    // Code size       11 (0xb)
    .maxstack  8
    IL_0000:  ldstr      "hello world !"
    IL_0005:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000a:  ret
  } // end of method Program::Test4

These code sections give you a rough idea of what we are going to code in runtime.
Start a new console project. You have to edit the file “AssemblyInfo.cs”. Add the assembly attribute AllowPartiallyTrustedCallersAttribute. The program won’t run otherwise. We are on a low coding level and Microsoft obviously tries to protect code especially on that level.

AssemblyInfo.cs

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

// 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("oink")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("blablabla")]
[assembly: AssemblyCopyright("Copyright ©  2014")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AllowPartiallyTrustedCallersAttribute]

// 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("0bf893aa-f3da-49ef-a2dc-a63d8ffc9ead")]

// 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")]

The Print() method is not big, nevertheless you find something unusual here. The keyword __arglist is not official. It has been implemented by Microsoft, but is not documented at all. It is also not well supported in C#. For instance you cannot use a foreach loop. __arglist takes any number of optional arguments like object[]. In our example it does make a big difference to use __arglist. We do not have to create an array and fill it in. This would be much more code in Emit. On the contrary the __arglist algorithm is quite short and comfortable. Follow the link for more __arglist documentation.

public static void Print(string xHeader, __arglist) {
    ArgIterator lIterator = new ArgIterator(__arglist);

    Console.WriteLine(xHeader);
    while (lIterator.GetRemainingCount() > 0) {
        TypedReference r = lIterator.GetNextArg();
        object o = TypedReference.ToObject(r);
        Console.Write(o);
    }
    Console.WriteLine();
    Console.WriteLine();
} //

EmitCode() is the actual code generation part. Let’s call the remaining source code “administration” to simplify the situation.
There are two ways to get the MethodInfo for a property. One is used for DateTime.Now, the other one is used for DateTime.UtcNow .
They can be used equally. I used both ways for demonstration purposes only.
I tried to keep the example simple. You don’t have to study the MSIL to understand the code. Some information about MSIL OpCodes can be found here.

static void EmitCode(ILGenerator xILGenerator) {
    MethodInfo lMethodInfo_Print = typeof(EmitDemo).GetMethod("Print");
    MethodInfo lDateTime_Now = typeof(DateTime).GetProperty("Now").GetGetMethod();
    MethodInfo lFormat = typeof(string).GetMethod("Format", new Type[] { typeof(string), typeof(object) });

    xILGenerator.Emit(OpCodes.Ldstr, "DateTime is {0:dd MMM yyyy  HH:mm:ss}");
    xILGenerator.Emit(OpCodes.Call, lDateTime_Now);
    xILGenerator.Emit(OpCodes.Box, typeof(DateTime));
    xILGenerator.Emit(OpCodes.Call, lFormat);
    xILGenerator.EmitCall(OpCodes.Call, lMethodInfo_Print, new Type[] { });

    xILGenerator.Emit(OpCodes.Ldstr, "This equals UTC: ");
    xILGenerator.Emit(OpCodes.Call, typeof(DateTime).GetMethod("get_UtcNow"));  // compare this with lDateTime_Now (== same, just another demo approach)
    xILGenerator.EmitCall(OpCodes.Call, lMethodInfo_Print, new Type[] { typeof(DateTime) });

    xILGenerator.Emit(OpCodes.Ldstr, "The assembly is: ");
    xILGenerator.Emit(OpCodes.Call, typeof(Assembly).GetMethod("GetExecutingAssembly"));
    xILGenerator.EmitCall(OpCodes.Call, lMethodInfo_Print, new Type[] { typeof(Assembly) });

    xILGenerator.Emit(OpCodes.Ldstr, "Console.WriteLine() is old school.");
    xILGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(object) }));

    xILGenerator.EmitWriteLine("EmitWriteLine() is for lazy programmers.");

    xILGenerator.Emit(OpCodes.Ret);
} //

Test1_ViaFullAssembly() shows you how to properly initialize and use an assembly. Remember the hierarchy from above: domain => assemblies => modules => classes => functions
The method is build like a chain. Each statement uses the result of the previous one.

public static void Test1_ViaFullAssembly() {
    AssemblyName lAssemblyName = new AssemblyName("MyAssembly");
    AssemblyBuilder lAssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(lAssemblyName, AssemblyBuilderAccess.Run);
    ModuleBuilder lModuleBuilder = lAssemblyBuilder.DefineDynamicModule("MyModule");
    TypeBuilder lTypeBuilder = lModuleBuilder.DefineType("MyType");
    MethodBuilder lMethodBuilder = lTypeBuilder.DefineMethod("DoSomething", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, typeof(void), Type.EmptyTypes);
    EmitCode(lMethodBuilder.GetILGenerator());

    Type lType = lTypeBuilder.CreateType();
    lType.GetMethod("DoSomething").Invoke(null, null);

    Console.ReadLine();
} //

example output:
DateTime is 15 Jan 2014 23:17:30

This equals UTC:
15/01/2014 23:17:30

The assembly is:
MyAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

Console.WriteLine() is old school.
EmitWriteLine() is for lazy programmers.

You can avoid building a full assembly. DynamicMethod simplifies the dynamic creation of methods. Of course you won’t have any class structure. The method is super-public, it reminds me of VBA modules.

public static void ViaDynamicMethod() {
    DynamicMethod lDynamicMethod = new DynamicMethod("DoSomething", typeof(void), Type.EmptyTypes, typeof(object));
    EmitCode(lDynamicMethod.GetILGenerator());
    lDynamicMethod.Invoke(null, null);
    Console.ReadLine();
} //

example output:
DateTime is 15 Jan 2014 23:17:58

This equals UTC:
15/01/2014 23:17:58

The assembly is:
mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

Console.WriteLine() is old school.
EmitWriteLine() is for lazy programmers.

Advertisements

About Bastian M.K. Ohta

Happiness only real when shared.

Posted on January 16, 2014, in C#, Professional, Reflection and tagged , , , , , , , , , , , , , , , , , . Bookmark the permalink. 1 Comment.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

%d bloggers like this: