更改静态链接DLL的DLL搜索路径

14

我已经搜索了任何线索以确定如何完成此操作,但我找到的只是如何将SxS DLL重定向到本地应用程序文件夹的方法。以下是我想要实现的内容:

(C++) Application.exe链接到一个名为 Plugin.DLL(依赖项目)的DLL文件,但该DLL文件不位于应用程序目录中,而是位于名为“plugins”的子文件夹中。由于该DLL被静态链接,应用程序会尝试从应用程序文件夹中加载它。

是否有任何方式可以更改特定DLL文件的搜索路径?可以通过清单或VS2008链接器配置来实现吗?

2个回答

21

我的第一反应是,如果你在静态链接一个dll,它不是一个插件。只需将dll放入EXE文件夹中即可完成部署配置,这是Windows支持静态加载DLL的方式。

话虽如此,还有一些方法可以实现你想要的功能。但它们大多数都很愚蠢,或者没有必要变得复杂:你的选择有:

  • 不要静态链接。使用LoadLibrary("plugins/Plugin.dll")和GetProcAddress来访问插件内容。
  • 将“插件文件夹的路径”添加到系统的PATH环境变量中。
  • 使用延迟加载机制来延迟访问插件功能,设置一个自定义帮助函数,该函数可以使用提供的路径加载dll(s)。
  • 将插件文件夹转换为程序集(通过在其中创建一个.manifest文件并列出plugin.dll)。将“plugins”作为依赖程序集添加到您的应用程序中。现在它将在插件文件夹中查找。
  • 将您的应用程序拆分为存根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的方法,因为我不能弄清楚制作“伪装程序集”所需的清单应该是什么样子的。幸运的是,这个方法很好地配合了整体架构。 - Oliver
1
延迟加载时不需要使用 SetDllDirectory 函数;可以使用 __pfnDliNotifyHook2 钩子,直接调用 LoadLibrary(".\\plugins\\PluginX.dll")。使用 %PATH% 变量的想法有些脆弱,因为它在检查位置列表中排名较低。 - MSalters
谢谢。我个人从未使用过延迟加载,所以我只是假设SetDllDirectory是调整它的方法。 - Chris Becke
我在某些方面不同意。我支持提供第三方应用程序的OCX的应用程序。我们无法控制第三方应用程序。我们可以将OCX安装到与EXE相同的文件夹中。但是,第三方会在升级其应用程序时擦除此文件夹并重新创建它。这意味着我们的OCX需要重新安装。这对用户群非常有问题。我们将OCX部署到唯一的文件夹中,并将路径添加到PATH环境变量中。这很复杂,但并不愚蠢。 - Joe B

11

扩展和详细阐述Chris的提议,“装配”子目录:


附注:Chris还有两篇更详细的文章:


MS文档

实际上,这被称为"私有程序集",MS文档这样解释:

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

例如(...)

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

(...)

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

示例

你有一个可靠的可执行文件在程序文件夹中 C:\Test\Program\app.exe,你想要在 加载时Plugins 子文件夹中加载你的 DLL 文件,即 C:\Test\Program\plugins\tool1.dll不需要 操纵 PATH 或其他任何东西。

你需要:

  • Compile app.exe with:

    #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
    

    Note: Compiling/Linking this in, instead of using an external manifest (app.exe.manifest) is required on my test system, I didn't find out why yet. (*a)

    What also works however is embedding/merging the manifest file listed below into the executable with the mttool, instead of with the linker pragma. (Configuration > Manifest Tool > Additional Manifest Files)

  • put tool1.dll intro the plugins subfolder

  • add a plugins.manifest file into the plugins subfolder, i.e. C:\Test\Program\plugins\plugins.manifest and that one looks like this:

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将自动在Load-Time中的子文件夹中找到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>

2
numpy维基@https://github.com/numpy/numpy/wiki/windows-dll-notes也有一个很好的介绍。 - Martin Ba
1
外部清单文件仅在没有嵌入式清单文件时使用。现在,VS编译器默认嵌入清单,因此如果要运行时加载清单,则需要先将其删除。 - Chris Becke
我确认使用额外清单的方法适用于使用VS2017构建的可执行文件。 - AntonK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接