【问题标题】:Altering DLL search path for static linked DLL更改静态链接 DLL 的 DLL 搜索路径
【发布时间】:2011-04-19 10:31:27
【问题描述】:

我已经搜索了如何执行此操作的任何提示,但我发现的只是如何将 SxS DLL 重定向到本地应用程序文件夹。 这是我想要完成的事情: (C++) Application.exe 链接到一个 DLL、Plugin.DLL(依赖项目)。这个 DLL 没有放在应用程序目录中,而是放在一个名为“plugins”的子文件夹中。由于 DLL 是静态链接的,因此应用程序会尝试从应用程序文件夹中加载它。

有什么方法可以改变这个特定 DLL 的搜索路径吗?通过清单或 VS2008 链接器配置?

【问题讨论】:

    标签: c++ dll linker redirect manifest


    【解决方案1】:

    我的第一个想法是,如果你静态链接一个 dll,它就不是一个插件。只需将dll放在EXE文件夹中即可。这是windows支持的静态加载DLL的部署配置。

    也就是说,有很多方法可以实现您想要的。但它们大多是愚蠢的,或者没有充分理由的复杂:你的选择是:

    • 不要静态链接。使用 LoadLibrary("plugins/Plugin.dll") 和 GetProcAddress 访问插件内容。
    • 将“插件文件夹的路径”添加到系统 PATH 环境变量中。
    • 使用延迟加载机制来延迟访问插件功能,设置一个custom helper function 可以使用提供的路径加载 dll。
    • 将 plugins 文件夹变成一个程序集(通过在其中创建一个列出 plugin.dll 的 .manifest 文件)。将“插件”作为依赖程序集添加到您的应用程序中。现在它将在插件文件夹中查找。
    • 将您的应用程序拆分为一个存根 exe 和一个动态加载的部分。在存根 exe 中调用 SetDllDirectory 指向插件文件夹,然后调用 LoadLibrary 将完整路径传递给“appstub.dll”。

    要将具有一个或多个 dll 的文件夹转换为“程序集”,只需将文件添加到文件夹名称为 .manifest 的文件夹中。

    所以,plugins.manifest :-

    <assembly manifestVersion="1.0">
      <assemblyIdentity type="Win32" name="Plugins" version="1.0.0.0" processorArchitecture="x86" />
      <file name="Plugin.dll"/>
    </assembly>
    

    确保文件夹和 dll 的名称不同是一个非常好的主意,就像 dll 名称是程序集名称一样,windows 开始查看其嵌入的清单文件以获取有关程序集的信息。

    假设您使用的是 Visual Studio 7 或更高版本,添加到项目中的 .c/.cpp 或 .h 文件的以下指令将使您的应用程序尝试从程序集加载 dll,而不仅仅是本地目录:

    #pragma comment(linker, "/manifestdependency:\"name='Plugins' "\
                            "processorArchitecture='*' version='1.0.0.0' "\
                            "type='win32'\"")
    

    【讨论】:

    • 谢谢 - 我现在使用 GetProcAddress 方法,因为我无法弄清楚清单必须如何才能使“假组装”方法起作用。幸运的是,这适用于整体架构。
    • 延迟加载不需要SetDllDirectory;有一个__pfnDliNotifyHook2 钩子,所以你可以直接调用LoadLibrary(".\\plugins\\PluginX.dll")%PATH% 的想法有点脆弱,因为它在要检查的位置列表中相当低。
    • 谢谢。我从来没有亲自使用过延迟加载,所以我只是假设 SetDllDirectory 将是调整它的方法。
    • 我部分不同意。我支持为第 3 方应用程序提供 OCX 的应用程序。我们无法控制第 3 方应用程序。我们可以将 OCX 安装到与 EXE 相同的文件夹中。但是,第 3 方会删除此文件夹并在其应用程序升级时重新创建它。这意味着我们的 OCX 需要重新安装。这对于用户群来说是非常有问题的。我们将 OCX 部署到一个唯一的文件夹,并将路径添加到 PATH 环境变量。这很复杂,但并不愚蠢。
    【解决方案2】:

    “程序集”子目录的扩展和detailing Chris' proposal


    旁注:克里斯还有两篇关于更多细节的精彩文章:


    MS 文档

    这实际上称为"Private Assembly",MS 文档是这样解释的:

    私有程序集安装在应用程序的文件夹中 目录结构。通常,这是包含 应用程序的可执行文件。私有程序集可以部署 与应用程序相同的文件夹,在与应用程序同名的文件夹中 程序集,或在具有相同名称的特定语言子文件夹中 作为大会。

    例如(...)

    Appdir\Microsoft.Tools.Pop\Microsoft.Tools.Pop.MANIFEST :清单作为单独的文件部署在具有程序集名称的子文件夹中。

    (...)

    可以通过任何可以将程序集文件复制到此文件夹中的安装方法来安装私有程序集,例如xcopy 命令。

    例子

    你在你的程序文件夹C:\Test\Program\app.exe 中有一个可靠的旧可执行文件,并且你想 -- at Load-Time -- 从Plugins 子文件夹加载你的 DLL 文件,即C:\Test\Program\plugins\tool1.dll 没有 em> 搞乱PATH或任何其他东西。

    你需要:

    • 使用以下命令编译 app.exe:

      #pragma comment(linker, "/manifestdependency:\"name='Plugins' version='1.0.0.0' type='win32'\"")
      // name, type and version seems to be the minimum info to get away with
      

      注意:在我的测试系统上需要编译/链接它,而不是使用外部清单 (app.exe.manifest),我还没有找到原因。 (*a)

      同样起作用的是,使用mttool,而不是使用链接器编译指示,将下面列出的清单文件嵌入/合并到可执行文件中。 (Configuration &gt; Manifest Tool &gt; Additional Manifest Files)

    • 将 tool1.dll 引入plugins 子文件夹

    • 添加一个 plugins.manifest 文件 plugins 子文件夹,即C:\Test\Program\plugins\plugins.manifest,它看起来像这样:

    plugins.manifest:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
        <assemblyIdentity
                                type="win32"
                                name="Plugins"
                                version="1.0.0.0"
                />
        <file name="tool1.dll"/>
    </assembly>
    

    就是这样。启动 app.exe 会在加载时自动在子文件夹中找到 dll。


    (*a) :合并此清单文件有效,但您不能将其用作唯一的外部清单文件,我怀疑这是因为它缺少构建系统已经放入可执行文件中的所有其他清单信息!

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
        <dependency>
            <dependentAssembly>
                <!-- Note: type: The value must be win32 and all in lower case. Required. --> 
                <!-- Note: version: The value must be win32 and all in lower case. Required. --> 
                <assemblyIdentity
                                        type="win32"
                                        name="plugins"
                                        version="1.0.0.0"
                />
            </dependentAssembly>
        </dependency>
    </assembly>
    

    【讨论】:

    • numpy wiki @github.com/numpy/numpy/wiki/windows-dll-notes 也有一篇很好的文章
    • 只有在没有嵌入清单文件的情况下才使用外部清单文件。 VS 编译器现在默认嵌入清单,因此只有在您首先删除清单时才能在运行时加载清单。
    • 我确认使用 VS2017 构建的可执行文件的额外清单工作的方法。
    猜你喜欢
    • 2011-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-20
    • 2011-04-09
    • 2010-09-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多