1,现象:
自定义C/C++ dll,编译无错误,另一程序使用动态调用方法,加载dll正常,但是调用函数时使用各种方法无法成功。
使用dll查看工具,发现函数名为func和乱码的组合,如下图:
2, 原因查找:
各方查找原因,发现原因并不是有的帖子说的要在编译dll的头文件中加入 extern "C"
extern "C"只是说明导出函数使用C编译器,不加extern "C"说明使用C++编译器规则。
两种规则区别如下:
(1)C编译器的函数名修饰规则
对于__stdcall调用约定,编译器和链接器会在输出函数名前加上一个下划线前缀,函数名后面加上一个“@”符号和其参数的字节数,如果原始函数为func(),编译后变为_func@;
(2)C++编译器的函数名修饰规则
C++的函数名修饰规则内容更加丰富,包含函数名,返回值类型,参数类型等信息。
a, 不管__cdecl,__fastcall还是__stdcall调用方式,函数修饰都是以一个“?”开始
b, 后面紧跟函数的名字
再后面是参数表的开始标识和按照参数类型代号拼出的参数表。
c, 详细规则如下:__stdcall,参数表的开始标识是“@@YG”
__cdecl方式则是“@@YA”
__fastcall方式则是“@@YI”。
d, 参数表以代号表示:
X--void
D--char
E--unsigned char
F--short
H--int
I--unsigned int
J--long
K--unsigned long
M--float
N--double
_N--bool
....
PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以"0"代替,一个"0"代表一次重复;
参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;
e, 参数表后以"@Z"标识整个名字的结束,如果该函数无参数,则以"Z"标识结束。
extern "C"只解决了C和C++语方之间调用的问题(extern "C" 是告诉编译器,让它按C的方式编译),它只能用于导出全局函数这种情况 而不能导出一个类的成员函数。
同时如果导出函数的调用约定发生改变,即使使用extern "C",编译后的函数名还是会发生改变。
3. 正确方法
添加模块定义文件(def文件):
LIBRARY "dodll"
EXPORTS
test1 @ 1
test2 @ 2
同时以release模式输出
调用正常