在另一个线程中创建线程
CreateThread inside another thread
在另一个线程中创建线程时遇到问题。通常我可以这样做,但出现这个问题的原因是我有启动这些线程的DLL的Incremented Reference Count
。我需要在这个DLL中启动多个线程。我如何绕过这一点,并能够在项目中需要时发出多个CreateThread()
,而不会因为DLL中的Incremented Reference Count
而出现问题?
这是我在DLL文件中为增量引用计数写的函数:
BOOL IncrementReference( HMODULE hModule )
{
if ( hModule == NULL )
return FALSE;
TCHAR ModulePath[ MAX_PATH + 1 ];
if ( GetModuleFileName( hModule , ModulePath , MAX_PATH ) == 0 )
return FALSE;
if ( LoadLibrary( ModulePath ) == NULL )
return FALSE;
return TRUE;
}
根据要求,这里有一个PoC程序来重现我面临的问题。我真的希望这能帮助你们给我一个解决方案。此外,请注意,由于我所针对的应用程序中的条件(该应用程序中已经设置了钩子),DLL正在卸载,因此首先需要增加引用计数才能运行我的线程。
此外,我不能在主线程中运行多个操作,因为它有自己的功能需要处理,并且需要另一个线程来处理其他事情。它们也必须同时运行,因此我需要解决在增量DLL中生成多个线程的问题。
// dllmain.cpp : Defines the entry point for the DLL application.
#pragma comment( linker , "/Entry:DllMain" )
#include <Windows.h>
#include <process.h>
UINT CALLBACK SecondThread( PVOID pParam )
{
MessageBox( NULL , __FUNCTION__ , "Which Thread?" , 0 );
return 0;
}
UINT CALLBACK FirstThread( PVOID pParam )
{
MessageBox( NULL , __FUNCTION__ , "Which Thread?" , 0 );
_beginthreadex(0, 0, &SecondThread, 0, 0, 0);
return 0;
}
BOOL IncrementReference( HMODULE hModule )
{
if ( hModule == NULL )
return FALSE;
TCHAR ModulePath[ MAX_PATH + 1 ];
if ( GetModuleFileName( hModule , ModulePath , MAX_PATH ) == 0 )
return FALSE;
if ( LoadLibrary( ModulePath ) == NULL )
return FALSE;
return TRUE;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
if (IncrementReference(0))
_beginthreadex(0, 0, &FirstThread, 0, 0, 0);
}
break;
}
return TRUE;
}
正如您所看到的,该代码从不执行SecondThread
函数。问题是,为什么?能做些什么来修复它?
#pragma comment( linker , "/Entry:DllMain" )
这是一个非常糟糕的想法,DLL的正确入口点是而不是,事实上是DllMain()。您必须记住,WinMain和DllMain只是占位符名称。Microsoft记录可执行文件入口点相关性的一种方法。按照惯例,你在程序中使用相同的名称,每个人都会理解他们的工作
但是在C或C++程序中还有一个非常重要的附加细节,CRT(C运行库)需要首先初始化在之前,您可以运行任何可能进行CRT函数调用的代码。像_beginthreadex()
。
换句话说,默认的/ENTRY链接器选项是而不是DllMain()。DLL的真正入口点是_DllMainCRTStartup()
。CRT内部的一个函数,负责所需的初始化,然后调用DllMain()。如果你在程序中写了一个,那么它就是运行的程序。如果你不这样做,那么CRT中的一个伪链接就会被链接起来。
当你调用CRT函数并且CRT没有初始化时,所有的赌注都会被取消。您必须删除#pragma,以便链接器使用正确的入口点。
根据MSDN,您既不能在DllMain中调用LoadLibrary,也不能在其中调用CreateThread——您的代码可以同时调用这两个!
发布的MCVE有三个问题:
-
第一个是一个简单的错误,您调用的是
IncrementReference(0)
而不是IncrementReference(hModule)
。 -
二是
rundll32
没有可供使用的入口点;入口点参数是强制性的,否则rundll32
将不起作用(我认为它甚至不会加载DLL)。 -
三是汉斯提出的CCD_ 11。
修复IncrementReference()调用后,删除#pragma
并添加一个入口点:
extern "C" __declspec(dllexport) void __stdcall EntryPoint(HWND, HINSTANCE, LPSTR, INT)
{
MessageBoxA( NULL , __FUNCTION__ , "Which Thread?" , 0 );
}
然后你可以像这样运行DLL:
rundll32 testdll.dll,_EntryPoint@16
这在我的机器上工作;EntryPoint、FirstThread和SecondThread都会生成消息框。请确保您不会过早地从EntryPoint中取消消息框,因为这将导致应用程序退出,并带走其他线程。
对LoadLibrary的调用仍然不正确,但在这种情况下似乎没有任何副作用(可能是因为有问题的库保证已经加载)。
(上一页)答案:
MCVE可以通过简单地将对IncrementReference的调用从DllMain移动到FirstThread来修复。这是解决问题的唯一安全和正确的方法。
附录:正如Hans所指出的,您还需要删除/Entry
杂注。
(多余?)评论:
如果加载DLL的应用程序行为不端,以至于在FirstThread运行之前卸载了DLL,并且为了论证起见,假设您无法修复它,那么唯一现实的选择就是解决这个问题——例如,DllMain可以挂起进程中的所有其他线程,这样它们就无法卸载DLL,并在调用IncrementReference之后从FirstThread恢复它们。
或者,你可以尝试挂接FreeLibrary,或者对加载程序进行反向工程,直接干扰引用计数,或者删除应用程序放置的挂接,或者在DllMain中手动加载DLL的单独副本(使用你自己的DLL加载程序,而不是Windows提供的加载程序),或者启动一个单独的进程,从那里开始工作,或者,哦,毫无疑问,还有很多其他的可能性,但在这一点上,我担心这个问题对Stack Overflow来说真的太宽泛了,特别是因为你无法向我们提供应用程序正在做什么的真正细节。
- 两个线程一个使用流 Api,另一个线程创建文件失败并出现错误ERROR_SHARING_VIOLATION
- C++ 线程创建/删除与线程停止/恢复
- Qt - 如何从线程创建 QFuture
- 我可以使用Qt线程ID为每个线程创建唯一的缓存吗?
- 零MQ 后台线程创建
- OpenMP 线程创建
- GLFW & ImGui:从 main 以外的线程创建 ImGui 控件
- 对象:无法为位于不同线程中的父线程创建子级
- C++ 11:线程创建给我一个"Attempt to use a deleted function"错误
- C 的周期性线程创建
- MPI - 当数组初始化值必须为常量时,如何为工作线程创建部分数组
- 当主GUI线程被阻塞时,如何从工作线程创建无模式对话框
- 多个线程创建5个线程来计算质数
- 为线程创建模板
- 线程创建,CRT和DLL是如何完成的?
- 同步线程创建和销毁(静态)对象
- 竞争条件:一个线程创建静态对象,另一个线程在初始化完成之前使用它.如何处理
- 从不同线程创建QMainWindow
- QFuture 无法为位于不同线程中的父线程创建子级
- ( QNativeSocketEngine)QObject:无法为位于不同线程中的父线程创建子级