DLL 调用约定和访问冲突
DLL calling conventions & access violation
在这个伟大的平台上阅读和学习多年后,这是我的第一个 post。
我的问题:
在 C++ 中,我正在尝试创建一个动态链接库(32 位),它将用作 AQMP 通信客户端(基于 SimpleAmqpClient)。然后,该 dll 文件将在第三方应用程序(32 位)中使用。
在我的测试中,我在自定义可执行文件中调用 dll,一切正常。但是当我尝试在第三方应用程序中使用 dll 时,出现访问冲突错误 (0x00000000)。我发现问题可能出在函数调用约定上。
使用下面显示的几行代码可以重现该错误。如果我删除 mytest.dll 中的 __stdcall
表达式,它就会消失。通常我希望代码能够工作,因为它在 custom_test.exe 和 mytest.dll 中使用相同的调用约定。
(旁注:第三方应用程序需要一个 __stdcall
函数,这就是我依赖它的原因)
我想了解这种行为。提前致谢!
我的设置:
- OS: Windows 7
- 32 位编译器:gcc 5.3 (Cygwin)
我的代码(custom_test.exe):
#include <stdio.h>
#include <windows.h>
int main(void) {
HINSTANCE hInstance;
hInstance=LoadLibrary("mytest.dll");
FARPROC lpfnGetProcessID = GetProcAddress(HMODULE(hInstance), "test");
// Function prototype
typedef void (__stdcall *myFunction)(void);
myFunction test;
test = myFunction(lpfnGetProcessID);
// Call Function
test();
FreeLibrary(hInstance);
}
我的代码(mytest.dll):
extern "C" __declspec(dllexport) void __stdcall test(void) {
printf("Inside Function \n");
}
我通过
编译代码
- dll:
g++ mytest.cpp -o mytest.dll -shared -std=gnu++11
- exe:
g++ custom_test.cpp -o custom_test.exe -std=gnu++11
__stdcall 约定由被调用函数负责在 return 上清理堆栈,而 __cdecl 则由调用者负责。
我们看不到第三方 DLL 中的实际声明,但我最初的假设是 DLL 需要参数并且正在使用它认为错误的堆栈参数,或者正在清理堆栈基于它对堆栈参数的假设并且通常会弄乱您的堆栈。
编辑
不过在这个例子中,我看到在 32 位编译时,测试函数以 'test@0' 的名称导出。如果您将 GetProcAddress 更改为使用此修饰名称,它将起作用。
好的现在几个小时后我又能看清楚了!感谢IanM_Matrix1的建议,所谓的名字修饰确实是重点
经过研究,我现在可以分享一些我发现的有用资源:
重要的是要知道某些编译器会为函数名称添加不同的修饰,请参见此处:
http://wyw.dcweb.cn/stdcall.htm
考虑到这一点,您可以阅读有关 Win32 调用约定的这一页:
http://www.unixwiz.net/techtips/win32-callconv.html
使用 gcc 时,也可以通过标志 -Wl,--kill-at
.
禁用装饰
在这个伟大的平台上阅读和学习多年后,这是我的第一个 post。
我的问题:
在 C++ 中,我正在尝试创建一个动态链接库(32 位),它将用作 AQMP 通信客户端(基于 SimpleAmqpClient)。然后,该 dll 文件将在第三方应用程序(32 位)中使用。
在我的测试中,我在自定义可执行文件中调用 dll,一切正常。但是当我尝试在第三方应用程序中使用 dll 时,出现访问冲突错误 (0x00000000)。我发现问题可能出在函数调用约定上。
使用下面显示的几行代码可以重现该错误。如果我删除 mytest.dll 中的 __stdcall
表达式,它就会消失。通常我希望代码能够工作,因为它在 custom_test.exe 和 mytest.dll 中使用相同的调用约定。
(旁注:第三方应用程序需要一个 __stdcall
函数,这就是我依赖它的原因)
我想了解这种行为。提前致谢!
我的设置:
- OS: Windows 7
- 32 位编译器:gcc 5.3 (Cygwin)
我的代码(custom_test.exe):
#include <stdio.h>
#include <windows.h>
int main(void) {
HINSTANCE hInstance;
hInstance=LoadLibrary("mytest.dll");
FARPROC lpfnGetProcessID = GetProcAddress(HMODULE(hInstance), "test");
// Function prototype
typedef void (__stdcall *myFunction)(void);
myFunction test;
test = myFunction(lpfnGetProcessID);
// Call Function
test();
FreeLibrary(hInstance);
}
我的代码(mytest.dll):
extern "C" __declspec(dllexport) void __stdcall test(void) {
printf("Inside Function \n");
}
我通过
编译代码- dll:
g++ mytest.cpp -o mytest.dll -shared -std=gnu++11
- exe:
g++ custom_test.cpp -o custom_test.exe -std=gnu++11
__stdcall 约定由被调用函数负责在 return 上清理堆栈,而 __cdecl 则由调用者负责。
我们看不到第三方 DLL 中的实际声明,但我最初的假设是 DLL 需要参数并且正在使用它认为错误的堆栈参数,或者正在清理堆栈基于它对堆栈参数的假设并且通常会弄乱您的堆栈。
编辑 不过在这个例子中,我看到在 32 位编译时,测试函数以 'test@0' 的名称导出。如果您将 GetProcAddress 更改为使用此修饰名称,它将起作用。
好的现在几个小时后我又能看清楚了!感谢IanM_Matrix1的建议,所谓的名字修饰确实是重点
经过研究,我现在可以分享一些我发现的有用资源: 重要的是要知道某些编译器会为函数名称添加不同的修饰,请参见此处: http://wyw.dcweb.cn/stdcall.htm
考虑到这一点,您可以阅读有关 Win32 调用约定的这一页: http://www.unixwiz.net/techtips/win32-callconv.html
使用 gcc 时,也可以通过标志 -Wl,--kill-at
.