最近充能看书,在书上看到函数调用可以"通过ldftn获得函数指针,然后使用calli指令"来进行调用,并说这种行为"类似C的函数指针,但是C#不支持这种行为",那么这是一种什么样的调用呢?我翻阅了一些资料,才知道ldftn和calli分别是IL语言中的两个指令,也就是说这是一种基于IL语言的调用。
1.首先我们要实现的方法调用,首先我们有如下类:
(1)Ldftn指令:
-语法:ldftn
-功能:把函数指针加载到由MethodDef或MemberRef类型的
(2)Calli指令:
-语法:calli
-功能:先从栈上弹出函数指针,再从栈上弹出所有的参数,然后根据
★3.IL调用函数方法的一般流程:
一般在IL中使用方式分为两大类,一种是调用托管函数,另外一种是调用非托管的函数,这里我们暂不考虑对非托管函数的调用。对于托管函数,按照其调用方法是不是要引用一个对象实列,可以分为静态方法和实例方法。
接下来就对这两种类型的方法,分别描述一下IL代码的一般流程。
(1)实例方法:
过程如下:
-通过"newobjinstance构造函数签名"先创建一个实例对象,并将对象从栈顶弹出保存到局部变量
-通过"ldftninstance实例方法签名"来获得函数方法的指针,调用完成后这个指针会被放置于栈顶,同理也将栈顶的函数指针弹出并保存到局部变量
-把实例对象放置到栈顶,由于实例方法要this指向的对象,所以这个对象会作为第一个参数arg.0作为this
-按照函数的调用的参数的顺序,依次将变量放置到栈顶
-把函数的指针放置到栈顶
-通过"calliinstance实例方法签名"来进行函数的调用,调用完成之后会把返回值放置与栈顶,最后把栈顶的返回值弹出并保存使用
(2)静态方法:
静态方法不需要this指向的对象,所以这边也不需要先创建一个对象,过程如下:
-通过"ldftn静态方法签名"来获得函数方法的指针,调用完成后这个指针会被放置于栈顶,将栈顶的函数指针弹出并保存到局部变量
-通过"calli静态方法签名"来进行函数的调用,调用完成之后会把返回值放置与栈顶,最后把栈顶的返回值弹出并保存使用
(3)一个简单的实例调用的例子:
.localsinit(nativeintfnptr)...ldfrnvoid[mscorlib]System.Console::WriteLine(int32)stloc.0//本地变量中存储函数指针...ldc.i412345//加载参数ldloc.0callovoid(int32)...下面我们看一下怎么实现对Test和Test2的调用的,直接上菜...
IL代码如下:
.assemblyexternmscorlib{auto}.assemblyMyTest{}.moduleMyTest.exe.classpublicA{.methodpublicspecialnamevoid.ctor(){ldarg.0callinstancevoid[mscorlib]System.Object::.ctor()ret}.methodpublicvoidTest(int32param_0){ldarg.1callvoid[mscorlib]System.Console::WriteLine(int32)ret}.methodpublicstaticvoidTest2(int32param_0){ldarg.0callvoid[mscorlib]System.Console::WriteLine(int32)ret}}.methodpublicstaticvoidMain(){.entrypoint.locals(classAa,int32v_1)//实例方法调用newobjinstancevoidA::.ctor()stloc.0ldftninstancevoidA::Test(int32)stloc.1ldloc.0ldc.i4120ldloc.1calliinstancevoid(int32)//静态方法调用ldftnvoidA::Test2(int32)stloc.1ldc.i4233ldloc.1callivoid(int32)ret}四.C#用Emit类来实现C#代码:
Typetype=CreateTypeHelper.CreateMethodCallingType();//获得方法MethodInfomethodInfo=type.GetMethod("CalliMethodCall");if(methodInfo!=null){methodInfo.Invoke(null,null);}执行结果:
最后我们验证一下动态生成的类。在代码中,我们创建了一个"MethodCalling.dll"用来保存动态生成的类,里面承载了我们的Emit代码生成的IL代码,如下图:
用ILDasm.exe查看后如下图:
看了一下与我们写的IL代码基本一致,今天对Calli调用函数方法的研究基本成功!