__stdcall,__cdcel,extern c 和导出函数名
2015-11-12 20:56
302 查看
无论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存在与否,导致的改名,影响了我们能否正确调用到一个函数。当然,调用约定在一个函数的调用者和被调者之间一定要一致,否则即便函数名字上面不出问题(可以得到函数的地址了),函数返回时清理堆栈出错,立即就会崩溃!
一.先说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存在与否,导致的改名,影响了我们能否正确调用到一个函数。当然,调用约定在一个函数的调用者和被调者之间一定要一致,否则即便函数名字上面不出问题(可以得到函数的地址了),函数返回时清理堆栈出错,立即就会崩溃!
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例
- C与C++之间相互调用实例方法讲解
- 解析C++中派生的概念以及派生类成员的访问属性
- C++ Custom Control控件向父窗体发送对应的消息