您的位置:首页 > 编程语言

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

2007-08-16 09:28 1016 查看
续上文【翻译】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

{

}

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

未完待续。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐