MSIL 教程

在网上发现了一个非常好的MSIL教程,可惜是英文版的,于是就翻译了一下,与大家共享,
原文http://www.codeguru.com/Csharp/.NET/net_general/il/article.php/c4635,初次翻译文章,请大家指正。

介绍

微软中间语言 (MSIL) 是一种语言,是许多编译器(C#,VB.NET等)的输出. ILDasm (中间语言反汇编器)程序和.Net Framework SDK(FrameworkSDK/Bin/ildasm.exe)打包在一起,让用户以人可阅读的格式查看MSIL代码。通过该工具,我们可以打开任何.net可执行文件(exe或dll)并查看其MSIL代码。

ILAsm 程序(中间语言编译器)从MSIL语言生成可执行文件。我们可以在WINNT / Microsoft.NET /Framework /vn.nn.nn目录中找到这个程序。

许多Visual C++程序员开始.net开发是因为他们对.NET框架的底层发生了一些什么感兴趣。学习MSIL给了用户理解某些对C#程序员或VB.NET程序员来说是透明的东西的机会。通晓MSIL给.NET程序员更多的能力。我们从不需要直接用MSIL编写程序,但是在某些情况下是非常有用的,我们可以用ILDasm打开程序的MSIL代码,查看它到底做了一些什么。

一个Doc格式的MSIL参考对.NET开发人员来说比较有用,它也许可以在Framework SDK目录下找到:

  • FrameworkSDK/Tool Developers Guide/docs/Partition II Metadata.doc (元数据定义和术语). 在这个文件中,我发现了所有MSIL指令的说明,例如.entrypoint, .locals等.
  • FrameworkSDK/Tool Developers Guide/docs/Partition III CIL.doc (CIL命令集)包含了一个MSIL命令的完整列表。

在工作中,我也用到了一个MSDN的ILDAsm教程,一篇2001年5月由John Robbins发表在MSDN杂志的优秀的文章: "ILDASM is Your New Best Friend"。

我想学习一门语言最好的途径就是用它写一些程序,所以我决定写一些小的MSIL程序。实际上,我们有写这些代码——是C#编译器生成的,我只是做一了一些小的更改,并加了许多注释以描述MSIL是如何工作的。

通过阅读附在本文的例子可以帮助.NET程序员理解中间语言,帮助其在需要的时候更易读懂MSIL代码。


一般信息

在MSIL中,所有的操作都在栈上完成。当调用一个函数的时候,其参数和局部变量都被分配到栈上。函数的代码从该栈开始,把一些值压入栈,对这些值进行一些操作,从栈上取出值。

执行MSIL名利和函数由3个步骤完成:

1.      把命令操作数和函数参数压入栈。

2.      执行命令或者调用函数。命令或函数从栈中取出他们的操作数(参数)并把他们压入结果栈 (返回值)。

3.      从栈中读取结果值。

步骤1~3是可选的,例如,void函数不会压入一个结果值到栈。

栈包含值类型对象和引用类型对象的引用。引用类型对象本身保存在堆中。

用来把一个值压入栈中的MSIL命令是ld... (装载),用来从栈中取出值的命令是st... (存储),因为值都存在变量中。我们可以把入栈操作叫做装载,出栈操作叫做存储。


示例项目

本文附上的代码中包含了许多用MSIL写的控制台程序. 如果需要编译他们,请确定ILAsm程序可以通过PATH访问。每个项目都是一个Visual Studio解决方案,IL源文件可以用VS的文本编辑器打开,Build命令运行ILAsm 程序在项目所在目录生成exe文件,run命令执行该文件。在每个程序的末尾,我加了几行代码,他们可以用C#来写:

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

    Console.Read();

这样,当从Windows Explorer运行的时候,就可以看到程序的输出。

下面是所含项目的列表:

1.      打印字符串—打印字符传到控制台。

2.      赋值—给一个int变量赋值并把它打印到控制台。

3.      运算—从控制台读取2个数字,惊醒+,-和乘的操作,并显示结果。

4.      数组— 分配一个int类型的数组,给他的元素赋值,打印其元素和数组的长度。

5.      比较— 输入2个数字并打印出最小的那个。

6.      数组2— 用循环填充数组元素并打印某些元素。

7.      不安全代码— 使用unsafe指针访问数组元素。

8.      PInvoke— 调用Win32 API。

9.      — 和类一起工作。

10.   异常— 异常处理。

我假设你以在这所说的顺序阅读这些项目。在下面的项目描述中,我用程序来解释每一条MSIL命令,并给出一些代码片段。


打印字符串

PrintString 就是MSIL版的 Hello, World

在代码中用到的MSIL指令如下:

l          .entrypoint— 定义程序的入口点(该函数在程序启动的时候由.NET 运行库调用)

l          .maxstack— 定义函数代码所用堆栈的最大深度。C#编译器可以对每个函数设置准确的值, 在例子中,我把他设为8。

用到的MSIL命令如下:

  • ldstr string—把一个字符串常量装入堆栈。
  • call function(parameters)—调用静态函数。函数的参数必须在函数调用前装入堆栈。
  • pop— 取出栈顶的值。当我们不需要把值存入变量时使用。
  • ret— 从一个函数中返回。

调用静态函数很简单。我们把函数的参数压入堆栈,调用函数,然后从堆栈中读取函数的返回值(如果是非void函数)。Console.WriteLine 就是一个这样的函数。

下面是代码:

.assembly PrintString {}

/*

    Console.WriteLine("Hello, World)"

*/

.method static public void main() il managed

{

    .entrypoint             // 该函数是程序的入口

    .maxstack 8

    // *****************************************************

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

    // *****************************************************

    ldstr "Hello, World"        // 把字符串压入堆栈

    // 调用静态的System.Console.Writeline函数

    // (函数移除栈顶的字符串)

    call   void [mscorlib]System.Console::WriteLine

                                 (class System.String)

    // *****************************************************

    ldstr "Press Enter to continue"

    call   void [mscorlib]System.Console::WriteLine

                                 (class System.String)

    // 调用 System.Console.Read 函数

    call int32 [mscorlib]System.Console::Read()

    // pop 指令移除栈顶元素

    // (移除由Read()函数返回的数字

    pop

    // *****************************************************

    ret

}


赋值

该程序给一个变量赋与int值并把它打印到控制台窗口。

命令:

  • ldc.i4.n—把一个 32位的常量(n从0到8)装入堆栈
  • stloc.n— 把一个从堆栈中返回的值存入第n(n从0到8)个局部变量

代码:

.assembly XequalN {}

// int x;

// x = 7;

// Console.WriteLine(x);

.method static public void main() il managed

{

    .entrypoint

    .maxstack 8

    .locals init ([0] int32 x)  // 分配一个局部变量

    // *****************************************************

    // x = 7;

    // *****************************************************

    ldc.i4.7                    // 把常量装入堆栈

    stloc.0                     // 把堆栈中的值存入第0个变量

    // *****************************************************

    // Console.WriteLine(x);

    // *****************************************************

    ldloc.0                     // 把第0个变量转入堆栈

    call void [mscorlib]System.Console::WriteLine(int32)

    ret

}


数据运算

本程序从控制台读取2个数字,对它们进行简单的运算,然后显示结果。

命令:

  • add—2个值相加。命令的参数必须在调用前装入堆栈,该函数从堆栈中移除参数并把运算后的结果压入堆栈。
  • sub— 2个值相减。
  • mul— 2个值相乘。

代码片段:

.assembly Operations {}

/*

// 程序的C#代码:

            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:"       // 把字符装入堆栈

call       void [mscorlib]System.Console::WriteLine(string)

 

    // *****************************************************

    // s = Console.ReadLine();

    // *****************************************************

    call       string [mscorlib]System.Console::ReadLine()

    stloc.3                     // 把值存入第3个变量

 

    // *****************************************************

    // x = Int32.Parse(s);

    // *****************************************************

    ldloc.3                     // 把第3个变量装入堆栈

 

    // 调用 System.Int32::Parse(string)函数

    // 把字符串从堆栈中移除并把解析的结果——int值压入堆栈

    call       int32 [mscorlib]System.Int32::Parse(string)

 

    stloc.0                     // 把值存入第0个变量

 

    // *****************************************************

    // 和变量y的一些运算

    // *****************************************************

    ldstr      "Enter y:"

               // 装入字符串

    call       void [mscorlib]System.Console::WriteLine(string)

               // 调用

    call       string [mscorlib]System.Console::ReadLine()

               // 调用

    stloc.3

               //把值存入第3个变量

    ldloc.3

               //把第3个变量装入堆栈

    call       int32 [mscorlib]System.Int32::Parse(string)

               // 调用

    stloc.1

               //把值存入第1个变量

 

    // *****************************************************

    // z = x + y;

    // *****************************************************

    ldloc.0             //把第0个变量装入堆栈

    ldloc.1             //把第1个变量装入堆栈

 

    // 把这2个值从堆栈中移除,把结果压入堆栈

add

 

    stloc.2             //把值存入第2个变量

 

    // *****************************************************

    // Console.Write("x + y = ");

    // *****************************************************

    ldstr      "x + y = "          // load string onto stack

    call       void [mscorlib]System.Console::Write(string)

 

    // *****************************************************

    // Console.Write(z);

    // *****************************************************

    ldloc.2                    //把第2个变量装入堆栈

    call       void [mscorlib]System.Console::Write(int32)

 

    // *****************************************************

    // Console.WriteLine("");

    // *****************************************************

    ldstr      ""                  //装入字符串

    call       void [mscorlib]System.Console::WriteLine(string)

 

    //相减和相乘运算过程与上面相同

 

    ret

}

 

休息一下
====================================================================

 

MSIL 教程(二):数组、分支、循环、使用不安全代码和如何调用Win32 API

 

续上文【翻译】MSIL 教程(一) ,本文继续讲解数组、分支、循环、使用不安全代码和如何调用Win32 API

数组

本程序分配一个int型的数组并给他的元素赋值,然后打印出元素和数组的长度。

命令:

  • newarr type— 生成一个元素类型为type 的数组。数组的大小必须在调用该命令前装入堆栈。该命令会把一个数组的引用装入堆栈。
  • stelem.i4— 给一个数组成员赋值。数组的引用、下标和值必须在调用该命令前装入堆栈。
  • ldelema type— 把数组元素的地址装入堆栈。数组的引用和下标必须在调用该命令前装入堆栈。地址用来调用非静态函数(参见后面)。
  • ldlen—把数组的长度装入堆栈。数组的引用必须在调用该命令前装入堆栈。
  • ldloca.s variable— 把变量的地址装入堆栈。
  • ldc.i4.s value— 把一个Int32的常量装入堆栈(用于大于8位的数)。
  • conv.i4— 把堆栈中值转换成Int32类型。
  • call instance function(arguments)— 调用类的非静态函数。在调用一个非静态函数之前,我们必须把某个类的实例的地址和函数的参数装入堆栈。在本例中,地址通过ldelemaldloca 命令装入。

在本例的某些代码片段中,我写了一些注释,以说明堆栈在最后一个变量后的状态。在本例中,我们看到变量由编译器生成,该变量用来调用类的非静态函数。

代码:

.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)    // 由编译器生成

 

    // *****************************************************

    // x = new int[5];

    // *****************************************************

    ldc.i4.5                     // 把常量装入堆栈。

 

    // 生成数组,并把他的引用压入堆栈

    newarr     [mscorlib]System.Int32

 

    // 把数组从堆栈中取出,存入第0个局部变量中

    stloc.0

 

    // *****************************************************

    // x[0] = 10;

    // *****************************************************

    ldloc.0           // 把第0个局部变量装入堆栈(数组)

    ldc.i4.0          // 把常量0装入堆栈(下标)

    ldc.i4.s   10     // 把常量10装入堆栈(值)

    stelem.i4         // array[index] = value

 

    // 对数组的其余元素进行同样的操作……

 

    // ***************************************************

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

    // ***************************************************

    ldstr      "x[0] = "            

                // 堆栈:"x[0] = "  (堆栈由局部变量表示)

    ldloc.0                         // 把第0个变量装入堆栈

    ldc.i4.0                        // 把第1个变量装入堆栈

                // 堆栈: "x[0] = " -> x -> 0

    // 把元素的地址装入堆栈

    ldelema    [mscorlib]System.Int32

                // 堆栈: "x[0] = " -> 指向一个Int32的指针

                // 10

    // 调用实例函数System.Int32::ToString().

    call       instance string [mscorlib]System.Int32::ToString()

                // 堆栈: "x[0] = " -> "10"

    // 调用静态函数System.String::Concat(string, string)

    call       string [mscorlib]System.String::Concat

                                               (string, string)

                // 堆栈: "x[0] = 10"

    // 调用静态函数 System.Console::WriteLine(string)

    call       void [mscorlib]System.Console::WriteLine(string)

                // 堆栈: 空

 

    //对数组的其余元素进行同样的操作……

 

    // *****************************************************

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

    // *****************************************************

    ldstr      "Array length = "  

                // 堆栈: "Array length = "

    ldloc.0     // 把第0个变量装入堆栈

                // 堆栈: "Array length = " -> x

    Ldlen       // 把数组的长度装入堆栈

                // 堆栈: "Array length = " -> 5

    conv.i4     // 把栈顶的值转换为Int32,并把他装入堆栈

                // 堆栈: "Array length = " -> 5

    stloc.1     // 把刚才的值存入第1个局部变量(tmp)

                // 堆栈: "Array length = "

    ldloca.s   tmp    //把变量tmp的地址装入堆栈

                // 堆栈: "Array length = " -> &tmp

    call       instance string [mscorlib]System.Int32::ToString()

                // 堆栈: "Array length = " -> "5"

    call       string [mscorlib]System.String::Concat

                                       (string, string)

                // 堆栈: "Array length = 5"

    call       void [mscorlib]System.Console::WriteLine(string)

                // 堆栈: 空

    ret

}


比较

本程序读取2个数字并打印其最小值。

命令:

  • bge.s label—跳转至label 如果value1≥value 2. Values 1和 2 必须在调用本命令前装入堆栈。
  • br.s label—跳转至label
  • box value type— 把一个值类型转成一个Object,并把该Object的引用装入堆栈。

本程序的装箱由如下C#程序引起: Console.WriteLine("{0:d}", z);
用这种形式就不会引起装箱: Console.WriteLine(z.ToString());.

代码:

.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:"               // 把字符串装入堆栈

    call  void [mscorlib]System.Console::WriteLine(string)

 

    // *****************************************************

    // s = Console.ReadLine();

    // *****************************************************

    call       string [mscorlib]System.Console::ReadLine()

    stloc.3                             // 保存到第3个变量

 

    // *****************************************************

    // x = Int32.Parse(s);

    // *****************************************************

    ldloc.3                             // 把第3个变量装入堆栈

    call       int32 [mscorlib]System.Int32::Parse(string)

    stloc.0                             // 保存到第0个变量

 

    // 对y进行相同的操作……

 

    // *****************************************************

    // 分支

    // if ( x >= y ) goto L_GR;

    // *****************************************************

    ldloc.0                     // 把x装入堆栈(value 1)

    ldloc.1                     // 把y装入堆栈(value 2)

    bge.s  L_GR                 // 跳转到 L_GR 如果value1≥value2

 

    // *****************************************************

    // z = x

    // *****************************************************

    ldloc.0                     // 把第0个变量装入堆栈

    stloc.2                     // 保存到第2个变量

 

    br.s       L_CONTINUE       // 跳转至 L_CONTINUE

 

L_GR:

 

    // *****************************************************

    // z = y

    // *****************************************************

    ldloc.1             // 把第1个变量装入堆栈

    stloc.2             // 保存到第2个变量

 

L_CONTINUE:

 

    // *****************************************************

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

    // 注意:这一行引起装箱操作

    // *****************************************************

    ldstr      "{0:d}"  // 把字符串装入堆栈

    ldloc.2             // 把第2个变量装入堆栈 (z)

    box       [mscorlib]System.Int32   // 把Int32变为Object

    call  void [mscorlib]System.Console::WriteLine(string, object)

 

    ret

}


数组2(循环)

本程序用循环填充一个数组并打印其元素。这一次,我们增加一个静态函数ShowNumber(int), 它在main函数中调用。

命令:

  • blt.s label—跳转到label 如果value 1小于 value 2. Values 1 和 2 必须在调用本命令之前装入堆栈。
  • ldelem.i4— 把一个数组元素装入堆栈。数组引用和下标必须在调用本命令之前装入堆栈。
  • ldarga.s argument— 把函数参数的地址装入堆栈。

我们可以看到,在本程序中,for 循环在MSIL中用标签来实现。

代码:

.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                      // 把常量装入堆栈

    newarr     [mscorlib]System.Int32   // 分配一个Int32型的数组

    stloc.0                             // 把它存入第0个变量

 

    // *****************************************************

    // i = 1

    // *****************************************************

    ldc.i4.1                    //把常量装入堆栈

    stloc.1                     //把它存入第1个变量

 

    br.s       CHECK_COUNTER    // 跳转到 CHECK_COUNTER

 

START_LOOP:

    // *****************************************************

    // px[i] = i + 1;

    // *****************************************************

    ldloc.0                     //  把第0个变量装入堆栈

                                // 堆栈: px

    ldloc.1                     // 把第1个变量装入堆栈

                                //堆栈; px -> i

    ldloc.1                     //把第1个变量装入堆栈

                                //堆栈: px -> i -> i

    ldc.i4.1                    //把常量装入堆栈

                                //堆栈: px -> i -> i -> 1.

    add                         // 2个值相加

                                //堆栈: px -> i -> i+1

                                //        (array,index,value)

    stelem.i4                   // 把值存入数组元素

                                //堆栈[index] = value

                                //堆栈: 空

    // *****************************************************

    // i = i + 1

    // *****************************************************

    ldloc.1                     //把第1个变量装入堆栈

    ldc.i4.1                    //把常量装入堆栈

    add                         // 相加

    stloc.1                     // 把值存入把第1个变量

 

CHECK_COUNTER:

    // *****************************************************

    // 如果 i < 100 跳转到循环开始的地方

    // *****************************************************

    ldloc.1                     // 把第1个变量装入堆栈

    ldc.i4.s   100              // 把常量装入堆栈

    blt.s      START_LOOP       // 如果value1<value2调转至START_LOOP

 

    // *****************************************************

    // ShowNumber(px[5]

    // *****************************************************

    ldloc.0                     // 把第0个变量装入堆栈

                                // (array)

    ldc.i4.5                    // 把常量装入堆栈

                                // (index)

    ldelem.i4                   // 把数组元素装入堆栈

    call       void ShowNumber(int32)   // 调用 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          // 把第n个参数的地址装入堆栈

  call       instance string [mscorlib]System.Int32::ToString()

  call       void [mscorlib]System.Console::WriteLine(string)

 

  ret

}


不安全代码

本程序通过unsafe指针填充和打印一个int型数组。

在本程序中,我们将看到新的类型:int32*int32&。使用关键字pinned 可以阻止GC移动由局部指针变量指向的对象。

命令:

  • dup—在堆栈上复制一个值。
  • stind.i4—存储值的地址。地址和值必须在调用本命令之前装入堆栈。

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不会移动该指针指向的对象

 

    // *****************************************************

    // nArray = new int[5];

    // *****************************************************

    ldc.i4.5                            // 把常量5装入堆栈                                       

    newarr     [mscorlib]System.Int32   // 生成数组 Int32[5]

    stloc.0                             // 存入第0个变量

 

    // *****************************************************

    // pArray = nArray    (pArray = &nArray[0])

    // *****************************************************

    ldloc.0

               //把第0个变量装入堆栈(array)

    ldc.i4.0

               //把常量0装入堆栈(index)

    ldelema    [mscorlib]System.Int32

               // 把array[index]装入堆栈

    stloc.3

               //存入第3个局部变量

 

    // *****************************************************

    // pCurrent = pArray;

    // *****************************************************

    ldloc.3                     //把第3个变量装入堆栈

    conv.i                      // 转变为int

    stloc.2                     //存入第2个变量

 

    // *****************************************************

    // i = 0

    // *****************************************************

    ldc.i4.0                    //把常量0装入堆栈

    stloc.1                     //存入第1个变量

 

    // *****************************************************

    // 跳转到 CHECK_COUNTER

    // *****************************************************

    br.s       CHECK_COUNTER

 

START_LOOP:

    // *****************************************************

    // *pCurrent++ = i + 1                             

    // *****************************************************

    // 1) 保存pCurrent到堆栈,然后累加pCurrent

    ldloc.2

          //把第2个变量装入堆栈            [pCurrent]

    dup

          // 复制栈顶的值

          //                                [pCurrent pCurrent]

    ldc.i4.4

          // 把常量4装入堆栈               [pCurrent pCurrent 4]

    add

          // 相加                           [pCurrent pCurrent + 4]

    stloc.2

          // 存入第2个变量                 [pCurrent]

         // 译注:因为int型指针是4位的,所以加pCurrent+4==*pCurrent++

 

    // 2) 把 (i+1) 保存到pCurrent

    ldloc.1

          // 把第1个变量装入堆栈           [pCurrent i]

    ldc.i4.1

          //把常量1装入堆栈                [pCurrent i 1]

    add   // 相加                           [pCurrent i+1]

   //                                 地址     值

    stind.i4

          // 把i+1的值的地址存入pCurrent   [empty]

 

    // *****************************************************

    // i = i + 1

    // *****************************************************

    ldloc.1             // 把第1个变量装入堆栈

    ldc.i4.1            // 把常量1装入堆栈

    add                 // 相加

    stloc.1             // 存入第1个变量

 

CHECK_COUNTER:

 

    // *****************************************************

    // 如果i < 5 跳转至 START_LOOP;

    // *****************************************************

    ldloc.1                     // 把第1个变量装入堆栈

    ldc.i4.5                    // 把常量5装入堆栈

    blt.s      START_LOOP       // 如果i<5跳转至START_LOOP

 

    // *****************************************************

    // pArray = 0               fixed 块结束

    // *****************************************************

    ldc.i4.0                    // 把常量0装入堆栈

    conv.u                      // 转变为unsigned int,并压入堆栈

    stloc.3                     // 存入第3个变量

 

    // 打印数组元素……

 

    ret

}


PInvoke

本程序使用Win32 API GetComputerName 和 MessageBox 显示计算机的名字。API的MSIL声明形式如下:

.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

{

}

其调用规则与其他函数一致。

休息一下

==============================================

MSIL 教程(三):类和异常处理

续上文【翻译】MSIL 教程(二):数组、分支、循环、使用不安全代码和如何调用Win32 API ,本文继续讲解类和异常处理。谨以这三篇译文纪念29年前的今日,那个让母亲今生难以忘记的幸福而又痛苦的日子。

在前面的程序中,我们在Main函数中调用类函数,在本程序中,我们将徐希如何定义类。本程序包含2个类: Class1和SampleClass,Class1带有函数Main,在Main中生成SampleClass的一个实例。

指令:

  • .field—定义类成员。和关键字public、private、static等一起使用。

命令:

  • stsfld static field—用堆栈中的值替换静态字段的值。
  • ldfld field—把一个非静态字段装入堆栈。类实例的地址必须在调用本命令之前装入堆栈。
  • ldarg.n—把第n个参数装入堆栈。在非静态函数中,第0个参数是一个隐含的参数,代表this。
  • newobj constructor—用构造函数constructor生成一个类的实例。构造函数的参数必须在调用本函数之前先装入堆栈。一个类的实例会被生成并装入堆栈。
  • callvirt instance function—调用一个对象的后期绑定方法。

代码:

.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 voidMain(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;

 

    // 该私有静态构造函数由编译器生成

    // (用以初始化类的静态成员)

    .method private hidebysig specialname rtspecialname static

        void  .cctor() cil managed

    {

        .maxstack  8

 

        // *************************************************

        // nStatic = 10

        // *************************************************

        ldc.i4.s 10            // 把常量装入堆栈

        // stsfld 命令把静态字段的值替换成堆栈中的值

        stsfld     int32 SampleClass::nStatic

 

        ret

    }

 

    // 构造函数

    // public SampleClass(int n, string s)

    //

    .method public hidebysig specialname rtspecialname

        instance void  .ctor(int32 n, string s) cil managed

    {

        .maxstack  8

 

        // *************************************************

        // 调用基类的构造函数

        // *************************************************

        ldarg.0         // 把第0个参数装入堆栈(隐含指针this)

        // 调用类Object的构造函数

        call       instance void [mscorlib]System.Object::.ctor()

 

        // *************************************************

        // m_n = n

        // *************************************************

        ldarg.0         // 把第0个参数装入堆栈(隐含指针this)

        ldarg.1         // 把第1个参数装入堆栈(n)

        // 把n的值存入this.m_n

        stfld      int32 SampleClass::m_n

 

        // *************************************************

        // m_s = s

        // *************************************************

        ldarg.0         //把第0个参数装入堆栈(隐含指针this)

        ldarg.2         //把第2个参数装入堆栈(s)

        // 把s的值存入this.m_s

        stfld      string SampleClass::m_s

 

        ret

    }

 

    // 数字型属性

    .property instance int32 Number()

    {

        // 调用 get_Number

        .get instance int32 SampleClass::get_Number()

    }

 

    .method public hidebysig specialname instance int32

        get_Number() cil managed

    {

        .maxstack  8

 

        // 由编译器生成的变量

        // 译注:实际上,只有Debug版的才有,Release版的就直接返回m_n

        .locals ([0] int32 tmp)

 

        // *************************************************

        // 返回 m_n;

        // *************************************************

        ldarg.0

                  // 装入第0个参数(this)

        ldfld      int32 SampleClass::m_n

                  // 装入由堆栈栈顶指针指向的对象的字段

        stloc.0

                  // 存入第0个变量

        ldloc.0

                  // 把第0个变量装入堆栈(函数的返回值)

        ret

    }

 

    // 字符型属性

    .property instance string String()

    {

        .get instance string SampleClass::get_String()

    }

 

    .method public hidebysig specialname instance string

            get_String() cil managed

    {

        .maxstack  8

 

        // 由编译器生成的变量

        .locals ([0] string tmp)

 

        ldarg.0

              // 装入第0个参数(this)

        ldfld      string SampleClass::m_s

             // 装入由堆栈栈顶指针指向的对象的字段

        stloc.0

             // 存入第0个变量

        ldloc.0

             // 把第0个变量装入堆栈(函数的返回值)

        ret

    }

}

 

 

.class private auto ansi beforefieldinit Class1

       extends [mscorlib]System.Object

{

    // public的缺省构造函数

    .method public hidebysig specialname rtspecialname

        instance void  .ctor() cil managed

    {

        .maxstack  8

 

        // *************************************************

        // 调用基类构造函数

        // *************************************************

        ldarg.0

                          // 装入thisr

        call       instance void [mscorlib]System.Object::.ctor()

                          // 类Objectr的构造函数

 

        ret

    }

 

    // Main 函数

    .method private hidebysig static void Main(string[] args)

            cil managed

    {

        // 本方法为程序的入口点

        .entrypoint

 

        // 自定义属性

        .custom instance void [mscorlib]System.

                STAThreadAttribute::.ctor() = ( 01 00 00 00 )

 

        .maxstack  8

 

        .locals ([0] class SampleClass o,

                 [1] int32 tmp)          // 由编译器生成

 

        // *************************************************

        // o = new SampleClass(1, "Sample");

        // *************************************************

        ldc.i4.1                        // 把常量1装入堆栈

        ldstr      "Sample"             // 把字符常量装入堆栈

        // 通过传入堆栈中的2个参数生成一个SampleClass的对象,

        // 并把他装入堆栈

        newobj     instance void SampleClass::.ctor(int32, string)

        stloc.0                         // 存入第0个变量

 

        // *************************************************

        // 访问静态类成员

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

        // *************************************************

 

        //把静态字段的地址装入堆栈

        ldsflda    int32 SampleClass::nStatic

        // 为堆栈中的对象调用Int32::ToString

        call       instance string [mscorlib]System.Int32

                            ::ToString()

        // 调用静态的WriteLine,其传入参数是堆栈中的字符串

        call       void [mscorlib]System.Console

                   ::WriteLine(string)

 

        // *************************************************

        // 调用实例函数

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

        // *************************************************

        ldloc.0                 // 装入第0个变量

        // 调用堆栈中对象的函数

        call   instance int32 SampleClass::get_Number()

        stloc.1                         // 存入第1个变量

        ldloca.s  tmp                   // 把地址装入堆栈

        call       instance string [mscorlib]System.Int32

                            ::ToString()

        call       void [mscorlib]System.Console

                        ::WriteLine(string)

 

        // *************************************************

        // 调用实例函数

        // 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

    }

}


异常处理

本程序使2个数相除,捕捉其除0异常。try/catch 块在MSIL中看起来像C#中的一样。

命令:

  • leave.s label—离开try/catch等保护块。

代码:

.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)

 

    //输入 x, y ...

 

    .try

    {

        // *************************************************

        // z = x / y;

        // *************************************************

        ldloc.0                 // 装入第0个变量

        ldloc.1                 // 装入第1个变量

        div                     // 相除

        stloc.2                 // 把结果存入第2个变量

 

        // *************************************************

        // Console.WriteLine(z.ToString());

        // *************************************************

        ldloca.s   z            // 装入z的地址

        call       instance string [mscorlib]System.Int32

                                   ::ToString()

        call       void [mscorlib]System.Console

                                   ::WriteLine(string)

 

        leave.s    END_TRY_CATCH        // 退出try

    }

    catch [mscorlib]System.Exception

    {

        stloc.s    e        // 存入由堆栈抛出的异常

 

        // *************************************************

        // 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        // 退出catch块

    }

 

END_TRY_CATCH:

 

    ret

}

Downloads

Download source - 43 Kb

转自:http://blog.csdn.net/mallva/article/details/2215976

转自:http://www.cnblogs.com/Yahong111/archive/2007/08/16/857771.html


你可能感兴趣的:(教程)