您的位置:首页 > 编程语言

在DLL编程中,导出函数为什么需要extern "C"

2016-03-10 19:58 435 查看
一般来讲,在DLL编程过程中,对于导出的函数前 都需要加入 extern “C”,

extern 表示这是个全局函数,可以供各个其他的函数调用;

“C” 表示编译时按照 C编译器的方式进行编译,而不是C++。 C++的编译方式考虑了函数重载,所以对函数名进行了新的修饰,产生了所谓的破坏性命名。

不过,也有特殊情况,有三种例外情况可以不加extern “C”:

1。如果不是用C++编译器而是用C编译DLL,名字不会变,可以不加extern "C"

2。如果DLL的使用者知道是用C++编译器编译DLL,不加extern “C”也可以,因为他知道名字改变的规则。调用GetProcAddress时,把函数名字改了就是了,改为修饰后的函数名。 如 fnDll1
改为 ?fnDll1@@YAHXZ。

例子关键代码如下:

----------------------------

DLL部分:

// This is an example of an exported function.

DLL1_API int __cdecl fnDll1(void)

{

return 42;

}

输出的修饰函数名为?fnDll1@@YAHXZ

DLL1_API int __cdecl fnDll1(int a)

{

return 42+a;

}

输出的修饰函数名为?fnDll1@@YAHH@Z

-----------------------------

EXE部分:

HINSTANCE hModule = LoadLibrary("dll1.dll");

ASSERT(hModule);

typedef int (*fnDll1)();

fnDll1 pfnDll1 = NULL;

//VERIFY(pfnDll1 = (fnDll1)::GetProcAddress(hModule, "fnDll1"));

VERIFY(pfnDll1 = (fnDll1)::GetProcAddress(hModule, "?fnDll1@@YAHXZ"));

ASSERT(pfnDll1() == 42);

typedef int (*fnDll2)(int);

fnDll2 pfnDll2 = NULL;

VERIFY(pfnDll2 = (fnDll2)::GetProcAddress(hModule, "?fnDll1@@YAHH@Z"));

ASSERT(pfnDll2(3) == 45);

---------------------------

3.上面的2太麻烦了。所以还有一种方法是使用def文件。

(如果DLL使用的是def文件,要删除TestDll.h文件中关键字extern "C",即2者是不能共存的)。

def 文件(模板定义文件),第一个语句必须是 LIBRARY 语句,指出DLL的名字;

EXPORTS语句 列出被导出函数的名字;将要输出的函数罗列出来,这个函数名字必须与定义函数的名字完全一致,如此既可以得到

一个没有任何修饰符的函数名了。

被导出的函数 可以和一个序号相对应。定义序号时必须在数字前加一个@。例如 isRUINIan @1 //IsRuiNian 函数对应序号为 1

这样的话,我们既可以GetProAddress(hinstance,“IsRuiNian”),也可以 GetProAddress(hinstance,(LPCSTR)1)实现调用。

参考:http://topic.csdn.net/t/20021012/17/1090973.html

问:要说在不同编程语言之间共享dll文件的导出函数的话,在函数前加 WINAPI 就够了,为什么要加 extern "C"

防止C++编译器的“名字破坏”特性

extern "C"的作用是,使编译器按照c的方式生成函数名,c的方式实际的函数名和你写的一样。如果没有这个,则按照c++的方式生成函数名,这样实际的函数名(LoadLibrary方式GetProcAddress传入的函数名)和你写得函数名不一样,这样你用LoadLibrary、GetProcAddress这种方式调用dll就不成功。

但是用引入库的方式调用,则编译器自动转换函数名,所以总是没有问题。

http://www.qqgb.com/program/vc/vcjq/program_166495.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: