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


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

    续上文【翻译】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
    {
    }

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

  • 相关阅读:
    ORACLE CASE WHEN 及 SELECT CASE WHEN的用法
    Oracle中NVARCHAR2字符集不匹配问题
    数据库读写分离
    oracle存储过程获取异常信息码和异常信息
    js删除字符串的最后一个字符三种方法
    我弄的一些TASKER配置
    分享我用Taker做任务时需要的各种资源的精华帖,方便查阅
    Tasker 正则表达式测试器
    支持Tasker控制的app合集
    Tasker文件夹说明
  • 原文地址:https://www.cnblogs.com/anbylau2130/p/6074333.html
Copyright © 2020-2023  润新知