【问题标题】:Can I get separate DLLs to resolve their imported DLLs separately?我可以获取单独的 DLL 来分别解析其导入的 DLL 吗?
【发布时间】:2016-08-01 11:58:04
【问题描述】:

我有一些这样排列的 DLL:

  • lib.dll - 带有您链接到的导入库的第 3 方 DLL
  • plugin.dll - 一个链接到 lib.dll 的 DLL,该插件旨在通过 LoadLibrary 加载到主机程序中
  • other\plugin.dll - 另一个链接到 lib.dll 的插件 DLL
  • other\lib.dll - otherplugin 的 lib.dll 副本

(这些只是带有一些导出函数的普通 DLL - 我没有使用 COM 或任何东西。)

在我的测试设置中,如果我使用LoadLibrary 加载plugin.dll,然后使用LoadLibrary 加载other\plugin.dll,似乎它们都共享相同的lib.dll 加载副本。但我实际上需要other\plugin.dll 来加载other\lib.dll - 我不希望共享这些库。

我可以这样做吗? (理想情况下无需重新编译lib.dll。)

【问题讨论】:

    标签: c++ windows winapi dll


    【解决方案1】:

    实现这一点的技术称为应用程序隔离。它的工作方式是让开发人员使用清单文件将他们的 dll 安排到 Side by Side 程序集中。

    阅读 MSDN 上的Isolated Applications 获取官方文档。


    或者,如果您实际上是通过LoadLibrary 显式加载“lib.dll”,那么这可以在没有所有这些的情况下工作。

    首先,请注意,当 Dll 调用 LoadLibrary 时,不会搜索 DLL 自己的文件夹。如果您在 MSDN 上查看 LoadLibrary 的文档,您将看到应用程序可执行文件文件夹是首选搜索位置。所以你需要做的第一件事是在加载 Plugin.dll 之前调用SetDllDirectory,这样它才能真正找到自己的“lib.dll”副本。

    下一步 - 请注意,LoadLibrary 搜索路径仅在传递相对路径时使用。

    因此,如果您首先修复 DllDirectory 和 LoadLibrary “Plugin.dll”,然后调用 LoadLibrary,将完全限定路径传递给您自己的 Lib.dll 副本,那么 plugin.dll 将使用搜索路径加载其自己的版本,并且您的应用将显式加载自己的版本。

    【讨论】:

    • lib.dll 通过导入库隐式加载。
    • 那么,您可能会被困在一个私有的并行程序集中包装“plugin.dll”,或者使用激活上下文 API 将 plugin.dll 的负载包装在不同的激活上下文中。我对这类问题有很多答案,但现在我和 5 岁的孩子对这个话题的了解都太陈旧了,他们都让我感到困惑:P
    【解决方案2】:

    还有另一种选择:将 DLL 设置为延迟加载,并使用the delay load DLL import hook 显式加载所需的 DLL。

    在解决延迟加载的 DLL 时,通常系统(虽然我不确定哪个部分 - 大概是 CRT?)会发现有一个 plugin.dll 已经加载并分配另一个句柄给它。但是使用延迟加载 DLL 挂钩,您可以使用 LoadLibrary 加载另一个 DLL - 在这种情况下,共享 DLL 的插件特定副本。提供完整路径,因此LoadLibrary 不可能返回现有 DLL 的句柄。

    为此,请将 DLL 的 HINSTANCE 存储在 DllMain 中。

    static HINSTANCE g_hinstDLL;
    
    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
        if (fdwReason == DLL_PROCESS_ATTACH)
            g_hinstDLL = hinstDLL;
    
        return TRUE;
    }
    

    并且有一个合适的延迟加载钩子来监视dliNotePreLoadLibrary

    static FARPROC WINAPI DliHook(unsigned dliNotify, PDelayLoadInfo pdli) {
        if (dliNotify == dliNotePreLoadLibrary) {
            if (strcmp(pdli->szDll, "plugin.dll") == 0) {
                char path[MAX_PATH];
                GetModuleFileNameA(g_hinstDLL, path, sizeof path);
                PathRemoveFileSpecA(path);
                PathAppendA(path, "plugin.dll");
    
                HMODULE hModule = LoadLibraryA(path);
                return (FARPROC)hModule;
            }
        }
    
        return 0;
    }
    
    const PfnDliHook __pfnDliNotifyHook2 = &DliHook;
    

    (最后我不得不放弃整个方法 - lib.dll 似乎认为它只会在每个进程中加载​​一次,并非不合理,并且在多次加载时会以各种方式与自身发生冲突。可能可以解决原则上,但我希望我只需要为 Linux 和 OS X 再次做同样的事情......)

    【讨论】:

      猜你喜欢
      • 2014-01-29
      • 2010-09-20
      • 2014-04-23
      • 1970-01-01
      • 2010-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多