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

发布于 2024-09-25 13:40:15 字数 247 浏览 1 评论 0原文

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

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

I've searched for any hints how I can do this, but all I found was how to redirect a SxS DLL to the local application folder.
Here is what I want to accomplish:
The (C++) Application.exe is linked to a DLL, Plugin.DLL (dependant project). This DLL is not placed inside the application directory, but in a subfolder called "plugins". As the DLL is statically linked, the application would try to load it from the application folder.

Is there any way how I can change the search path for this particular DLL? Either through manifests or VS2008 linker configurations?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

咿呀咿呀哟 2024-10-02 13:40:15

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

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

  • 不要静态链接。使用 LoadLibrary("plugins/Plugin.dll") & GetProcAddress 访问插件内容。
  • 将“插件文件夹的路径”添加到系统 PATH 环境变量中。
  • 使用延迟加载机制延迟访问插件功能,设置自定义帮助函数 可以使用提供的路径加载 dll。
  • 将插件文件夹转换为程序集(通过在其中创建列出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'\"")

My first thought is, if you are statically linking a dll, it isnt a plugin. Just put the dll in the EXE folder and be done with it. That is the deployment configuration supported by windows for statically loaded DLLs.

That said, there are ways to achieve what you want. But they are mostly stupid, or complicated for no good reason: Your options are:

  • Don't statically link. Use LoadLibrary("plugins/Plugin.dll") & GetProcAddress to access plugin content.
  • Add "the path to your plugins folder" to the systems PATH environment variable.
  • Use the delay load mechanism to delay accessing the plugins functionality, set a custom helper function that can load the dll(s) using a provided path.
  • Turn the plugins folder into an assembly (by creating a .manifest file in it that lists the plugin.dll). Add "plugins" as a dependent assembly to your app. Now it will look in the plugins folder.
  • Split your application into a stub exe and a dynamically loaded part. In the stub exe call SetDllDirectory to point to the plugin folder, then call LoadLibrary passing the full path to "appstub.dll".

To turn a folder, with one or more dll's into an "assembly", simply add a file to the folder with the folders name.manifest.

So, plugins.manifest :-

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

It is a VERY good idea to ensure that the folder and the dll's name is different as if the dll name is the assembly name windows starts looking at its embedded manifest file for information about the assembly.

Assuming you are using Visual Studio 7 or later, the following directive added to a .c/.cpp or .h file in the project will then make your application attempt to load dlls from the assembly rather than just the local directory:

#pragma comment(linker, "/manifestdependency:\"name='Plugins' "\
                        "processorArchitecture='*' version='1.0.0.0' "\
                        "type='win32'\"")
岁月静好 2024-10-02 13:40:15

扩展并详细说明克里斯对“程序集”子目录的提议


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


MS docs

这实际上称为 “Private Assembly” 和 MS 文档是这样解释的:

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

例如(...)

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

(...)

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

您在程序文件夹

C:\Test\Program\app.exe 中找到了值得信赖的旧可执行文件,并且您想要 -- at Load-Time -- 从插件加载 DLL 文件 子文件夹,即 C:\Test\Program\plugins\tool1.dll 不会弄乱 PATH 或任何其他内容。

您需要:

  • 使用以下内容编译 app.exe:

    #pragma comment(linker, "/manifestdependency:\"name='Plugins' version='1.0.0.0' type='win32'\"")
    // 名称、类型和版本似乎是最少的信息
    

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

    但是,也可以使用 mt 工具(而不是使用链接器编译指示)将下面列出的清单文件嵌入/合并到可执行文件中。 (配置 > 清单工具 > 其他清单文件

  • 将 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>

Expanding and detailing Chris' proposal of an "assembly" subdirectory:


Side note: Chris also has two excellent write ups of more details:


MS docs

This is actually called a "Private Assembly" and MS docs explain it this way:

Private assemblies are installed in a folder of the application's
directory structure. Typically, this is the folder containing the
application's executable file. Private assemblies may be deployed in
the same folder as the application, in a folder with the same name as
the assembly
, or in a language specific subfolder with the same name
as the assembly.

For example (...)

Appdir\Microsoft.Tools.Pop\Microsoft.Tools.Pop.MANIFEST : The manifest is deployed as a separate file in a subfolder having the assembly's name.

(...)

Private assemblies can be installed by any installation method that can copy the assembly's file into this folder, such as the xcopy command.

The example

You got you're trusty old executable in you program folder C:\Test\Program\app.exe and you want to -- at Load-Time -- load your DLL file from the Plugins subfolder, i.e. C:\Test\Program\plugins\tool1.dll without messing with PATHor any other stuff.

You need to:

  • 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>

That's it. Starting app.exe will automatically find the dll in the subfolder at Load-Time.


(*a) : Merging this manifest file works, but you can't use that as the sole external manifest file, and I suspect it's because it's missing all the other manifest info the build system already places into your executable!

<?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>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文