我是否可以获取单独的 DLL 来单独解析其导入的 DLL

Can I get separate DLLs to resolve their imported DLLs separately?

本文关键字:DLL 单独解 导入 是否 获取 单独      更新时间:2023-10-16

我有一些DLL是这样安排的:

  • lib.dll - 具有链接到的导入库的第三方 DLL
  • plugin.dll - 链接到lib.dll的DLL,一个旨在加载到具有LoadLibrary的主机程序中的插件
  • otherplugin.dll - 另一个链接到lib.dll的插件 DLL
  • otherlib.dll - 其他插件的lib.dll副本

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

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

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

允许此操作的技术称为应用程序隔离。它的工作方式是让开发人员使用清单文件将其 dll 排列到并排程序集中。

有关官方文档,请阅读 MSDN 上的独立应用程序。


或者,这可以在没有所有这些的情况下工作,假设您实际上是通过 LoadLibrary 显式加载"lib.dll"。

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

接下来 - 请注意,加载库搜索路径仅在传递相对路径时使用。

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

还有另一种选择:将 DLL 设置为延迟加载,并使用延迟加载 DLL 导入挂钩显式加载所需的 DLL。

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

为此,请将 DLL 的 H实例存储在 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 做同样的事情......