msil入门说明.doc_第1页
msil入门说明.doc_第2页
msil入门说明.doc_第3页
msil入门说明.doc_第4页
msil入门说明.doc_第5页
已阅读5页,还剩23页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

介绍微软中间语言 (MSIL) 是一种语言,是许多编译器(C#,VB.NET等)的输出. ILDasm (中间语言反汇编器)程序和.Net Framework SDK(FrameworkSDKBinildasm.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目录下找到:FrameworkSDKTool Developers GuidedocsPartition II Metadata.doc (元数据定义和术语). 在这个文件中,我发现了所有MSIL指令的说明,例如.entrypoint, .locals等. FrameworkSDKTool Developers GuidedocsPartition 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. 从栈中读取结果值。步骤13是可选的,例如,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 mscorlibSystem.Console:WriteLine(class System.String)/ *ldstr Press Enter to continuecall void mscorlibSystem.Console:WriteLine(class System.String)/ 调用 System.Console.Read 函数call int32 mscorlibSystem.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 mscorlibSystem.Console:WriteLine(int32)ret数据运算本程序从控制台读取2个数字,对它们进行简单的运算,然后显示结果。命令:add2个值相加。命令的参数必须在调用前装入堆栈,该函数从堆栈中移除参数并把运算后的结果压入堆栈。 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 mscorlibSystem.Console:WriteLine(string)/ */ s = Console.ReadLine();/ *call string mscorlibSystem.Console:ReadLine()stloc.3 / 把值存入第3个变量/ */ x = Int32.Parse(s);/ *ldloc.3 / 把第3个变量装入堆栈/ 调用 System.Int32:Parse(string)函数/ 把字符串从堆栈中移除并把解析的结果int值压入堆栈call int32 mscorlibSystem.Int32:Parse(string)stloc.0 / 把值存入第0个变量/ */ 和变量y的一些运算/ *ldstr Enter y:/ 装入字符串call void mscorlibSystem.Console:WriteLine(string)/ 调用call string mscorlibSystem.Console:ReadLine()/ 调用stloc.3/把值存入第3个变量ldloc.3/把第3个变量装入堆栈call int32 mscorlibSystem.Int32:Parse(string)/ 调用stloc.1/把值存入第1个变量/ */ z = x + y;/ *ldloc.0 /把第0个变量装入堆栈ldloc.1 /把第1个变量装入堆栈/ 把这2个值从堆栈中移除,把结果压入堆栈addstloc.2 /把值存入第2个变量/ */ Console.Write(x + y = );/ *ldstr x + y = / load string onto stackcall void mscorlibSystem.Console:Write(string)/ */ Console.Write(z);/ *ldloc.2 /把第2个变量装入堆栈call void mscorlibSystem.Console:Write(int32)/ */ Console.WriteLine();/ *ldstr /装入字符串call void mscorlibSystem.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 int5;x0 = 10;x1 = 20;Console.WriteLine(x0 = + x0.ToString();Console.WriteLine(x1 = + x1.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 int5;/ *ldc.i4.5 / 把常量装入堆栈。/ 生成数组,并把他的引用压入堆栈newarr mscorlibSystem.Int32/ 把数组从堆栈中取出,存入第0个局部变量中stloc.0/ */ x0 = 10;/ *ldloc.0 / 把第0个局部变量装入堆栈(数组)ldc.i4.0 / 把常量0装入堆栈(下标)ldc.i4.s 10 / 把常量10装入堆栈(值)stelem.i4 / arrayindex = value/ 对数组的其余元素进行同样的操作/ */ Console.WriteLine(x0 = + x0.ToString();/ *ldstr x0 = / 堆栈:x0 = (堆栈由局部变量表示)ldloc.0 / 把第0个变量装入堆栈ldc.i4.0 / 把第1个变量装入堆栈/ 堆栈: x0 = - x - 0/ 把元素的地址装入堆栈ldelema mscorlibSystem.Int32/ 堆栈: x0 = - 指向一个Int32的指针/ 10/ 调用实例函数System.Int32:ToString().call instance string mscorlibSystem.Int32:ToString()/ 堆栈: x0 = - 10/ 调用静态函数System.String:Concat(string, string)call string mscorlibSystem.String:Concat(string, string)/ 堆栈: x0 = 10/ 调用静态函数 System.Console:WriteLine(string)call void mscorlibSystem.Console:WriteLine(string)/ 堆栈: 空/对数组的其余元素进行同样的操作/ */ Console.WriteLine(Array length = + x.Length.ToString();/ *ldstr Array length = / 堆栈: Array length = ldloc.0 / 把第0个变量装入堆栈/ 堆栈: Array length = - xLdlen / 把数组的长度装入堆栈/ 堆栈: Array length = - 5conv.i4 / 把栈顶的值转换为Int32,并把他装入堆栈/ 堆栈: Array length = - 5stloc.1 / 把刚才的值存入第1个局部变量(tmp)/ 堆栈: Array length = ldloca.s tmp /把变量tmp的地址装入堆栈/ 堆栈: Array length = - &tmpcall instance string mscorlibSystem.Int32:ToString()/ 堆栈: Array length = - 5call string mscorlibSystem.String:Concat(string, string)/ 堆栈: Array length = 5call void mscorlibSystem.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;elsez = 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 mscorlibSystem.Console:WriteLine(string)/ */ s = Console.ReadLine();/ *call string mscorlibSystem.Console:ReadLine()stloc.3 / 保存到第3个变量/ */ x = Int32.Parse(s);/ *ldloc.3 / 把第3个变量装入堆栈call int32 mscorlibSystem.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_CONTINUEL_GR:/ */ z = y/ *ldloc.1 / 把第1个变量装入堆栈stloc.2 / 保存到第2个变量L_CONTINUE:/ */ Console.WriteLine(0:d, z);/ 注意:这一行引起装箱操作/ *ldstr 0:d / 把字符串装入堆栈ldloc.2 / 把第2个变量装入堆栈 (z)box mscorlibSystem.Int32 / 把Int32变为Objectcall void mscorlibSystem.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 int100;int i;for ( i = 1; i 100; i+ )pxi = i + 1;ShowNumber(px5);ShowNumber(px10);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 int100/ *ldc.i4.s 100 / 把常量装入堆栈newarr mscorlibSystem.Int32 / 分配一个Int32型的数组stloc.0 / 把它存入第0个变量/ */ i = 1/ *ldc.i4.1 /把常量装入堆栈stloc.1 /把它存入第1个变量br.s CHECK_COUNTER / 跳转到 CHECK_COUNTERSTART_LOOP:/ */ pxi = i + 1;/ *ldloc.0 / 把第0个变量装入堆栈/ 堆栈: pxldloc.1 / 把第1个变量装入堆栈/堆栈; px - ildloc.1 /把第1个变量装入堆栈/堆栈: px - i - ildc.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 / 如果value1value2调转至START_LOOP/ */ ShowNumber(px5/ *ldloc.0 / 把第0个变量装入堆栈/ (array)ldc.i4.5 / 把常量装入堆栈/ (index)ldelem.i4 / 把数组元素装入堆栈call void ShowNumber(int32) / 调用 ShowNumber/ */ ShowNumber(px10/ *ldloc.0ldc.i4.s 10ldelem.i4call void ShowNumber(int32)ret.method static public void ShowNumber(int32 n) il managed.maxstack 1ldarga.s n / 把第n个参数的地址装入堆栈call instance string mscorlibSystem.Int32:ToString()call void mscorlibSystem.Console:WriteLine(string)ret不安全代码本程序通过unsafe指针填充和打印一个int型数组。在本程序中,我们将看到新的类型:int32* 和 int32&。使用关键字pinned 可以阻止GC移动由局部指针变量指向的对象。命令:dup在堆栈上复制一个值。 stind.i4存储值的地址。地址和值必须在调用本命令之前装入堆栈。 Code:.assembly Unsafe /*int nArray = new int5;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(nArrayi.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 int5;/ *ldc.i4.5 / 把常量5装入堆栈 newarr mscorlibSystem.Int32 / 生成数组 Int325stloc.0 / 存入第0个变量/ */ pArray = nArray (pArray = &nArray0)/ *ldloc.0/把第0个变量装入堆栈(array)ldc.i4.0/把常量0装入堆栈(index)ldelema mscorlibSystem.Int32/ 把arrayindex装入堆栈stloc.3/存入第3个局部变量/ */ pCurrent = pArray;/ *ldloc.3 /把第3个变量装入堆栈conv.i / 转变为intstloc.2 /存入第2个变量/ */ i = 0/ *ldc.i4.0 /把常量0装入堆栈stloc.1 /存入第1个变量/ */ 跳转到 CHECK_COUNTER/ *br.s CHECK_COUNTERSTART_LOOP:/ */ *pCurrent+ = i + 1 / */ 1) 保存pCurrent到堆栈,然后累加pCurrentldloc.2/把第2个变量装入堆栈 pCurrentdup/ 复制栈顶的值/ pCurrent pCurrentldc.i4.4/ 把常量4装入堆栈 pCurrent pCurrent 4add/ 相加 pCurrent pCurrent + 4stloc.2/ 存入第2个变量 pCurrent/ 译注:因为int型指针是4位的,所以加pCurrent+4=*pCurrent+/ 2) 把 (i+1) 保存到pCurrentldloc.1/ 把第1个变量装入堆栈 pCurrent ildc.i4.1/把常量1装入堆栈 pCurrent i 1add / 相加 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 / 如果i5跳转至START_LOOP/ */ pArray = 0 fixed 块结束/ *ldc.i4.0 / 把常量0装入堆栈conv.u / 转变为unsigned int,并压入堆栈stloc.3 / 存入第3个变量/ 打印数组元素retPInvoke 本程序使用Win32 API GetComputerName 和 MessageBox 显示计算机的名字。API的MSIL声明形式如下: .method public hidebysig static pinvokeimpl(kernel32.dllautochar winapi)int32 GetComputerName(class mscorlibSystem.Text.StringBui

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论