DLL导出与调用约定
2013-08-29 21:35
316 查看
一般来说,从DLL导出函数有两种方法。一种是使用.def文件;另一种是使用__declspec(dllexport)。
使用上面两种方法各有优缺点。使用.def文件就是需要额外维护,当导出函数更改名字或者追加导出函数。而使用__declspec(dllexport)则需要注意使用的调用约定,在使用C++编译器时。
一般来说,调用约定只关乎参数的压栈和最后的清理工作。还有就是名字修饰。比如:
使用__cdecl调用:
使用C编译器编译后的修饰名字为:_func。清理栈的工作由调用方做。
使用__stdcall和__thiscall调用:
使用C编译器编译后的修饰名字为_func@20。清理栈的工作由被调方做。
使用__fastcall调用:
使用C编译器编译后的修饰名字为@func@20。清理栈的工作由被调方做。
下图是使用__cdecl编译:
大致一看都差不多,只是在名字后的有个字母不一样,__stdcall得是YG,而__cdecl的是YA。而这也正好指明了C++的名字修饰的规则,简单来说就是:
・名字(可以包含命名空间)
・调用约定
・返回值
・参数列表
调用约定修饰规则:
类型修饰规则:
参数表后以”@Z”标示整个名字的结束,如果该函数无参数,则以”Z”标识结束。
最后一点:如果我们直接使用__declspec(dllexport)导出函数的话,最好使用C编译器。如果直接用C++编译器,导出的函数名就像上面一样。在动态使用DLL时调用GetProcAddress使用原始函数名称一般来说会失败。例如:
所以一般使用C编译器,比如这样:
这时,修饰后的名字就是这样:
一般导入函数时也建议这样:
第一种写法在效率上来说会更好点。
参考:http://hi.baidu.com/luosiyong/item/c8664533f56b66c31b9696e3
http://msdn.microsoft.com/en-us/library/k2b2ssfy.aspx
使用上面两种方法各有优缺点。使用.def文件就是需要额外维护,当导出函数更改名字或者追加导出函数。而使用__declspec(dllexport)则需要注意使用的调用约定,在使用C++编译器时。
调用约定
在使用C++或者在常见的windows API的声明头文件常见的WINAPI,而WINAPA就是__stdcall。在C++还有__cdecl。而__pascal和__thiscall都已忽略了。宏PASCAL也用的是__stdcall。一般来说,调用约定只关乎参数的压栈和最后的清理工作。还有就是名字修饰。比如:
void func(char, short, int, double)
使用__cdecl调用:
使用C编译器编译后的修饰名字为:_func。清理栈的工作由调用方做。
使用__stdcall和__thiscall调用:
使用C编译器编译后的修饰名字为_func@20。清理栈的工作由被调方做。
使用__fastcall调用:
使用C编译器编译后的修饰名字为@func@20。清理栈的工作由被调方做。
名字修饰
从上面的图也可看见,压栈都是从右到左,而名字修饰的方法却不一样。C编译器的修饰方法相对来说比较简单。以_func@20为例,_是追加的,func就是函数的名字,@是追加的,20为参数的字节数。C++的修饰方法相对来说比较复杂。下图是使用C++编译器产生的名字,使用__declspec(dllexport)和__stdcall。下图是使用__cdecl编译:
大致一看都差不多,只是在名字后的有个字母不一样,__stdcall得是YG,而__cdecl的是YA。而这也正好指明了C++的名字修饰的规则,简单来说就是:
・名字(可以包含命名空间)
・调用约定
・返回值
・参数列表
调用约定修饰规则:
代号 | 调用约定 |
@@YG | __stdcall |
@@YA | __cdecl |
@@YI | __fastcall |
代号 | 含义 |
X | void |
D | char |
E | unsigned char |
F | short |
H | int |
I | unsigned int |
J | long |
K | unsigned long(DWORD) |
M | float |
N | double |
_N | bool |
U | struct |
PA | pointer |
PB | const pointer |
最后一点:如果我们直接使用__declspec(dllexport)导出函数的话,最好使用C编译器。如果直接用C++编译器,导出的函数名就像上面一样。在动态使用DLL时调用GetProcAddress使用原始函数名称一般来说会失败。例如:
GetProcAddress(handle, "func");
所以一般使用C编译器,比如这样:
#ifdef __cplusplus // If used by C++ code extern "C" { // we need to export the C interface #endif __declspec(dllexport) VOID __cdecl func(char, short, int, double) { ... } #ifdef __cplusplus } #endif
这时,修饰后的名字就是这样:
一般导入函数时也建议这样:
#ifdef __cplusplus extern "C" { #endif // _declspec(dllimport) VOID func(char, short, int, double); // or VOID func(char, short, int, double); #ifdef __cplusplus } #endif
第一种写法在效率上来说会更好点。
参考:http://hi.baidu.com/luosiyong/item/c8664533f56b66c31b9696e3
http://msdn.microsoft.com/en-us/library/k2b2ssfy.aspx
相关文章推荐
- 显示调用DLL 应该注意DLL导出函数的调用约定问题
- DLL 导出 导入 调用约定
- DLL中导出函数(函数名及其调用约定)??
- DLL 导出函数 _stdcall 和 _cdecl调用约定
- C++编译时函数名修饰约定规则和DLL中导出函数的方法
- QT导出dll 与 QT调用dll
- 在C#中动态调用native dll的导出函数
- VC++编写DLL导出函数及其调用方法
- 调用未知DLL中的导出函数[转]
- DLL中调用约定和名称修饰
- 调用未知DLL中的导出函数[转]
- DLL中调用约定和名称修饰
- 通过GetProcAddress函数动态调用dll中地函数,是否必须通过extern C声明导出函数?(转)
- C++编译时函数名修饰约定规则和DLL中导出函数的方法
- DLL导出函数的声明和调用
- C/C++/动态链接库DLL中函数的调用约定与名称修饰
- 【转】调用未知DLL中的导出函数
- DLL中调用约定和名称修饰
- 透过汇编另眼看世界之DLL导出函数调用
- 用模块定义文件的方法调用DLL,获取其导出的全局变量值