C++ 使用stdcall&;调用DLL;VS2013中的GetProcAddress()
我试图从自己的DLL调用函数,但根据DLL项目中的调用约定,我可能找不到ProcAddress,或者堆栈已损坏。它适用于第三方DLL,因此如果没有重大问题,我不想更改加载代码本身的任何内容。一个简单的例子:C++ 使用stdcall&;调用DLL;VS2013中的GetProcAddress(),c++,dll,visual-studio-2013,C++,Dll,Visual Studio 2013,我试图从自己的DLL调用函数,但根据DLL项目中的调用约定,我可能找不到ProcAddress,或者堆栈已损坏。它适用于第三方DLL,因此如果没有重大问题,我不想更改加载代码本身的任何内容。一个简单的例子: #include <windows.h> #include <cstdlib> #include <iostream> typedef long (__stdcall* tMyFunction)(int); int main(int argc, cha
#include <windows.h>
#include <cstdlib>
#include <iostream>
typedef long (__stdcall* tMyFunction)(int);
int main(int argc, char* argv[]){
HINSTANCE m_dllHandle = LoadLibrary("MyDll.dll");
if (m_dllHandle != NULL){
tMyFunction function = (tMyFunction)GetProcAddress(m_dllHandle, "myFunction");
if (function != NULL){
long value = function(1);
std::cout << value << std::endl;
}else{
std::cout << "GetProcAddress() failed" << std::endl;
}
FreeLibrary(m_dllHandle);
m_dllHandle = NULL;
}else{
std::cout << "LoadLibrary() failed" << std::endl;
}
system("pause");
return EXIT_SUCCESS;
}
结果:GetProcAddress()失败
垃圾箱/出口->_myFunction@4 = _myFunction@4
extern "C" __declspec(dllexport) long __cdecl myFunction(int a){
return 10;
}
结果:“运行时检查失败#0-ESP的值未在函数调用中正确保存。这通常是调用使用一个调用约定声明的函数,而函数指针使用另一个调用约定声明的结果。”(因为我在加载代码中使用u stdcall,在DLL中使用u cdecl)
dumpbin/EXPORTS->\u myFunction=\u myFunction
在第三方DLL中,我可以看到,“dumpbin/EXPORTS”只显示
myFunction(没有下划线,没有@4)我可以做些什么来实现同样的功能,并且仍然能够用上面定义的类型(
typedef long(u stdcall*tMyFunction)(int);
)加载它?我的编译器是“Visual Studio 2013”。首先,DLL函数使用的调用约定必须与您使用的函数指针定义匹配。由于它不匹配,您会得到一个错误,即损坏了堆栈
其次,使用
GetProcAddress
时,调用GetProcAddress
时使用的函数名必须与导出的DLL函数名完全匹配。它不仅要匹配字符,还要匹配大小写,即myFunction
与myFunction
不同
示例中导出的名称是_myFunction@4
。这意味着使用GetProcAddress
访问函数将是:
GetProcAddress(myModuleHandle, "_myFunction@4");
不必这样指定名称,因为这是函数的名称
所以你有两个选择:
myFunction
模块定义文件
(或简称为.DEF
文件)重新定义名称
以下是模块定义文件的链接:
因此,典型的.DEF文件将包含以下内容:
LIBRARY MyDLL
EXPORTS
myFunction @2
@2
是序号
。出于您的目的,这个数字是多少并不重要,因为只有一个函数。我选择了@2
,但你可以选择任何数字(@3
,@4
,甚至@1000
,如果你愿意的话)。但是,如果有多个导出函数,则序号应该是唯一的,即不能有两个具有相同序号的导出函数
如果将上述内容保存到MyDll.DEF
并将其添加到构建DLL的项目(而不是将使用DLL的项目),则需要重建DLL。完成后,DLL现在将有一个导出名称myFunction
,没有@4
装饰,也没有下划线
(注意:如上所述,使用的extern“C”
不会关闭Windows使用的装饰(名称后面附加的下划线和@x
)。所有extern“C”确实关闭C++名称。要关闭Windows名称,必须使用<代码> .DEF< /COD>文件>
另外,我使用一个名为Dependency Walker
的工具来轻松确定DLL中导出的函数名。由于Dependency Walker是一个GUI应用程序,因此输出比dumpbin.exe
补充一点,您提到DLL在其他应用程序中可以完美地工作。如果这些其他应用程序使用导入库
而不是使用加载库
和GetProcAddress
访问函数,那么这些导入库将自动处理myFunction
到的转换_myFunction@4
这就是为什么它对这些类型的应用程序没有问题。但是,当您选择LoadLibrary
和GetProcAddress
时,您在翻译名称时没有得到这种“帮助”,您基本上只能靠自己。您导出修饰名称,但将未修饰的名称传递给GetProcAddress
。我猜这是由于符号名称不匹配造成的。也许您可以尝试使用DEF文件来指定导出的函数?参见FYI,<代码>外部C < /COD>不停止名称修饰,只有C++名称的修改。要完全没有装饰,您需要使用@KingsleyChen建议的DEF文件。
LIBRARY MyDLL
EXPORTS
myFunction @2