【问题标题】:Windows DLL function behaviour is different if DLL is moved to different location如果将 DLL 移动到不同的位置,Windows DLL 函数的行为会有所不同
【发布时间】:2021-09-24 02:53:51
【问题描述】:

我正在尝试在 CI 机器上调试 Unreal 中的 DLL 的一些非常不透明的问题(有关更多信息,请参阅 Unreal: Diagnosing why Windows cannot load a DLL)。 glu32.dll 似乎是 Unreal 进程崩溃的 DLL,并且由于 Windows Server 不包含普通 Windows 10 所包含的所有与图形相关的 DLL,因此建议我从我的机器/Microsoft 可再发行组件中上传某些 DLL以确保 Unreal 构建过程可以运行。

出于理智的目的,我编写了一个小实用程序来测试我机器上的glu32.dll 是否可以动态加载并可以正确调用其函数。我打算很快在麻烦的 CI 机器上运行这个可执行文件,看看会发生什么。

程序代码如下:

#include <windows.h> 
#include <iostream>
#include <GL/gl.h>

extern "C"
{
    typedef const GLubyte* (__stdcall *ErrorStringFunc)(GLenum error);
}

int main(int argc, char** argv)
{
    if (argc < 2)
    {
        std::cerr << "Usage: GLU32Loader.exe <path to glu32.dll>" << std::endl;
        return 1;
    }

    const char* path = argv[1];
    std::cout << "Attempting to load: " << path << std::endl;

    HMODULE dllHandle = LoadLibraryA(path);

    if (!dllHandle)
    {
        std::cerr << "Could not load " << path << std::endl;
        return 1;
    }

    std::cout << "Successfully loaded DLL: 0x" << dllHandle << std::endl;

    const char* funcName = "gluErrorString";

    std::cout << "Looking up function: " << funcName << std::endl;
    ErrorStringFunc func = reinterpret_cast<ErrorStringFunc>(GetProcAddress(dllHandle, funcName));

    if (func)
    {
        std::cout << "Successfully loaded function: 0x" << func << std::endl;

        const GLubyte* str = (*func)(100902);
        std::cout << "Error string for value 100902: \"" << str << "\" (0x" << static_cast<const void*>(str) << ")" << std::endl;
    }
    else
    {
        std::cerr << "Failed to load function " << funcName << std::endl;
    }

    FreeLibrary(dllHandle);

    return 0;
}

当我运行可执行文件并将其指向System32 文件夹中的glu32.dll 时,我得到了预期的输出:

> GLU32Loader.exe "C:\Windows\System32\glu32.dll"
Attempting to load: C:\Windows\System32\glu32.dll
Successfully loaded DLL: 0x00007FFC7A350000
Looking up function: gluErrorString
Successfully loaded function: 0x00007FFC7A35C650
Error string for value 100902: "out of memory" (0x000001E5757F51D0)

但是,如果我将 DLL 复制到我的桌面并再次运行程序,虽然 DLL 和函数似乎已加载,但函数返回的字符串为空:

> GLU32Loader.exe "C:\Users\Jonathan\Desktop\glu32.dll"
Attempting to load: C:\Users\Jonathan\Desktop\glu32.dll
Successfully loaded DLL: 0x00007FFC8DDB0000
Looking up function: gluErrorString
Successfully loaded function: 0x00007FFC8DDBC650
Error string for value 100902: "" (0x0000025C5236E520)

为什么会这样?它是完全相同的 DLL,只是在不同的文件夹中,我认为它引用的任何其他依赖 DLL 应该仍然可用,因为它们都在 System32 中。是否有一些我不熟悉的 Windows DLL 的神秘属性可能导致这种情况发生?

【问题讨论】:

  • 这是一个奇怪的具体问题,在这两种情况下我都会使用调试器进入 gluErrorString 看看发生了什么有什么不同
  • 调试器实际上并没有让我看到 gluErrorString 的内容。我不知道这是因为它的 PDB 在我的机器上不可用,还是因为我完全动态链接到它。
  • 您也可以使用进程监视器查看运行时访问的文件是否有任何差异。
  • 调试器:如果你在你的代码中设置了一个断点,然后“进入”它应该工作的函数调用,你应该得到 glu32 的反汇编代码并能够单步执行它 -当然不是来源,而是汇编
  • Visual Studio 似乎甚至不允许我进入程序集...

标签: c++ c windows dll


【解决方案1】:

这是一个示例,说明为什么不要乱用系统 DLL。

有问题的 DLL 与许多 Microsoft DLL 一样,使用 MUI(多语言用户界面)。

如果你查看它的资源,它除了 MUI 类型的资源外没有其他资源,指向包含相应 .mui 文件的文件夹,该文件包含它的实际(国际化)资源。

所以,如果还想复制,至少也要复制对应的.mui文件:

  • System32\glu32.dll&lt;my_files&gt;\glu32.dll
  • System32\en-US\glu32.dll.mui&lt;my_files&gt;\en-US\glu32.dll.mui

en-US 部分在您的系统上可能会有所不同,具体取决于默认语言环境。

【讨论】:

  • 这几乎肯定是问题所在。我会在我的机器上测试它,如果它有效,我会批准这个答案。谢谢!
  • 啊,MUI,我忘记了。你说的对。因此,在缺少 MUI 文件的情况下,LoadString 返回 0glu32 的行为不会将此视为错误,而是将其视为零长度字符串,这也将如我的回答中所述。
【解决方案2】:

编辑:我现在才从您的日志中看到您没有重命名文件。然后我不确定它会是什么。无论如何我都会留下这个解释,因为如果重命名该文件也会发生这种情况,所以也许它对其他人有帮助......


在我看来,您重命名了 DLL 文件(不仅是从另一个位置加载它,而且还使用了另一个文件名)。

glu32.dll 不喜欢被重命名,因为在某些地方使用像 GetModuleHandle("glu32.dll") 这样的代码,而不是将在 DllMain 中收到的 hInstance 保存到全局变量中并使用该句柄(这就是 应该已经完成,但不幸的是它不是微软所做的)。如果重命名 DLL,此调用将返回 NULL。现在不幸的是,在这种情况下,glu32 中也没有进行太多错误处理。

错误字符串存储在某种全局数组中,但它们是从字符串表资源中延迟加载的。第一次调用gluErrorString 时,错误字符串使用LoadString 加载,它采用DLL 的hInstance。使用重命名的 DLL,这将是伪造的 NULL 句柄,调用 LoadString(NULL, ...) 将返回 0,指示错误。通常返回的数字是字符串的长度。 glu32 不以任何特殊方式处理零大小写,只是将零字符复制到数组中,并在最后愉快地返回一个空字符串。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-01
    • 2021-11-19
    • 2021-03-13
    • 2012-05-21
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多