【问题标题】:How to execute threads in a DLL in C如何在 C 中执行 DLL 中的线程
【发布时间】:2019-06-05 12:46:31
【问题描述】:

我创建了一个运行 3 个工作线程的 DLL,主线程处于循环中等待线程完成。线程被创建,但没有执行线程。

我尝试在使用 CreateThread() 创建的函数内设置 MessageBox 函数,但该框没有出现。我也尝试过调试,来自 CreateThread() 的返回值是有效的,所以线程被创建了。

BOOL WINAPI DllMain() {
   main();
   return 1;
}

int main() {
    HANDLE h1, h2, h3;

    h1 = CreateThread(first)...
    h2 = CreateThread(second)...
    h3 = CreateThread(third)...

   WaitForSingleObject(h3, INFINITE);
   return 1;
}

first() {
    MessageBoxA("print some stuff");
    return;
}

我已经包含了一些关于我的布局的伪代码。由于它的敏感性,我无法提供真实的代码。然而,这就是正在发生的事情。我在另一个加载此 .DLL 的项目中使用 LoadLibrary。 DLL 被加载并执行 DllMain。然后它调用我的 main 函数来创建 3 个线程。每个线程都被创建。但是线程内部的东西并没有执行。

编辑:

// dllmain.cpp : Defines the entry point for the DLL application.
#include <Windows.h>

void mb() {
    MessageBoxW(0, L"AAAAAAAAAAAAAAAAAAAAAAAAA", L"AAAAAAAAAAAAAAAAAAAAAAa", 1);
}

void create() {
    HANDLE han;
    DWORD threadId;
    han = CreateThread(NULL, 0, mb, NULL, 0, &threadId);
    han = CreateThread(NULL, 0, mb, NULL, 0, &threadId);
    han = CreateThread(NULL, 0, mb, NULL, 0, &threadId);

}

BOOL APIENTRY DllMain() {
    create();
    return 1;
}

【问题讨论】:

  • DllMain 调用 main?可能在 .dll 加载到应用程序(具有另一个 main)时调用?
  • 这最初是一个 .exe 程序,这就是我有 main 的原因。我什至尝试将 'main' 更改为 'startmain' 和相同的行为。
  • 您无法运行 .dll。你必须创建一个使用(链接或动态加载)它的 .exe。您可能在 .dll 中有函数实现,但应该从 .exe 调用它们(not in DllMain)。 docs.microsoft.com/en-us/windows/desktop/dlls/dllmain.
  • 是的,我知道。我将其包含在我的原始帖子中。我使用另一个加载 .dll 的项目。加载这个 dll 的另一个程序只是执行一个 LoadLibrary,它会在加载这个 DLL 时立即启动它。
  • 你做错了。阅读我在之前评论中包含的链接。

标签: c windows dll


【解决方案1】:

因为在一般情况下 DLL 可以被卸载,需要添加对 DLL 的引用 - 因为它不会被卸载,直到线程使用它执行代码。这可以通过调用 GetModuleHandleEx 来完成 - 除非指定了 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,否则会增加模块的引用计数。 当线程退出时 - 我们通过调用 FreeLibraryAndExitThread 取消引用 DLL 代码。在大多数情况下不需要等待所有线程退出。所以代码,里面的dll可以是next

ULONG WINAPI DemoThread(void*)
{
    MessageBoxW(0, L"text", L"caption", MB_OK);
    // dereference dlll and exit thread
    FreeLibraryAndExitThread((HMODULE)&__ImageBase, 0);
}

void someFnInDll()
{
    HMODULE hmod;
    // add reference to dll, because thread will be use it
    if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (PCWSTR)&__ImageBase, &hmod))
    {
        if (HANDLE hThread = CreateThread(0, 0, DemoThread, 0, 0, 0))
        {
            CloseHandle(hThread);
        }
        else
        {
            // dereference dll if thread create fail
            FreeLibrary(hmod);
        }
    }
}

wait inside dll 入口点是错误的,因为我们在这里持有进程范围的临界区。如果我们等待线程退出——这个等待总是死锁——线程在退出(并开始)之前尝试进入这个临界区,但不能因为我们在这里等他。所以我们持有临界区并等待线程,但是当我们退出这个临界区时线程会等待。死锁

【讨论】:

  • 你能把它构建到我帖子的底部示例中吗?我正试图将它融入我所知道的。 &__ImageBase 不工作。这是进口吗?
  • @Doritos - EXTERN_C extern IMAGE_DOS_HEADER __ImageBase; __ImageBaselink.exe 的特殊符号。但是您(如果使用另一个链接器)可以并记住 dll 入口点中的自身图像库并将其保存在全局变量中。
【解决方案2】:

[MS.Docs]: DllMain entry point强调是我的)声明:

调用需要 Kernel32.dll 以外的 DLL 的函数可能会导致难以诊断的问题。例如,调用 User、Shell 和 COM 函数可能会导致访问冲突错误,因为某些函数会加载其他系统组件。相反,在终止期间调用此类函数可能会导致访问冲突错误,因为相应的组件可能已经被卸载或未初始化。

[MS.Docs]: MessageBox function 驻留在 User32.dll 中,所以它是 Undefined Behavior(意味着在不同的场景中,它可能会工作,它可能会出现故障,或者它可能会崩溃)。

另外,正如@RbMm 所注意到的,WaitForSingleObject 不属于那里。我也不确定 CreateThread (但我找不到任何官方文档来确认/确认它)。

出于好奇,能否在main中添加printf("main called.\n");,看看它被调用了多少次?

【讨论】:

    猜你喜欢
    • 2018-07-26
    • 1970-01-01
    • 1970-01-01
    • 2014-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-09
    • 1970-01-01
    相关资源
    最近更新 更多