MSIL Tutorial

Introduction

Microsoft Intermediate Language (MSIL) is a language used as the output of a number of compilers (C#, VB, .NET, and so forth). The ILDasm (Intermediate Language Disassembler) program that ships with the .NET Framework SDK (FrameworkSDK\Bin\ildasm.exe) allows the user to see MSIL code in human-readable format. By using this utility, we can open any .NET executable file (EXE or DLL) and see MSIL code.

The ILAsm program (Intermediate Language Assembler) generates an executable file from the MSIL language. We can find this program in the WINNT\Microsoft.NET\Framework\vn.nn.nn directory.

Any Visual C++ programmer starting with .NET development is interested in what happens in the low level of the .NET Framework. Learning MSIL gives a user the chance to understand some things that are hidden from a programmer working with C# or VB.NET. Knowing MSIL gives more power to a .NET programmer. We never need to write programs in MSIL directly, but in some difficult cases it is very useful to open the MSIL code in ILDasm and see how things are done.

A MSIL reference in DOC format is available to a .NET developer and may be found in the Framework SDK directory:

  • FrameworkSDK\Tool Developers Guide\docs\Partition II Metadata.doc (Metadata Definition and Semantics). In this file, I found a description of all MSIL directives such as .entrypoint, .locals, and so on.
  • FrameworkSDK\Tool Developers Guide\docs\Partition III CIL.doc (CIL Instruction Set) contains a full list of the MSIL commands.

I also used in my work in an ILDAsm tutorial from MSDN and an excellent article in the May 2001 issue of MSDN Magazine: "ILDASM is Your New Best Friend" by John Robbins.

I think the best way to learn the language is to write some programs in it. This is a reason I decided to make several small MSIL programs. Actually, I didn't write this code—the C# compiler generated it. I made some minor changes and added a lot of notes describing how MSIL is working.

Reading the sample projects attached to this article may help a .NET programmer understand Intermediate Language and easily read MSIL code when this is necessary.

General Information

All operations in MSIL are executed on the stack. When a function is called, its parameters and local variables are allocated on the stack. Function code starting from this stack state may push some values onto the stack, make operations with these values, and pop values from the stack.

Execution of both MSIL commands and functions is done in three steps:

  1. Push command operands or function parameters onto the stack.
  2. Execute the MSIL command or call function. The command or function pops their operands (parameters) from the stack and pushes onto the stack result (return value).
  3. Read result from the stack.

Steps 1 and 3 are optional. For example, the void function doesn't push a return value to the stack.

The stack contains objects of value types and references to objects of reference type. Reference type objects are kept in the heap.

MSIL commands used to push values onto the stack are called ld... (load). Commands used to pop values from the stack are called st... (store), because values are stored in variables. Therefore, we will call the push operation loading and the pop operation storing.

Sample Projects

The code attached to this article contains a number of Console Applications written in MSIL. To build them, ensure that the ILAsm program is available through the PATH. Each project is done as a Visual Studio solution. The source IL file may be opened in the VS Text Editor. The build command runs the ILAsm program, which generates an exe file in the project directory. The run command executes this file. At the end of each program, I added these lines, which can be written in C#:

    Console.WriteLine("Press Enter to continue");
    Console::Read();

This is done to see the program output when it is run from Windows Explorer.

Here's a list of the included projects:

  1. PrintString—prints the string to the console.
  2. XequalN—assigns a value to the int variable and prints it to the console.
  3. Operations—reads two numbers from the console; makes operations +, -, and *; and shows the result.
  4. Array1—allocates int array, assign values to its elements; print elements and array length.
  5. Compare—enters two numbers and prints the minimum.
  6. Array2—fills array elements in loop and prints some elements.
  7. Unsafe—uses unsafe pointers to access array elements.
  8. PInvoke—calls Win32 API.
  9. Classes—works with classes.
  10. Exception—handles exceptions.

I suggest that you read these projects in the same order as they are described here. In the projects' descriptions given below, I explain each new MSIL command used in the program and show some code fragments.

PrintString Program

PrintString is the MSIL Hello, World application.

MSIL directives used in the code are as follows:

  • .entrypoint—defines the application entry point (the function called by .NET Runtime when the program starts).
  • .maxstack—defines the maximum stack depth used by the function code. The C# compiler sets always the exact value for each function. In the sample project, I set this value to 8.

MSIL commands are as follows:

  • ldstr string—loads the string constant onto the stack.
  • call function(parameters)—calls the static function. Parameters for the function should be loaded onto the stack before this call.
  • pop—pops a value from the stack. Used when we don't need to store a value in the variable.
  • ret—returns from a function.

Calling the static function is simple. We push to stack the function parameters, call the function, and read from the stack function return value (if function is not void). Console.WriteLine is an example of such a function.

Here is the code:

.assembly PrintString {}

/*
    Console.WriteLine("Hello, World)"
*/

.method static public void main() il managed
{
    .entrypoint             // this function is the application
                            // entry point

    .maxstack 8


    // *****************************************************
    // Console.WriteLine("Hello, World)";
    // *****************************************************
    ldstr "Hello, World"        // load string onto stack

    // Call static System.Console.Writeline function
    // (function pops string from the stack)
    call   void [mscorlib]System.Console::WriteLine
                                 (class System.String)


    // *****************************************************
    ldstr "Press Enter to continue"
    call   void [mscorlib]System.Console::WriteLine
                                 (class System.String)


    // Call the System.Console.Read function
    call int32 [mscorlib]System.Console::Read()

    // The pop instruction removes the top element from the stack.
    // (remove number returned by Read() function)
    pop
    // *****************************************************

    ret
}

XequalN Program

The program assigns a value to the integer variable and prints it to the console window.

Commands:

  • ldc.i4.n—loads a 32-bit constant (n from 0 to 8) onto the stack
  • stloc.n—stores a value from the stack to local variable number n (n from 0 to 3)

Code:

.assembly XequalN {}

// int x;
// x = 7;
// Console.WriteLine(x);

.method static public void main() il managed
{
    .entrypoint

    .maxstack 8

    .locals init ([0] int32 x)  // Allocate local variable

    // *****************************************************
    // x = 7;
    // *****************************************************
    ldc.i4.7                    // load constant onto stack
    stloc.0                     // store value from stack to
                                // var. 0
    
    // *****************************************************
    // Console.WriteLine(x);
    // *****************************************************
    ldloc.0                     // load var.0 onto stack
    call void [mscorlib]System.Console::WriteLine(int32)

    ret
}

Operations Program

The program reads two numbers from the console, makes simple math operations with them, and shows the result.

Commands:

  • add—adds two values. Command parameters should be loaded onto the stack before the call. The function pops the parameters and pushes a result onto the stack.
  • sub—subtracts two values.
  • mul—multiplies two values.

Code fragments:

.assembly Operations {}
/*
// This program works as C# code:

            int x, y, z;
            string s;

            Console.WriteLine("Enter x:");
            s = Console.ReadLine();
            x = Int32.Parse(s);

            Console.WriteLine("Enter y:");
            s = Console.ReadLine();
            y = Int32.Parse(s);

            z = x + y;
            Console.Write("x + y = ");
            Console.Write(z);
            Console.WriteLine("");

            z = x - y;
            Console.Write("x - y = ");
            Console.Write(z);
            Console.WriteLine("");

            z = x * y;
            Console.Write("x * y = ");
            Console.Write(z);
            Console.WriteLine("");
*/

.method static public void main() il managed
{
    .entrypoint
    .maxstack 8

    .locals init ([0] int32 x,
           [1] int32 y,
           [2] int32 z,
           [3] string s)

    // *****************************************************
    // Console.WriteLine("Enter x:");
    // *****************************************************
    ldstr      "Enter x:"       // load string onto stack
    call       void [mscorlib]System.Console::WriteLine(string)

    // *****************************************************
    // s = Console.ReadLine();
    // *****************************************************
    call       string [mscorlib]System.Console::ReadLine()
    stloc.3                     // store value to var. 3

    // *****************************************************
    // x = Int32.Parse(s);
    // *****************************************************
    ldloc.3                     // load variable 3 onto stack

    // Call System.Int32::Parse(string)
    // Function pops string from stack and pushes to stack
    // int32 value - result of parsing.
    call       int32 [mscorlib]System.Int32::Parse(string)

    stloc.0                     // store value to var. 0



    // *****************************************************
    // Same operations with variable y
    // *****************************************************
    ldstr      "Enter y:"
               // load string
    call       void [mscorlib]System.Console::WriteLine(string)
               // call
    call       string [mscorlib]System.Console::ReadLine()
               // call
    stloc.3
               // store to var. 3
    ldloc.3
               // load var. 3
    call       int32 [mscorlib]System.Int32::Parse(string)
               // call
    stloc.1
               // store to var. 1

    // *****************************************************
    // z = x + y;
    // *****************************************************
    ldloc.0             // load variable 0 onto stack
    ldloc.1             // load variable 1 onto stack

    // pop two values from the stack, add them and push result
    // onto stack
    add

    stloc.2             // store to variable 2

    // *****************************************************
    // Console.Write("x + y = ");
    // *****************************************************
    ldstr      "x + y = "          // load string onto stack
    call       void [mscorlib]System.Console::Write(string)

    // *****************************************************
    // Console.Write(z);
    // *****************************************************
    ldloc.2                    // load variable 2 onto stack
    call       void [mscorlib]System.Console::Write(int32)

    // *****************************************************
    // Console.WriteLine("");
    // *****************************************************
    ldstr      ""                  // load string onto stack
    call       void [mscorlib]System.Console::WriteLine(string)

    // Same operations with subtraction and multiplication ...

    ret
}

Array1 Program

The program allocates the int array, assigns values to its elements, and then prints the elements and array length.

Commands:

  • newarr type—creates an array of type elements. The array size should be loaded onto the stack before a call to this command. Loads onto the stack a reference to the array.
  • stelem.i4—assigns a value to an array member. The value has type Int32. The array reference, index, and value should be loaded onto the stack before a call to this command.
  • ldelema type—loads to the stack the address of an array element. The array reference and index should be loaded onto the stack before a call to this command. The address is used to call a non-static class function (see later).
  • ldlen—loads the length of an array onto the stack. The array reference should be loaded onto the stack before a call to this command.
  • ldloca.s variable—loads the address of the variable onto the stack.
  • ldc.i4.s value—loads an Int32 constant onto the stack (used for values more than 8).
  • conv.i4—converts value from the stack to Int32.
  • call instance function(arguments)—calls a non-static class function. Before a call to a non-static function, we need to load onto the stack the address of the class object (used first as a hidden parameter, as in C++) and function arguments. In this sample object, the address is loaded using the ldelema and ldloca commands.

In some code fragments in this sample, I wrote in the notes to stack the state starting after the last local variable. In this sample, we see the variable generated by the compiler. This variable is used to make the call to the non-static class function.

Code:

.assembly Array1 {}

/*
// This program works as C# code:

int[] x = new int[5];
x[0] = 10;
x[1] = 20;

Console.WriteLine("x[0] = " + x[0].ToString());
Console.WriteLine("x[1] = " + x[1].ToString());

Console.WriteLine("Array length = " + x.Length.ToString());

*/

.method static public void main() il managed
{
    .entrypoint
    .maxstack 8

    .locals init ([0] int32[] x,
                  [1] int32 tmp)    // generated by compiler


    // *****************************************************
    // x = new int[5];
    // *****************************************************
    ldc.i4.5                     // load constant onto stack

    // create array and store reference onto stack
    newarr     [mscorlib]System.Int32

    // Store (pop) value from the stack and place it to local
    // variable 0.
    stloc.0

    // *****************************************************
    // x[0] = 10;
    // *****************************************************
    ldloc.0           // Load local variable 0 onto stack (array)
    ldc.i4.0          // Load constant 0 to the stack     (index)
    ldc.i4.s   10     // Load constant 10 to the stack    (value)
    stelem.i4         // array[index] = value


    // The same operations with element number 1...


    // ***************************************************
    // Console.WriteLine("x[0] = " + x[0].ToString());
    // ***************************************************
    ldstr      "x[0] = "            // load string onto stack
                // STACK: "x[0] = "  (stack is shown from local
                // variables)
    ldloc.0                         // load variable 0 onto stack
    ldc.i4.0                        // load constant 0 onto stack
                // STACK: "x[0] = " -> x -> 0
    // Load address of array element onto stack.
    ldelema    [mscorlib]System.Int32
                // STACK: "x[0] = " -> pointer to Int32 instance
                // 10
    // Call non-static function System.Int32::ToString().
    call       instance string [mscorlib]System.Int32::ToString()
                // STACK: "x[0] = " -> "10"
    // call static System.String::Concat(string, string)
    call       string [mscorlib]System.String::Concat
                                               (string, string)
                // STACK: "x[0] = 10"
    // call static System.Console::WriteLine(string)
    call       void [mscorlib]System.Console::WriteLine(string)
                // STACK: empty


    // The same operations with element number 1 ...


    // *****************************************************
    // Console.WriteLine("Array length = " + x.Length.ToString());
    // *****************************************************
    ldstr      "Array length = "
                // load string onto stack
                // STACK: "Array length = "
    ldloc.0
                // load variable 0 to stack
                // STACK: "Array length = " -> x
    ldlen
                // push the length of array onto stack
                // STACK: "Array length = " -> 5
    conv.i4
                // Convert to int32, pushing int32 onto stack
                // STACK: "Array length = " -> 5
    stloc.1
                // store to local variable 1 (tmp)
                // STACK: "Array length = "
    ldloca.s   tmp
                // load address of variable tmp onto stack
                // STACK: "Array length = " -> &tmp
    call       instance string [mscorlib]System.Int32::ToString()
                // STACK: "Array length = " -> "5"
    call       string [mscorlib]System.String::Concat
                                       (string, string)
                // STACK: "Array length = 5"
    call       void [mscorlib]System.Console::WriteLine(string)
                // STACK: empty

    ret
}

Compare Program

The program reads two numbers and prints their minimum.

Commands:

  • bge.s label—goes to label if value1 is greater than or equal to value 2. Values 1 and 2 should be loaded onto the stack before a call to this command.
  • br.s label—goes to label.
  • box value type—converts a value type to an Object and loads the Object's reference onto the stack.

Boxing in this program is caused by the C# line: Console.WriteLine("{0:d}", z);
Writing this line in this way: Console.WriteLine(z.ToString());
doesn't cause boxing.

Code:

.assembly Compare {}
/*
            int x, y, z;
            string s;

            Console.WriteLine("Enter x:");
            s = Console.ReadLine();
            x = Int32.Parse(s);

            Console.WriteLine("Enter y:");
            s = Console.ReadLine();
            y = Int32.Parse(s);

            if ( x < y )
                z = x;
            else
                z = y;

            Console.WriteLine("{0:d}", z);
*/

.method static public void main() il managed
{
    .entrypoint
    .maxstack 8
    
    .locals init ([0] int32 x,
                  [1] int32 y,
                  [2] int32 z,
                  [3] string s)

    // *****************************************************
    // Console.WriteLine("Enter x:");
    // *****************************************************
    ldstr      "Enter x:"               // load string onto stack
    call  void [mscorlib]System.Console::WriteLine(string)
    
    // *****************************************************
    // s = Console.ReadLine();
    // *****************************************************
    call       string [mscorlib]System.Console::ReadLine()
    stloc.3                             // store to var. 3

    // *****************************************************
    // x = Int32.Parse(s);
    // *****************************************************
    ldloc.3                             // load var. 3 onto stack
    call       int32 [mscorlib]System.Int32::Parse(string)
    stloc.0                             // store to var. 0

    // The same operations for y ...

    // *****************************************************
    // branch
    // if ( x >= y ) goto L_GR;
    // *****************************************************
    ldloc.0                     // load x onto stack (value 1)
    ldloc.1                     // load y onto stack (value 2)
    bge.s  L_GR                 // goto L_GR if value1 is greater
                                // than or equal to value2

    // *****************************************************
    // z = x
    // *****************************************************
    ldloc.0                     // load variable 0 onto stack
    stloc.2                     // store to variable 2

    br.s       L_CONTINUE       // goto L_CONTINUE

L_GR:

    // *****************************************************
    // z = y
    // *****************************************************
    ldloc.1             // load variable 1 onto stack
    stloc.2             // store to variable 2

L_CONTINUE:

    // *****************************************************
    // Console.WriteLine("{0:d}", z);
    // NOTE: this line causes boxing.
    // *****************************************************
    ldstr      "{0:d}"  // load string onto stack
    ldloc.2             // load variable 2 to stack (z)
    box       [mscorlib]System.Int32   // convert Int32 to Object
    call  void [mscorlib]System.Console::WriteLine(string, object)

    ret
}

Array2 Program

The program fills an array in the loop and prints its elements. This time, we add the static function ShowNumber(int), which is called from main.

Commands:

  • blt.s label—goes to label if value 1 is less than value 2. Values 1 and 2 should be loaded onto the stack before a call to this command.
  • ldelem.i4—loads an array element onto the stack. A reference to the array and index should be loaded onto the stack before a call to this command.
  • ldarga.s argument—loads the address of the function argument onto the stack.

We can see in this program that the for loop is implemented in MSIL using labels.

Code:

.assembly Array2 {}
/*

            int[] px = new int[100];
            int i;

            for ( i = 1; i < 100; i++ )
            {
                px[i] = i + 1;
            }

            ShowNumber(px[5]);
            ShowNumber(px[10]);
            

        static void ShowNumber(int n)
        {
            Console.WriteLine(n.ToString());
        }
*/

.method static public void main() il managed
{
    .entrypoint
    .maxstack 8

    .locals init ([0] int32[] px,
                  [1] int32 i)

    // *****************************************************
    // x = new int[100]
    // *****************************************************
    ldc.i4.s   100                      // load constant onto
                                        // stack
    newarr     [mscorlib]System.Int32   // allocate Int32
    stloc.0                             // store to variable 0

    // *****************************************************
    // i = 1
    // *****************************************************
    ldc.i4.1                    // load constant onto stack
    stloc.1                     // store to variable 1

    br.s       CHECK_COUNTER    // goto CHECK_COUNTER

START_LOOP:
    // *****************************************************
    // px[i] = i + 1;
    // *****************************************************
    ldloc.0                     // load variable 0 to stack
                                // STACK: px
    ldloc.1                     // load variable 1 to stack
                                // STACK; px -> i
    ldloc.1                     // load variable 1 to stack
                                // STACK: px -> i -> i
    ldc.i4.1                    // load constant to stack
                                // STACK: px -> i -> i -> 1.
    add                         // add last two values
                                // STACK: px -> i -> i+1
                                //        (array,index,value)
    stelem.i4                   // store value to array element:
                                // array[index] = value
                                // STACK: empty
    // *****************************************************
    // i = i + 1
    // *****************************************************
    ldloc.1                     // load variable 1 onto stack
    ldc.i4.1                    // load constant onto stack
    add                         // add
    stloc.1                     // store to variable 1

CHECK_COUNTER:
    // *****************************************************
    // if i < 100 goto start f loop
    // *****************************************************
    ldloc.1                     // load variable 1 onto stack
    ldc.i4.s   100              // load constant onto stack
    blt.s      START_LOOP       // if value1 < value2 go to
                                // START_LOOP


    // *****************************************************
    // ShowNumber(px[5]
    // *****************************************************
    ldloc.0                     // load variable 0 onto stack
                                // (array)
    ldc.i4.5                    // load constant onto stack
                                // (index)
    ldelem.i4                   // load array element to stack
    call       void ShowNumber(int32)   // call ShowNumber

    // *****************************************************
    // ShowNumber(px[10]
    // *****************************************************
    ldloc.0
    ldc.i4.s   10
    ldelem.i4
    call       void ShowNumber(int32)

    ret
}

.method static public void  ShowNumber(int32 n) il managed
{
  .maxstack  1
 
  ldarga.s   n          // load to stack address of argument n
  
  call       instance string [mscorlib]System.Int32::ToString()
  call       void [mscorlib]System.Console::WriteLine(string)
  
  ret
} 

Unsafe Program

The program fills and prints the int array using an unsafe pointer.

In this program, we see the new, unsafe types: int32* and int32&. The pinned keyword, used with a local variable, prevents GC from moving the object pointed to by the variable.

Commands:

  • dup—duplicates the value on the stack.
  • stind.i4—stores the value by address. The address and value should be loaded onto the stack before a call to this command.

Code:

.assembly Unsafe {}
/*
int[] nArray = new int[5];
int i;
int* pCurrent;

fixed ( int* pArray = nArray )
{
    pCurrent = pArray;

    for ( i = 0; i < 5; i++ )
    {
        *pCurrent++ = i + 1;
    }
}

for ( i = 0; i < 5; i++ )
{
    Console.WriteLine(nArray[i].ToString());
}

*/

.method static public void main() il managed
{
    .entrypoint
    .maxstack 8

    .locals ([0] int32[] nArray,
             [1] int32 i,
             [2] int32* pCurrent,
             [3] int32& pinned pArray)  // GC doesn't move
                                        // pointed object

    // *****************************************************
    // nArray = new int[5];
    // *****************************************************
    ldc.i4.5                            // load constant 5 onto
                                        // stack
    newarr     [mscorlib]System.Int32   // create array Int32[5]
    stloc.0                             // store value from stack
                                        // to local var. o

    // *****************************************************
    // pArray = nArray    (pArray = &nArray[0])
    // *****************************************************
    ldloc.0
               // load variable 0 onto stack (array)
    ldc.i4.0
               // load constant 0 onto stack (index)
    ldelema    [mscorlib]System.Int32
               // load address of array[index] to stack
    stloc.3
               // store value from stack to local var. 3

    // *****************************************************
    // pCurrent = pArray;
    // *****************************************************
    ldloc.3                     // load variable 3 onto stack
    conv.i                      // convert to native int
    stloc.2                     // store to variable 2

    // *****************************************************
    // i = 0
    // *****************************************************
    ldc.i4.0                    // load constant 0 onto stack
    stloc.1                     // store value to var. 1

    // *****************************************************
    // goto CHECK_COUNTER
    // *****************************************************
    br.s       CHECK_COUNTER

START_LOOP:

    // *****************************************************
    // *pCurrent++ = i + 1                            [STACK]
    // *****************************************************
    // 1) keep old pCurrent value on the stack and increment
    // pCurrent
    ldloc.2
          // load variable 2 onto stack     [pCurrent]
    dup
          // duplicate the top value of the stack
          //                                [pCurrent pCurrent]
    ldc.i4.4
          // load constant 4 onto stack     [pCurrent pCurrent 4]
    add
          // add                            [pCurrent pCurrent + 4]
    stloc.2
          // store from stack to variable 2 [pCurrent]

    // 2) write (i+1) by old pCurrent value kept on the stack
    ldloc.1
          // load variable 1 onto stack     [pCurrent i]
    ldc.i4.1
          // load constant 1 onto stack     [pCurrent i 1]
    add   // add                            [pCurrent i+1]
    //                                      address  value
    stind.i4
          // store value by address         [empty]

    // *****************************************************
    // i = i + 1
    // *****************************************************
    ldloc.1             // load variable 1 onto stack
    ldc.i4.1            // load constant 1 onto stack
    add                 // add
    stloc.1             // store to variable 1

CHECK_COUNTER:

    // *****************************************************
    // if (i < 5) goto START_LOOP;
    // *****************************************************
    ldloc.1                     // load variable 1 onto stack
    ldc.i4.5                    // load constant 5 onto stack
    blt.s      START_LOOP       // goto if less

    // *****************************************************
    // pArray = 0               fixed block finished
    // *****************************************************
    ldc.i4.0                    // load constant 0 to stack
    conv.u                      // convert to native unsigned int,
                                // pushing native int on stack
    stloc.3                     // store in variable 3

    // print array elements to console...

    ret
}

PInvoke Program

The program shows the computer name using the Win32 API GetComputerName and MessageBox. API declarations in MSIL look like this:

.method public hidebysig static pinvokeimpl("kernel32.dll"
                                             autochar winapi)
        int32  GetComputerName(
               class [mscorlib]System.Text.StringBuilder
                                      marshal( lptstr) buffer,
               int32& size) cil managed preservesig
{
}

.method public hidebysig static pinvokeimpl("User32.dll"
                                             autochar winapi)
        int32  MessageBox(native int hWnd,
                          string  marshal( lptstr) lpText,
                          string  marshal( lptstr) lpCaption,
                          int32 uType) cil managed preservesig
{
}

They are called by the same rules as other any functions.

Classes Program

In previous programs, we called the class functions from the static function main. In this program, we will see how to write classes. The program contains two classes: Class1, with function main; and SampleClass, created in main.

Directive:

  • .field—defines class member. Used with keywords public, private, static, and so forth.

Commands:

  • stsfld static field—replaces the value of the static field with the value from the the stack.
  • ldfld field—loads a non-static class field onto the stack. The address of the class instance should be loaded onto the stack before a call to this command.
  • ldarg.n—loads argument number n onto the stack. In a non-static class function, argument 0 is a hidden argument and points to the this instance.
  • newobj constructor—creates a new instance of a class using constructor. Constructor parameters should be loaded onto the stack before this call. A reference to the created instance is loaded onto the stack.
  • callvirt instance function—calls a late-bound method on an object.

Code:

.assembly Classes {}
/*
    class SampleClass
    {
        private int m_n;
        private string m_s;

        public static int nStatic = 10;

        public SampleClass(int n, string s)
        {
            m_n = n;
            m_s = s;
        }

        public int Number
        {
            get
            {
                return m_n;
            }
        }

        public string String
        {
            get
            {
                return m_s;
            }
        }

    };

    class Class1
    {
        [STAThread]
        static void Main(string[] args)
        {
            SampleClass o = new SampleClass(1, "Sample");

            Console.WriteLine(SampleClass.nStatic.ToString());

            Console.WriteLine(o.Number.ToString());

            Console.WriteLine(o.String);
        }
    }
*/


.class private auto ansi beforefieldinit SampleClass
       extends [mscorlib]System.Object
{
    .field private int32 m_n              // private int m_n;
    .field private string m_s             // private string m_s;
    .field public static int32 nStatic    // public static int
                                          // nStatic;

    // private static constructor generated by compiler
    // (generated to initialize static class member)
    .method private hidebysig specialname rtspecialname static
        void  .cctor() cil managed
    {
        .maxstack  8

        // *************************************************
        // nStatic = 10
        // *************************************************
        ldc.i4.s 10            // load constant onto stack
        // The stsfld instruction replaces the value of a static
        // field with a value from the stack
        stsfld     int32 SampleClass::nStatic

        ret
    }

    // constructor
    // public SampleClass(int n, string s)
    //
    .method public hidebysig specialname rtspecialname 
        instance void  .ctor(int32 n, string s) cil managed
    {
        .maxstack  8

        // *************************************************
        // Call base class constructor
        // *************************************************
        ldarg.0         // Load argument 0 onto stack (hidden
                        // pointer to this)
        // call Object constructor
        call       instance void [mscorlib]System.Object::.ctor()

        // *************************************************
        // m_n = n
        // *************************************************
        ldarg.0         // Load argument 0 onto stack
                        // (hidden pointer to this)
        ldarg.1         // load argument 1 onto stack (n) 
        // store value n in field m_n in instance pointed
        // by this
        stfld      int32 SampleClass::m_n

        // *************************************************
        // m_s = s
        // *************************************************
        ldarg.0         // Load argument 0 onto stack
                        // (hidden pointer to this)
        ldarg.2         // load argument 1 onto stack (s)
        // store value s in field m_s in instance pointed
        // by this
        stfld      string SampleClass::m_s

        ret
    }

    // Number property
    .property instance int32 Number()
    {
        // call get_Number
        .get instance int32 SampleClass::get_Number()
    }

    .method public hidebysig specialname instance int32
        get_Number() cil managed
    {
        .maxstack  8

        // variable generated by compiler
        .locals ([0] int32 tmp)

        // *************************************************
        // return m_n;
        // *************************************************
        ldarg.0
                  // load argument 0 (this pointer)
        ldfld      int32 SampleClass::m_n
                  // load field of object pointed by stack value
        stloc.0
                  // store in variable 0
        ldloc.0
                  // load variable 0 onto stack (return value
                  // of function)

        ret
    }

    // String property
    .property instance string String()
    {
        .get instance string SampleClass::get_String()
    }
    
    .method public hidebysig specialname instance string
            get_String() cil managed
    {
        .maxstack  8

        // variable generated by compiler
        .locals ([0] string tmp)

        ldarg.0
             // load argument 0 (this pointer)
        ldfld      string SampleClass::m_s
             // load field of object pointed by stack value
        stloc.0
             // store in variable 0
        ldloc.0
             // load variable 0 onto stack (return value
             // of function)
        ret
    }
}


.class private auto ansi beforefieldinit Class1
       extends [mscorlib]System.Object
{
    // public default constructor
    .method public hidebysig specialname rtspecialname 
        instance void  .ctor() cil managed
    {
        .maxstack  8

        // *************************************************
        // Call base class constructor
        // *************************************************
        ldarg.0
                          // load this pointer
        call       instance void [mscorlib]System.Object::.ctor()
                          // call Object constructor

        ret
    }

    // Main function
    .method private hidebysig static void  Main(string[] args)
            cil managed
    {
        // this method is the entry point to the application
        .entrypoint

        // Custom attribute
        .custom instance void [mscorlib]System.
                STAThreadAttribute::.ctor() = ( 01 00 00 00 )

        .maxstack  8

        .locals ([0] class SampleClass o,
                 [1] int32 tmp)          // generated by compiler

        // *************************************************
        // o = new SampleClass(1, "Sample");
        // *************************************************
        ldc.i4.1                        // load constant 1 onto
                                        // stack
        ldstr      "Sample"             // load string constant
                                        // onto stack
        // create new object SampleClass passing 2 parameters
        // from stack.
        // Load reference to created object onto stack
        newobj     instance void SampleClass::.ctor(int32, string)
        stloc.0                         // store to variable 0

        // *************************************************
        // Access static class member
        // Console.WriteLine(SampleClass.nStatic.ToString());
        // *************************************************

        // Load the address of the static field on the stack
        ldsflda    int32 SampleClass::nStatic
        // call Int32::ToString for object from stack
        call       instance string [mscorlib]System.Int32
                            ::ToString()
        // call static WriteLine passing string from stack
        // as parameter
        call       void [mscorlib]System.Console
                   ::WriteLine(string)

        // *************************************************
        // Call non-static class function
        // Console.WriteLine(o.Number.ToString());
        // *************************************************
        ldloc.0                 // load variable 0
        // call function for object from stack
        call   instance int32 SampleClass::get_Number()
        stloc.1                         // store to variable 1
        ldloca.s  tmp                   // load address to stack
        call       instance string [mscorlib]System.Int32
                            ::ToString()
        call       void [mscorlib]System.Console
                        ::WriteLine(string)

        // *************************************************
        // Call non-static class member
        // Console.WriteLine(o.String);
        // *************************************************
        ldloc.0
        callvirt   instance string SampleClass::get_String()
        call       void [mscorlib]System.Console
                        ::WriteLine(string)

        // *************************************************
        ldstr "Press Enter to continue"
        call   void [mscorlib]System.Console
                    ::WriteLine(class System.String)
        call int32 [mscorlib]System.Console::Read()
        pop
        // *************************************************

        ret
    }
}

Exception Program

The program divides two numbers, catching a divide-by-zero exception. The try/catch block in MSIL looks like it does in C#.

Command:

  • leave.s label—leaves a protected block such as try or catch.

Code:

.assembly Exception {}

/*
            int x, y, z;
            string s;

            Console.WriteLine("Enter x:");
            s = Console.ReadLine();
            x = Int32.Parse(s);

            Console.WriteLine("Enter y:");
            s = Console.ReadLine();
            y = Int32.Parse(s);

            try
            {
                z = x / y;

                Console.WriteLine(z.ToString());
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

*/

.method static public void main() il managed
{
    .entrypoint
    .maxstack 8

    .locals ([0] int32 x,
             [1] int32 y,
             [2] int32 z,
             [3] string s,
             [4] class [mscorlib]System.Exception e)
             
    // Enter x, y ...

    .try
    {
        // *************************************************
        // z = x / y;
        // *************************************************
        ldloc.0                 // load var. 0
        ldloc.1                 // load var. 1
        div                     // divide
        stloc.2                 // store in var. 2

        // *************************************************
        // Console.WriteLine(z.ToString());
        // *************************************************
        ldloca.s   z            // load address of z
        call       instance string [mscorlib]System.Int32
                                   ::ToString()
        call       void [mscorlib]System.Console
                                   ::WriteLine(string)

        leave.s    END_TRY_CATCH        // exit try block
    }
    catch [mscorlib]System.Exception 
    {
        stloc.s    e        // store exception thrown on
                            // the stack

        // *************************************************
        // Console.WriteLine(e.Message);
        // *************************************************
        ldloc.s    e                // load e
        callvirt   instance string [mscorlib]System.Exception
                                   ::get_Message()
        call       void [mscorlib]System.Console
                                   ::WriteLine(string)
        leave.s    END_TRY_CATCH        // exit catch block
    }

    END_TRY_CATCH:

    ret
}

Downloads

Download source - 43 Kb



Comments

  • GHD news De plus en plus de gens commencent à utiliser lisseur GHD en Norvège

    Posted by gffbih384 on 07/16/2013 03:33pm

    Maintenant, avec tension universelle pour une performance optimale partout dans le monde que vous êtes deg.Hvilemodus est une configuration de sécurité intégré qui vous donne la tranquillité d'esprit, car il s'éteint automatiquement technologie numérique av.Unik signifie que la température est contrôlée automatiquement encore plus rapide pour un meilleur style. New rounder baril de sorte qu'il est facile de créer des boucles parfaites, vagues ou chiquenaudes, ainsi que les supprimer. [url=http://ghdpascherferfr.webgarden.com/]lisseur ghd pas cher[/url] Avec GHD IV Mini styler peut cornichon boucles, flips et des vagues ainsi que Slett cheveux. Le redskap idéal de style pour deg avec les cheveux courts, pannelugg ou encore pour menn.Med un design plus élégant et gyllne Plater, qui sont lisses et non formé, afin qu'ils lett glisse creux les cheveux et donne un résultat ultra-brillant. Avec le Skall d'expression accrue est stylerne kjøligere y toucher et léger poignée de flux. [url=http://ghdpascherfer.tripod.com/]ghd pas cher[/url] Il a fait sa liste (et le mien) parce qu'il atteigne 450 degrés en moins de 20 secondes, ce qui signifie le plus grossier de cheveux est plat avec juste un pull-through de ce fer. Autre technologie met ce fer à part des autres. Il communique avec vous via Smart Sound lorsque vous êtes prêt à utiliser, et peut automatiquement éteindre s'il n'est pas utilisé pendant 30 minutes.

    Reply
  • Micsactiodo Jingolurmoumn Pemiliailia atmoltabs

    Posted by SnotteMob on 07/14/2013 02:16am

    ro american football europe bo football shop europe id american football in europe fc discounted coach purses xa discount coach bags oo discounted coach purses vv louboutin pas cher xz louboutins pas cher tg escarpins louboutin pas cher hm burberry outlet online ns burberry outlet store ch burberry outlet store om coach purses outlet online hy coach purses outlet wi coach purses outlet online ne burberry outlet ky burberry bags pc burberry kids nd nike air max 90 canada dx nike air max 1 canada xd air max 90 canada ci prada outlet in hong kong by prada factory outlet hong kong da space prada hong kong ot sac burberry pas cher wr sacs burberry hc sacs burberry xi louis vuitton outlet ls louis vuitton wallet ck louis vuitton singapore fx red coach shoes mx red coach shoes fj red coach ub nike air max 1 fq nike air max 90 rg air max shop nh miu miu wallet singapore ur miu miu singapore price ys miu miu bags singapore xc nfl europe qr nfl jerseys deutschland gn nfl football deutschland vw nike air max 90 australia eb air max australia ih air maxes fm cheap nike air max 1 uk st cheap nike air max 90 uk ow cheap air max uk ry air max 90 women nh airmax 90 dx airmax 90 jr burberry outlet online np burberry kids mf burberry outlet online hg cheap nfl jersey ey cheap nfl jerseys authentic gz cheap nfl jerseys from china uk air max 90 jl nike air max 90 womens xv nike air max 90 australia kc burberry shoes rm burberry greece vq burberry outlet vz mulberry handbags uk mz mulberry outlet uk re fake mulberry bags uk jd coach mens wallets rh coach men wallet ok mens coach wallet uo burberry sunglasses tz burberry glasses ka burberry glasses pg mulberry singapore im mulberry alexa singapore qy mulberry alexa singapore oe air max uk df my air max uk dn nike air max 90 uk pz prada handbags ye prada handbags bv prada thailand uf nfl replica jersey sd nfl replica jerseys zg nfl replica jersey vk american football shirt hb american football shirts fc american football t shirts sb kelly birkin qs hermes kelly po sac kelly jy borse prada ci borse prada bd outlet prada yl air max 1 uf air max 1 sale fs nike air max 1 premium oc black coach purse qv black coach wristlet bs black coach shoes wx louboutin uk cp christian louboutin outlet uk kq christian louboutin sale uk af prada sac we prada sacs uh prada sac ru nfl brazil kw nfl brasil bl camisas nfl brasil or chaussure christian louboutin dm chaussure louboutin jl louboutin chaussures rl hermes hong kong lq hermes in hong kong sl hermes hong kong on nfl jersey sales xe nfl jerseys for sale kj nfl jerseys on sale ur chloe sunglasses uk hw chloe handbags uk lh see by chloe uk yv miu miu australia store xp miu miu australia an miu miu bags australia qu air max 1 portugal sb nike air max 90 portugal rg air max portugal rc jordan shoes uk de air jordans uk qq nike air jordan uk vx miu miu uk aj miu miu uk wy miu miu outlet uk vp christian louboutin schuhe zr louboutin schuhe fe louboutin outlet

    Reply
  • COACH 財布=http://www.coachcute.com/

    Posted by LesNaltestake on 06/23/2013 06:55am

    コーチ 長財布=http://www.coachcute.com/ にあまり効果がなますぶっ璧な[url=http://www.coachcandle.com/]コーチ 財布[/url]SEOがでく(とい存在しながいるのも事調べてみ コーチ アウトレット 財布=http://www.coacheternal.com/ 現役て本気交良く似合うり抜いライドをかなぐり捨てではない唾液でグてて[url=http://www.coacheternal.com/]COACH メンズ[/url]いく。ったマラをめ、舌ほとんどうすピン漏れて。 コーチ 斜めがけ=http://www.coachcandle.com/ 「お前にぇだろ丁寧に解してッ込む!!させる。「てねぇだろパンパ。それにず、[url=http://www.coacheternal.com/]コーチ 財布[/url]自分!これがだ!!ねぇだろ!

    Reply
  • Dll on the fly

    Posted by dado on 03/04/2013 05:14am

    Hi,can I ask you how you would make this dll on the fly ? Public Function fairisorsa(ByVal nomeris As String, ByVal nome As String) Dim rm As New Resources.ResourceManager(nomeris, Assembly.GetEntryAssembly) Dim rb As Byte() = DirectCast(tlogicrm.GetObject(nome), Byte()) Return rb End Function thank you for your help!

    Reply
  • !!!

    Posted by alejandro varela on 11/21/2012 05:02am

    great work! really cool!

    Reply
  • Yeah, but how do you do it?

    Posted by Dillinger on 02/02/2006 01:49pm

    This article tells you everything except how to actually do it! Perhaps you could include a bit that explains how to compile the MSIL file once you have created one.

    • See sample projects

      Posted by Alex F on 02/03/2006 01:23am

      Sample projects are built as makefile projects, which execute build.bat file. 
      For example, this is build.bat file from Array1 project: 
      
      ilasm /nologo /debug Array1.il 
      
      ilasm is MSIL compiler. /debug is configuration, Array.il is input file. 
      ilasm.exe can be found somewhere in Visual Studio or SDK directories.

      Reply
    Reply
  • Good stuff!

    Posted by Legacy on 01/23/2003 12:00am

    Originally posted by: JTeeuwen

    Glad you made these tuts. very usefull!
    I'm surprised by the appearant high-level approach they took with MSIL. One would expect it to be much more Assembly like then it really is.. ah well, makes it all the more easy to grasp :)

    Nice work!

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: May 7, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT This eSeminar will explore three popular games engines and how they empower developers to create exciting, graphically rich, and high-performance games for Android® on Intel® Architecture. Join us for a deep dive as experts describe the features, tools, and common challenges using Marmalade, App Game Kit, and Havok game engines, as well as a discussion of the pros and cons of each engine and how they fit into your development …

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds