我正在尝试从我自己的DLL调用一个函数,但根据DLL项目中的调用约定,我找不到ProcAddress或我的堆栈已损坏。它适用于第三方DLL,所以如果没有重大问题,我想在加载代码本身中不做任何改变。一个最小的例子:
#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;
}
在DLL中:
extern "C" __declspec(dllexport) long __stdcall myFunction(int a){
return 10;
}
结果:GetProcAddress()失败
dumpbin / EXPORTS - &gt; _myFunction @ 4 = _myFunction @ 4
extern "C" __declspec(dllexport) long __cdecl myFunction(int a){
return 10;
}
结果:“运行时检查失败#0 - ESP的值未在函数调用中正确保存。这通常是调用使用一个调用约定声明的函数,其中函数指针使用不同的调用声明惯例。” (因为我在加载代码中使用__stdcall而在DLL中使用__cdecl)。
dumpbin / EXPORTS - &gt; _myFunction = _myFunction
在第三方DLL中,我可以看到,“dumpbin / EXPORTS”只显示
myFunction(没有下划线,没有@ 4)我可以做些什么来完成相同的操作并且仍然可以使用上面定义的类型(typedef long (__stdcall* tMyFunction)(int);
)加载它?我的编译器是“Visual Studio 2013”。
答案 0 :(得分:8)
首先,DLL函数使用的调用约定必须与您正在使用的函数指针定义匹配。由于它不匹配,您会收到错误,即您已损坏堆栈。
其次,使用GetProcAddress
时,在GetProcAddress
调用中使用的函数名称必须与完全导出的DLL函数名称匹配。它不仅要匹配字符,还要匹配外壳,即myFunction
与MyFunction
不一样。
示例中的导出名称为_myFunction@4
。这意味着使用GetProcAddress
访问函数将是:
GetProcAddress(myModuleHandle, "_myFunction@4");
没有必要以这种方式指定名称,因为是函数的名称。
所以你有两个选择:
myFunction
由于我们介绍了第一个选项,对于第二个选项,您必须重建DLL以使用Module Definition File
(或简称为.DEF
文件)来重新定义名称。
以下是模块定义文件的链接:
因此,典型的.DEF文件将包含此内容:
LIBRARY MyDLL
EXPORTS
myFunction @2
@2
是ordinal number
。出于您的目的,这个数字并不重要,因为只有一个功能。我选择了@2
,但如果您愿意,可以选择任意数字(@3
,@4
,甚至@1000
。但是,如果有多个导出函数,则序数应该是唯一的,即,您不能有两个具有相同序号的导出函数。
如果将上述内容保存到MyDll.DEF
并将其添加到构建DLL的项目(而不是将使用DLL的项目),则需要重建DLL。完成后,DLL现在将导出名称myFunction
,但没有@4
装饰且没有下划线。
(注意:正如上面的评论中提到的,extern "C"
使用了而不是关闭Windows使用的装饰(附加的下划线和@x
附加到所有extern "C"
都关闭了C ++名称修改。要关闭Windows名称修改,需要.DEF
文件。)
P.S。我使用一个名为Dependency Walker
的工具来轻松确定DLL中导出的函数名称。由于Dependency Walker是一个GUI应用程序,因此输出比dumpbin.exe
添加,您提到DLL在其他应用程序中完美运行。如果其他应用程序使用import libraries
而不是使用LoadLibrary
和GetProcAddress
来访问该函数,那么这些导入库会自动处理myFunction
到_myFunction@4
的转换。
这就是为什么它适用于这些类型的应用程序没有问题。但是,当您选择LoadLibrary
和GetProcAddress
的路线时,您无法获得翻译名称的“帮助”,而且您基本上是靠自己。