P/Invoke方式调用非托管代码的调用约定

由于非托管dll在导出非托管函数时会采用一些不同的调用约定,在某些情况下如果使用默认的调用约定就可能出现无法正确调用非托管函数的情况,因此必须显示的制定调用约定既DllImport数次那个中的CallingConvention字段,CallingConvention字段定义了调用约定,CallingConvention可以取的所有值如下:

Cdecl : 调用方既托管代码方负责清理堆栈,这就使得开发人员能够调用具有可变参数的函数,使之可用于接收可变数目的参数的方法

StdCall: 被调用方既非托管代码函数负责清理堆栈,这是在windows操作系统下使用平台调用非托管代码的默认调用约定

Winapi:使用默认平台的调用约定,比如在windows平台上默认为StdCall,而在windows CE上默认为Cdecl

ThisCall:此调用约定主要用于调用C++类中的方法,而在调用C/C++导出函数时一般不使用此值,第一个参数是this指针,它存储在寄存器ECX中,其他参数被推送到堆栈上,此调用约定用于调用从非托管DLL导出的类中的方法

FastCall:虽然CallingConvertion中支持此字段,但是.Net却不支持此字段

从以上CallingConvertion枚举的介绍中,其实调用约定的一个重要作用是是垃圾处理在哪方做的问题。而另一个十分重要的作用是调用约定控制着非托管代码函数的名称重整问题,这就影响着非托管代码导出的实际名称,也影响着我们进行P/Invoker方式进行平台调用的入口函数名称问题。

举个例子:

如果一个非托管代码被导出时,使用extern "C"和__stdcall进行定义,其函数名就会被重整成 “下划线+原函数名+@+所有函数参数的总字节数” 这种格式。如一个C头文件中的函数声明如下:

extern “C” _declspec(dllexport)

      int _stdcall Multiply(int factorA,int factorB);

两个整形参数共占用了8个字节,那么其导出的函数名应该是_Mutiply@8,

这里需要说明的是,这种情况其实并不需要担心,只要在进行平台调用的时候我们正确的制定了调用约定,既调用约定与非托管函数定义的调用约定一致时,CLR会自动为我们进行处理,无需担心函数名重整问题。所以获得非托管函数的调用约定信息就显得十分重要了。

最好的办法是使用查看头文件,如果没有显示的定义调用约定,那么调用约定就取决于C/C++的项目设置。如果无法获得源码,那么可以借助dumpbin工具,通过查看DLL导出的函数名以函数名重整为线索,断定一个非托管函数的调用约定也是可以的。

总之在进行P/Invoke平台调用时,调用约定会直接导致无法找到有效的EntryPoint。 

 

原文链接: https://www.cnblogs.com/chongsenLi/archive/2010/09/01/1814890.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    P/Invoke方式调用非托管代码的调用约定

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/14576

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月7日 下午2:15
下一篇 2023年2月7日 下午2:16

相关推荐