您好,欢迎来到保捱科技网。
搜索
您的当前位置:首页msil入门说明

msil入门说明

来源:保捱科技网
介绍

微软中间语言 (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指令如下:

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

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

数组

本程序分配一个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)— 调用类的非静态函数。在调用一个非静态函数之前,我们必须把某个类的实例的地址和函数的参数装入堆栈。在本例中,地址通过ldelema和ldloca 命令装入。

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

代码:

.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}\用这种形式就不会引起装箱: 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}\*/

.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}\// 注意:这一行引起装箱操作

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

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 { }

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

在前面的程序中,我们在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 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;

// 该私有静态构造函数由编译器生成 // (用以初始化类的静态成员)

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

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- baoaiwan.cn 版权所有 赣ICP备2024042794号-3

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务