__stdcall,__cdcel,extern c 和导出函数名

   无论c或c++都会对导出函数改名或不改名,无论你是静态还是动态调用一个导出函数,都可能碰上改名后导致的调用失败(甚至可能是调用约定不同而导致清理堆栈出错,造成崩溃),下面分析一下改名和调用约定之间的关系。

        一.先说extern c。
        其含义是指,按c的方式编译代码。在vs7及以后的vs中,有编译选项可以直接选择“编译为:c代码”(在工程属性----》c/c++----》advance----》编译为),其效果和用extern c包裹起来一样。vs默认是编译为c++方式的。

        二.简述下__stdcall和__cdcel。
        __stdcall是从右往左压参数,由被调者清理参数的相关堆栈,windows的系统函数都是这种方式。__cdcel也是从右往左压参数,但由调用者清理参数的相关堆栈,vs编译环境默认就是__cdcel(即,函数前如果不写调用约定,那就是__cdcel)。

        三.函数的改名问题。
        一个函数编译后的名字,其实由按c还是c++编译,调用约定两者共同决定。两者互相搭配,就有四种情况。下面假定一个函数名字在代码中叫FunName,
       1.c方式(即,你用extern c括起来的那些函数,或是改编译选项则影响所有函数)和__cdcel,编译后名字为:_FunName,如果是导出函数,则在Dll的导出函数中叫FunName
        2.c方式和__stdcall,编译后名字为_FunName@x,如果是导出函数,则在Dll中也叫_FunName@x(x是所有参数占空间的总大小)。
        3.c++方式和_cdcel,编译后名字为:_FunName@xxxxxxxxx,如果是导出函数,则在Dll中叫_FunName@xxxxxxxxx(由于c++编译后的名字,包含了参数个数,类型,返回值等信息,所以改名后会比较长,这里不做详细讨论)。
        4.c++方式和__stdcall,编译后名字为:_FunName@xxxxxxxxx,如果是导出函数,则在Dll中叫_FunName@xxxxxxxxx。

        四.导出函数的问题。
        Dll中的导出函数,其名字信息,其实位于两个位置,其一,在该dll对应的lib中;其二,在该Dll中。当我们使用静态的方式去调用一个导出函数时,我们会在其lib中寻找该函数的地址信息;当我们使用动态的方式(LoadLibrary和GetProcAddress)调用一个导出函数时,我们会在其Dll中寻找该函数的地址信息。
        我们都知道windows的系统函数,是extern c和__stdcall的。这种方式下,一个导出函数是会被改名的,无论在lib和dll中都是_FunName@x形式,但windows通过def模块,修改了dll中的函数名为FunName,而在其lib中还是_FunName@x。lib中名字为 _FunName@x,就确保了我们静态调用时,能链接到匹配的名字。dll中函数名字为FunName,就保证了动态调用时,可以传递正常的函数名给GetProcAddress(否则传一个改名后的函数名会多么恶心)时,能在dll中找到匹配的名字。
        所以,调用约定和extern c存在与否,导致的改名,影响了我们能否正确调用到一个函数。当然,调用约定在一个函数的调用者和被调者之间一定要一致,否则即便函数名字上面不出问题(可以得到函数的地址了),函数返回时清理堆栈出错,立即就会崩溃!

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Windows 平台上,_stdcall 是一种函数调用约定,它规定了参数传递和堆栈清理的方式。在使用这种约定的函数的声明中,函数前面通常会加上一个下划线,例如 _stdcall foo()。而在 C++ 中,成员函数是不能直接作为全局函数来使用的,因为成员函数需要通过对象来调用。因此,如果要将成员函数转换为全局函数,就需要使用回调函数(callback function)的方法。 具体来说,可以定义一个全局函数,该函数的参数包括一个指向的指针和其他需要的参数,然后在该函数中调用成员函数成员函数需要在调用时指定对象,因此可以将对象的指针作为参数传递给全局函数,然后在全局函数中使用该指针来调用成员函数。 下面是一个简单的示例代码,演示了如何将成员函数转换为全局函数: ```cpp class MyClass { public: void _stdcall myFunc(int param) { // do something } }; // 定义一个全局函数作为回调函数 void _stdcall myCallback(MyClass* obj, int param) { obj->myFunc(param); } int main() { MyClass obj; // 将成员函数转换为全局函数 void (_stdcall *func)(MyClass*, int) = &myCallback; // 调用全局函数 func(&obj, 123); return 0; } ``` 在上面的示例代码中,我们首先定义了一个 MyClass,其中包含一个成员函数 myFunc。然后定义了一个全局函数 myCallback,该函数接受一个指向 MyClass 对象的指针和一个整型参数,然后在该函数中调用 MyClass 的成员函数 myFunc。在主函数中,我们将 myCallback 转换为一个函数指针 func,并将 MyClass 对象的指针和参数传递给该函数指针来调用 MyClass 的成员函数 myFunc。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值