gpt4 book ai didi

c++ - Qt 应用程序在使用 DLL 时崩溃,如果导出函数未声明 APIENTRY 则工作正常

转载 作者:行者123 更新时间:2023-11-30 01:53:39 24 4
gpt4 key购买 nike

我在 Visual Studio 2010 中构建了一个 DLL 项目(通过关注 this post)。它只包含一个功能:

extern "C" __declspec(dllexport) void APIENTRY hello()
{
std::cout << "Hello DLL.\n" << std::endl;
}

然后我创建了一个 Qt 控制台应用程序来使用该 DLL。它的 main.cpp 包含这个:

typedef bool (*f_void)(void);

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

QLibrary lib("TestDll");
f_void hello = (f_void) lib.resolve(QString("hello").toLatin1());
hello();

return a.exec();
}

当我在 DLL 中使用 APIENTRY 时,程序在调用 hello() 时崩溃。不过,如果我从 hello() 声明中删除 APIENTRY ,它就可以正常工作。为什么会这样?

最佳答案

除了需要匹配调用约定的函数调用机制(如下所述通过为函数指针提供正确的类型来解决),调用约定会影响名称重整。

extern "C" 阻止了将类型包含在名称中的 C++ 操作方法,这样函数的重载就可以获得唯一的名称,并且可以在符号查找期间进行区分。但它并不能完全防止重整。例如,问题中的函数 void __stdcall hello(void) 将被 __declspec(dllexport) 导出为 _hello@0,其中尾随数字是参数列表中的字节数。这有助于避免调用者和被调用者对参数大小不一致的情况,这在被调用者清理堆栈的 __stdcall 中尤其成问题。

不过,可以禁用名称修改(gdi32.dllshell32.dll 等 Win32 DLL 已经这样做了)。为此,您需要一个链接器定义文件:

EXPORTS
hello
; or hello = _hello@0

链接器知道重整规则并且会在目标文件中找到重整的名称,即使您没有明确提供它也是如此。

此外,当导出列在定义文件中时,代码中不再需要 __declspec(dllexport)

更多information is available on MSDN .


如果您通过错误类型的函数指针调用函数,您会得到未定义的行为。调用约定是类型的一部分。尝试:

typedef bool (APIENTRY *f_void)(void);  // or __stdcall instead of APIENTRY

我猜你的头文件之一包含 #define APIENTRY __stdcall

显式设置导出函数和函数指针的调用约定始终是个好主意。如果不这样做,您将获得当前有效的默认调用约定,这是特定于项目的编译器选项。


迂腐地说,函数和函数指针是否标记为 extern "C" 也是类型的一部分。但是在发现 DLL 的 Windows 上,extern "C"extern "C++" 对调用约定具有相同的效果。

关于c++ - Qt 应用程序在使用 DLL 时崩溃,如果导出函数未声明 APIENTRY 则工作正常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22995120/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com