通过GetProcAddress函数动态调用dll中地函数,是否必须通过extern C声明导出函数?(转)
通过GetProcAddress函数动态调用dll中的函数,是否必须通过extern "C"声明导出函数? [已结贴,结贴人:darongtou]
如题,网上搜了N多资料,一直找不到确定的答案,目前我的答案是“是”。
晚上因为一个程序,好好研究了一下。
很多资料上都只是说明“如果没有 extern "C" 修饰,输出函数仅仅能从 C++ 代码中调用。”
却并没有明确这个调用是通过显式调用还是隐式调用,我也一直没有看到过有代码是通过显示调用没有 extern "C" 修饰的导出函数。
MSDN上也只是说
The spelling and case of the function name pointed to by lpProcName must be identical to that in the EXPORTS statement of the source DLL's module-definition (.DEF) file. The exported names of Win32 API functions may differ from the names you use when calling these functions in your code.
下面再从理论方面进行一些分析:
GetProcAddress函数声明是:
FARPROC GetProcAddress(
HMODULE hModule, // handle to DLL module
LPCSTR lpProcName // name of function
);
C++是支持函数重载的,也就是说允许多个不同的函数可以有同样的函数名,如果不通过extern "C"修饰,就可以输出相同的函数名。
这样,就和GetProcAddress函数声明不一致了,所以推断不能动态调用没有extern "C"修饰的导出函数,因为GetProcAddress函数是通过函数名来唯一确定被调用函数的地址的。
欢迎大家讨论!
第1个回答
显示调用必须使用extern "C"修饰符。隐式调用可以使用任何类型,但只有C++能调用没有extern "C"修饰的导出函数。
GetProcAddress是一种通用的获取函数入口点的API,能被任何语言调用,所以限制一定比较多,比如它的参数一定是一个ANSI串(操作系统并未提供UNICODE版本)。
第2个回答
我的理解是这样的:GetProcAddress实际上跟你直接调用myfunc()一样,都是查询Export表来得到函数地址再去调用,因此你修饰符不对就会造成找不到entry,是不行的。当然我没试过,没有完全的把握。
第3个回答
C函数和C++函数的名称是不一样的,可以使用工具来查看,比如Dependency Walker。如果你想试验,可以根据工具看到的名称来调用GetProcAddress试试
第4个回答
主要是就是名字的问题
有两种例外情况可以不加extern "C":
1。如果不是用C++编译器而是用C编译DLL,名字不会变,可以不加extern "C"
2。如果DLL的使用者知道是用C++编译器编译DLL,不加extern "C"也可以,因为他知道名字改变的规则。调用GetProcAddress,把函数名字改了就是了
第5个回答
C++编译器和C编译器编译后生成的函数名不一样。
GetProcAddress认为是cdecl的函数,而
编译DLL的是VC++,所以要加一个extern "C"的修饰符
指明以cdecl的方式生成函数。
第6个回答
发现不用加extern "C"也是可以的,只要在调用端用修饰过的函数名即可,不能用原函数名。
例子关键代码如下:
----------------------------
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);
---------------------------
这事暂时可以告一段落了,实验还是最有力的证明。