我知道我不能从 DllMain 创建线程,但是在 DLL 中创建线程的正确方法是什么?
我试图创建一个环绕它的类并声明该类的一个全局实例,但是 WaitForSingleObject 卡在该类的析构函数中,即使我确定线程已经退出。
这是我从 exe 加载 dll 的示例程序:
#include <Windows.h>
#include <iostream>
using namespace std;
int main(int argc, TCHAR* argv[])
{
HMODULE module = LoadLibraryA("dll.dll");
Sleep(500);
if (module) FreeModule(module);
else cout << "failed to load library...\n";
return 0;
}
这里是dll代码:
#include <Windows.h>
#include <iostream>
using namespace std;
class X
{
HANDLE thread = NULL;
static DWORD WINAPI run_thread(LPVOID param)
{
cout << "dll.cpp: thread started\n";
Sleep(1000);
cout << "dll.cpp: thread is exiting\n";
return 0;
}
public:
X()
{
DWORD tmp;
thread = CreateThread(NULL, 0, run_thread, NULL, 0, &tmp);
if (thread == NULL)
{
cout << "failed to create thread\n";
throw 1;
}
}
~X()
{
if (thread != NULL)
{
cout << "joining thread...\n";
DWORD ret = WaitForSingleObject(thread, INFINITE);
cout << "result from waiting: " << ret << "\n";
thread = NULL;
}
}
}x;
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved)
{
switch (reason)
{
case DLL_PROCESS_ATTACH:
cout << __FUNCTION__ << " reason DLL_PROCESS_ATTACH\n";
break;
case DLL_THREAD_ATTACH:
cout << __FUNCTION__ << " reason DLL_THREAD_ATTACH\n";
break;
case DLL_THREAD_DETACH:
cout << __FUNCTION__ << " reason DLL_THREAD_DETACH\n";
break;
case DLL_PROCESS_DETACH:
cout << __FUNCTION__ << " reason DLL_PROCESS_DETACH\n";
break;
}
return TRUE;
}
这是程序输出:
DllMain reason DLL_PROCESS_ATTACH
DllMain reason DLL_THREAD_ATTACH
dll.cpp: thread started
DllMain reason DLL_PROCESS_DETACH
joining thread...
dll.cpp: thread is exiting
<- notice how WaitForSingleObject hangs,
even though the thread has exited
这种方法有什么问题?
如果我碰巧从 run_thread 中删除了 Sleep(1000),这样线程在调用 WaitForSingleObject 之前完成,那么它就不会挂起:
DllMain reason DLL_PROCESS_ATTACH
DllMain reason DLL_THREAD_ATTACH
dll.cpp: thread started
dll.cpp: thread is exiting
DllMain reason DLL_THREAD_DETACH
DllMain reason DLL_PROCESS_DETACH
joining thread...
result from waiting: 0
我不明白其中的区别,这也不是一个选项,因为线程的目的是在 DLL_PROCESS_DETACH 未发出信号时不断循环。
最佳答案
I know I cannot create threads from DllMain, but what is the correct approach for creating threads in a DLL?
在主应用程序加载 dll 后(通过 LoadLibrary
隐式或显式),它调用一些由您的 dll 导出的函数,它可以做任何它喜欢的事情,包括启动线程。
I tried to create a class that wraps around it and declare a global instance of that class
全局对象的初始化(销毁)也发生在 DllMain
中; “真正的”DllMain
入口点(这是 MSDN 谈论的入口点)由 CRT 获取,CRT 使用它来初始化(销毁)globals & co。然后然后调用您的DllMain
。
在this answer对于类似的问题(它是关于 LoadLibrary
而不是 CreateThread
,但其中大部分也适用于这种情况),您可以找到更多详细信息。
What is wrong with this approach?
在 DllMain
中尝试做复杂的事情是错误的,特别是 explicitly discouraged 中的事情;理想情况下,DllMain
应该只是空的或做极其琐碎的工作。
最有可能的是,在这种特定情况下,被终止的线程正在尝试再次调用 DllMain
以获得 DLL_THREAD_DETACH
通知,但由于存在隐式互斥锁而被锁定这会阻止对 DllMain
的并发访问,或者因为 Windows 明确禁止 DLL_THREAD_DETACH
通知在 DLL_PROCESS_DETACH
之后到达。
这不会让我感到惊讶 - 毕竟,正如 Hans Passant 指出的那样,在代码仍在运行的情况下调用 FreeLibrary
显然是未定义的行为。不可能有一个特定的函数 (FreeLibraryAndExitThread
) 让持有对库的引用的线程自动释放库并杀死自己。
关于c++ - 如何在 dll (c++) 中创建线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48809595/