另一个线程

时间:2015-11-29 00:02:48

标签: c++ multithreading winapi dll

我在另一个线程内部创建线程时遇到问题。通常我可以这样做,但这个问题的原因是因为启动这些线程的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函数。问题是,为什么?还有什么可以解决它?

3 个答案:

答案 0 :(得分:2)

   #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,以便链接器使用正确的入口点。

答案 1 :(得分:0)

根据MSDN,您既不会在DllMain中调用LoadLibrary也不会调用CreateThread - 您的代码同时执行这两项操作!

答案 2 :(得分:0)

发布的MCVE有三个问题:

  • 第一个是一个简单的错误,您正在调用IncrementReference(0)而不是IncrementReference(hModule)

  • 第二个是rundll32没有使用的入口点;入口点参数是必需的,或者rundll32不会工作(我不认为它甚至加载了DLL)。

  • 第三个是汉斯指出的#pragma

修复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的调用仍然不合适,但在这种情况下似乎没有任何副作用(可能是因为保证已经加载了相关库)。

(上一页)答案:

只需将对DllMain的IncrementReference调用移至FirstThread即可修复MCVE。这是解决问题的唯一安全和正确的方法。

附录:正如汉斯指出的那样,您还需要删除/Entry pragma。

(冗余?)评论:

如果正在加载DLL的应用程序在FirstThread可以运行之前卸载DLL的程度不正常,并且为了论证而假设您无法修复它,唯一可行的选择是解决问题 - 例如,DllMain可以暂停进程中的所有其他线程,以便它们无法卸载DLL,并在调用IncrementReference后从FirstThread恢复它们。

或者您可以尝试挂钩FreeLibrary,或者对加载器进行逆向工程并直接处理引用计数,或删除应用程序放置的挂钩,或者在DllMain中手动加载DLL的单独副本(使用您自己的DLL加载器)而不是一个Windows提供的)或开始一个单独的过程并在那里工作,或者哦,毫无疑问还有其他任何可能性,但在那一点上,我担心这个问题确实太宽泛了Stack Overflow,特别是因为您无法向我们提供应用程序正在执行的操作的真实细节。

相关问题