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)— 调用类的非静态函数。在调用一个非静态函数之前,我们必须把某个类的实例的地址和函数的参数装入堆栈。在本例中,地址通过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
.locals init ([] int32[] x,
[] int32 tmp) // 由编译器生成
// *****************************************************
// x = new int[5];
// *****************************************************
ldc.i4. // 把常量装入堆栈。
// 生成数组,并把他的引用压入堆栈
newarr [mscorlib]System.Int32
// 把数组从堆栈中取出,存入第0个局部变量中
stloc.
// *****************************************************
// x[0] = 10;
// *****************************************************
ldloc. // 把第0个局部变量装入堆栈(数组)
ldc.i4. // 把常量0装入堆栈(下标)
ldc.i4.s // 把常量10装入堆栈(值)
stelem.i4 // array[index] = value
// 对数组的其余元素进行同样的操作……
// ***************************************************
// Console.WriteLine("x[0] = " + x[0].ToString());
// ***************************************************
ldstr "x[0] = "
// 堆栈:"x[0] = " (堆栈由局部变量表示)
ldloc. // 把第0个变量装入堆栈
ldc.i4. // 把第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个变量装入堆栈
// 堆栈: "Array length = " -> x
Ldlen // 把数组的长度装入堆栈
// 堆栈: "Array length = " -> 5
conv.i4 // 把栈顶的值转换为Int32,并把他装入堆栈
// 堆栈: "Array length = " -> 5
stloc. // 把刚才的值存入第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
.locals init ([] int32 x,
[] int32 y,
[] int32 z,
[] 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个变量
// *****************************************************
// x = Int32.Parse(s);
// *****************************************************
ldloc. // 把第3个变量装入堆栈
call int32 [mscorlib]System.Int32::Parse(string)
stloc. // 保存到第0个变量
// 对y进行相同的操作……
// *****************************************************
// 分支
// if ( x >= y ) goto L_GR;
// *****************************************************
ldloc. // 把x装入堆栈(value 1)
ldloc. // 把y装入堆栈(value 2)
bge.s L_GR // 跳转到 L_GR 如果value1≥value2
// *****************************************************
// z = x
// *****************************************************
ldloc. // 把第0个变量装入堆栈
stloc. // 保存到第2个变量
br.s L_CONTINUE // 跳转至 L_CONTINUE
L_GR:
// *****************************************************
// z = y
// *****************************************************
ldloc. // 把第1个变量装入堆栈
stloc. // 保存到第2个变量
L_CONTINUE:
// *****************************************************
// Console.WriteLine("{0:d}", z);
// 注意:这一行引起装箱操作
// *****************************************************
ldstr "{0:d}" // 把字符串装入堆栈
ldloc. // 把第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
.locals init ([] int32[] px,
[] int32 i)
// *****************************************************
// x = new int[100]
// *****************************************************
ldc.i4.s // 把常量装入堆栈
newarr [mscorlib]System.Int32 // 分配一个Int32型的数组
stloc. // 把它存入第0个变量
// *****************************************************
// i = 1
// *****************************************************
ldc.i4. //把常量装入堆栈
stloc. //把它存入第1个变量
br.s CHECK_COUNTER // 跳转到 CHECK_COUNTER
START_LOOP:
// *****************************************************
// px[i] = i + 1;
// *****************************************************
ldloc. // 把第0个变量装入堆栈
// 堆栈: px
ldloc. // 把第1个变量装入堆栈
//堆栈; px -> i
ldloc. //把第1个变量装入堆栈
//堆栈: px -> i -> i
ldc.i4. //把常量装入堆栈
//堆栈: px -> i -> i -> 1.
add // 2个值相加
//堆栈: px -> i -> i+1
// (array,index,value)
stelem.i4 // 把值存入数组元素
//堆栈[index] = value
//堆栈: 空
// *****************************************************
// i = i + 1
// *****************************************************
ldloc. //把第1个变量装入堆栈
ldc.i4. //把常量装入堆栈
add // 相加
stloc. // 把值存入把第1个变量
CHECK_COUNTER:
// *****************************************************
// 如果 i < 100 跳转到循环开始的地方
// *****************************************************
ldloc. // 把第1个变量装入堆栈
ldc.i4.s // 把常量装入堆栈
blt.s START_LOOP // 如果value1<value2调转至START_LOOP
// *****************************************************
// ShowNumber(px[5]
// *****************************************************
ldloc. // 把第0个变量装入堆栈
// (array)
ldc.i4. // 把常量装入堆栈
// (index)
ldelem.i4 // 把数组元素装入堆栈
call void ShowNumber(int32) // 调用 ShowNumber
// *****************************************************
// ShowNumber(px[10]
// *****************************************************
ldloc.
ldc.i4.s
ldelem.i4
call void ShowNumber(int32)
ret
}
.method static public void ShowNumber(int32 n) il managed
{
.maxstack
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
.locals ([] int32[] nArray,
[] int32 i,
[] int32* pCurrent,
[] int32& pinned pArray) // GC不会移动该指针指向的对象
// *****************************************************
// nArray = new int[5];
// *****************************************************
ldc.i4. // 把常量5装入堆栈
newarr [mscorlib]System.Int32 // 生成数组 Int32[5]
stloc. // 存入第0个变量
// *****************************************************
// pArray = nArray (pArray = &nArray[0])
// *****************************************************
ldloc.
//把第0个变量装入堆栈(array)
ldc.i4.
//把常量0装入堆栈(index)
ldelema [mscorlib]System.Int32
// 把array[index]装入堆栈
stloc.
//存入第3个局部变量
// *****************************************************
// pCurrent = pArray;
// *****************************************************
ldloc. //把第3个变量装入堆栈
conv.i // 转变为int
stloc. //存入第2个变量
// *****************************************************
// i = 0
// *****************************************************
ldc.i4. //把常量0装入堆栈
stloc. //存入第1个变量
// *****************************************************
// 跳转到 CHECK_COUNTER
// *****************************************************
br.s CHECK_COUNTER
START_LOOP:
// *****************************************************
// *pCurrent++ = i + 1
// *****************************************************
// 1) 保存pCurrent到堆栈,然后累加pCurrent
ldloc.
//把第2个变量装入堆栈 [pCurrent]
dup
// 复制栈顶的值
// [pCurrent pCurrent]
ldc.i4.
// 把常量4装入堆栈 [pCurrent pCurrent 4]
add
// 相加 [pCurrent pCurrent + 4]
stloc.
// 存入第2个变量 [pCurrent]
// 译注:因为int型指针是4位的,所以加pCurrent+4==*pCurrent++
// 2) 把 (i+1) 保存到pCurrent
ldloc.
// 把第1个变量装入堆栈 [pCurrent i]
ldc.i4.
//把常量1装入堆栈 [pCurrent i 1]
add // 相加 [pCurrent i+1]
// 地址 值
stind.i4
// 把i+1的值的地址存入pCurrent [empty]
// *****************************************************
// i = i + 1
// *****************************************************
ldloc. // 把第1个变量装入堆栈
ldc.i4. // 把常量1装入堆栈
add // 相加
stloc. // 存入第1个变量
CHECK_COUNTER:
// *****************************************************
// 如果i < 5 跳转至 START_LOOP;
// *****************************************************
ldloc. // 把第1个变量装入堆栈
ldc.i4. // 把常量5装入堆栈
blt.s START_LOOP // 如果i<5跳转至START_LOOP
// *****************************************************
// pArray = 0 fixed 块结束
// *****************************************************
ldc.i4. // 把常量0装入堆栈
conv.u // 转变为unsigned int,并压入堆栈
stloc. // 存入第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
{
}
其调用规则与其他函数一致。
MSIL 教程(二):数组、分支、循环、使用不安全代码和如何调用Win32 API(转)的更多相关文章
- MSIL 教程(三):类和异常处理(转)
转自:http://www.cnblogs.com/Yahong111/archive/2007/08/16/857771.html 续上文[翻译]MSIL 教程(二):数组.分支.循环.使用不安全代 ...
- JavaScript数组forEach循环
JavaScript数组forEach循环 今天写JavaScript代码把forEach循环数组忘记写法了,在此记录一下以防止未来忘记. let a = [1, 2, 3]; a.forEach(f ...
- C# 互操作性入门系列(二):使用平台调用调用Win32 函数
好文章搬用工模式启动ing ..... { 文章中已经包含了原文链接 就不再次粘贴了 言明 改文章是一个系列,但只收录了2篇,原因是 够用了 } --------------------------- ...
- [转]C# 互操作性入门系列(二):使用平台调用调用Win32 函数
传送门 C#互操作系列文章: C# 互操作性入门系列(一):C#中互操作性介绍 C# 互操作性入门系列(二):使用平台调用调用Win32 函数 C# 互操作性入门系列(三):平台调用中的数据封送处理 ...
- java基础二 分支循环
分支循环: if... if...else... if...else if... if...else if...else... switch...case...defau ...
- 【Visual C++】游戏开发五十六 浅墨DirectX教程二十三 打造游戏GUI界面(一)
本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/16384009 作者:毛星云 ...
- 第三次实验计算分段函数 第四次计算分段函数和循环NEW 第五次分支+循环加强版 实验报告
一.实验题目,设计思路,实现方法 第四次分支+循环 加强版 (2-2计算个人所得税,2-7 装睡,2-8计算天数) 设计思路:2-2 用if-else的语句,与计算分段函数的题类似的做法:2-7 运用 ...
- 手把手教从零开始在GitHub上使用Hexo搭建博客教程(二)-Hexo参数设置
前言 前文手把手教从零开始在GitHub上使用Hexo搭建博客教程(一)-附GitHub注册及配置介绍了github注册.git相关设置以及hexo基本操作. 本文主要介绍一下hexo的常用参数设置. ...
- C#微信公众号开发系列教程二(新手接入指南)
http://www.cnblogs.com/zskbll/p/4093954.html 此系列前面已经更新了两篇博文了,都是微信开发的前期准备工作,现在切入正题,本篇讲解新手接入的步骤与方法,大神可 ...
随机推荐
- Atitit.如何避免公司破产倒闭的业务魔咒
Atitit.如何避免公司破产倒闭的业务魔咒 1. 大型公司的衰落或者倒闭破产案例1 1.1. 摩托罗拉1 1.2. 诺基亚2 1.3. sun2 2. 为什么他们会倒闭?? 常见的一些倒闭元素2 2 ...
- paip.语义分析--单字词名词表
paip.语义分析--单字名词表 INSERT INTO t (word) SELECT DISTINCT word FROM `word_main` where tsisin is not n ...
- MySQL分库分表总结参考
单库单表 单库单表是最常见的数据库设计,例如,有一张用户(user)表放在数据库db中,所有的用户都可以在db库中的user表中查到. 单库多表 随着用户数量的增加,user表的数据量会越来越大,当数 ...
- JavaWeb学习总结(二)——Tomcat服务器学习和使用(一)
一.Tomcat服务器端口的配置 Tomcat的所有配置都放在conf文件夹之中,里面的server.xml文件是配置的核心文件. 如果想修改Tomcat服务器的启动端口,则可以在server.xml ...
- 上海SAP代理商 电子行业ERP系统 SAP金牌代理商达策
上海SAP代理商 电子行业ERP系统 SAP金牌代理商达策上海达策为电子行业企业提供了多样的ERP信息化管理系统.基于多营运中心的管理架构体系,构造了以供应链.生产管理.财务一体化为核心,协同HR.B ...
- 强烈推荐android studio用的几个插件
http://blog.csdn.net/liang5630/article/details/46366901 android studio常用插件,可极大简化开发,增强开发效率. 不懂安装studi ...
- asp.net关于页面不回发的问题,寻求完美解决方案
原文地址:http://www.sufeinet.com/thread-4564-1-1.html 这个问题我相信有不少人见过,就是使用系统的分页功能时,或者是使用系统控件,都会有一个回发的功能, 这 ...
- 騰訊RTX的API開發,給RTX開個天窗
好多人可能沒聽說RTX這個軟件,在此我簡單說明一下,這個軟件是騰訊為企業開發的一個內部聊天軟件,服務端不是在騰訊那邊,而是需要企業自己安裝到自己公司內部的服務器上,以供企業內部員工交流使用,功能和QQ ...
- 通过修改host文件来允许和禁止主机的访问
通过修改host文件来允许和禁止主机的访问 修改/etc/hosts.deny,加入"sshd:ALL" 修改/etc/hosts.allow,加入"sshd:192.1 ...
- lampp 在linux ubuntu下自动开机启动
lampp 在linux ubuntu下自动开机启动 lampp在linux下是不会自动启动的.需要手工处理.如下: 假如,你的lampp安装在 /opt/lampp 目录下,那么可以如下处理: 1. ...