续上文【翻译】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 如果value1value 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 如果value1value2

 

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

    // 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 显示计算机的名字。APIMSIL声明形式如下:

.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

{

}

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


未完待续。
posted on 2007-08-16 09:28  永红  阅读(3433)  评论(6编辑  收藏  举报