【问题标题】:Load a DLL from another directory at program start在程序启动时从另一个目录加载 DLL
【发布时间】:2012-05-23 07:02:38
【问题描述】:

我的基本问题是:我的程序 (MyProgram.exe) 依赖于另一个程序 (OtherProgram) 的 DLL,我试图避免每次 OtherProgram 更新时都重新打包一个新的 DLL。我想在 MyProgram.exe 启动时在 OtherProgram 的 DLL 中有链接,但我不完全确定 Windows 是否允许这样做。因此,如果有某种解决方法也是可以接受的。

仅作为背景,平台是 Windows 7 x64,当我在 MyProgram.exe 项目目录中创建指向 OtherProgram 安装目录中 DLL 的符号链接时,MyProgram.exe 运行良好。当我尝试在没有符号链接的情况下运行它时,出现“程序无法启动,因为您的计算机中缺少 OtherProgramDLL.dll”错误。

非常感谢任何建议或相关信息的链接!

编辑:澄清:DLL 在编译时未链接,此问题在运行时出现

【问题讨论】:

  • 一种选择是使用delay loading 来控制用于DLL 的路径,而不必弄乱全局PATH 变量。例如看这篇文章:codemaestro.com/articles/6
  • 您的编辑毫无意义。所有 DLL 在运行时加载。
  • 您是否使用LoadLibrary 加载DLL?
  • 这个 SO 答案有更多关于延迟加载选项:stackoverflow.com/questions/280485/…
  • MSDN 页面指的是两种类型的动态链接。第一个也可能是最常用的方法是在程序启动时自动加载 DLL。这称为Load-Time Linking。第二个是当您在代码中显式调用LoadLibrary 以加载DLL 时。这称为Run-Time Linking,它们是互斥的。使用其中一种。

标签: c++ windows visual-studio dll


【解决方案1】:

在 Windows 世界中有两种类型的动态链接:

  1. Load-Time 链接是在程序启动时自动加载 DLL。 Windows 使用我将在下面讨论的特定算法找到此 DLL。
  2. Run-Time 链接是指 通过在代码中调用 LoadLibrary 来专门加载 DLL。类似的规则适用于如何找到库,但您可以指定完全限定或相对限定的路径来控制搜索。

对于加载时链接,MS 建议将程序的 DLL 存储在加载应用程序的同一目录中并从该目录加载。如果这完全可行,这可能是您的最佳选择。

如果这不起作用,还有其他几个选项,outlined here。一种是通过将 DLL 放在工作目录或加载应用程序的目录中来利用搜索顺序。

您可以通过以下方式更改应用程序的工作目录:

  1. 创建应用程序的快捷方式。
  2. 调出快捷方式的属性
  3. 使用 DLL 所在的目录编辑“Start in”属性。

当您使用快捷方式启动应用程序时,它将加载正确的 DLL。

加载时链接的其他选项包括:

  • 向您的应用程序添加一个清单,指定您的依赖 assemblies 在哪里,或者,
  • 设置PATH

【讨论】:

  • 使用当前目录查找 DLL 是遗留行为,如果系统管理员已实施 KB2264107,则无法正常工作。
  • 这个知识库只为管理员添加选项,他可以从dll搜索路径中删除cwd,但默认情况下仍会搜索cwd。
【解决方案2】:

您可以使用LoadLibrary,但您需要一种方法来保证 DLL 的位置。这个 Wikipedia article 提供了如何在加载 DLL 后使用它的好例子。

【讨论】:

  • 错了。如果 exe 链接到库,无论您在哪里调用 LoadLibrary,都为时已晚。
  • 我没有投反对票,但您通常不想LoadLibrary DLL。当然,不仅如此,您还可以指定它所在的路径。
  • @LuchianGrigore:好吧,公平地说,它可以从库中取消链接。
  • @JohnDibling 我不同意你的第一条评论。在各种情况下,LoadLibrary 是最佳选择。而且您的第二条评论非常正确,这显然是这个答案所设想的。
  • @cjm571 不,我认为你不明白。您必须使用LoadLibraryGetProcAddress 并停止使用导致所谓的隐式链接 的.lib 文件。如果有很多功能要链接,那肯定会更不方便,但您可以获得灵活性。
【解决方案3】:

您可以将dll所在的目录添加到PATH环境变量中。

【讨论】:

  • 成功了!不敢相信我没有想到,哈哈。谢谢!
  • 虽然你可以做到这一点,但你是在用大锤敲碎坚果。每当我遇到更改全局PATH 变量的程序时,我都会有一种不好的感觉。
  • @David:同意。不要乱搞PATH。以后只会搞砸你。
  • 另一种选择是使用加载程序来搜索 dll 的路径,并使用更改的 非全局 PATH 环境变量运行“MyProgram.exe”。
【解决方案4】:

我一直在为同样的问题而苦苦挣扎,并且还发现了LoadLibrarySetDllDirectory、Qt 的addLibraryPath 等建议方法的死胡同。不管我怎么尝试,问题仍然存在,即应用程序在实际运行代码之前检查了库(但没有找到它们),因此任何代码解决方案都注定会失败。

我几乎绝望了,但后来发现了一种非常简单的方法,在像你这样的情况下也可能会有所帮助:使用批处理文件!(或在实际应用程序之前使用类似的加载器)

用于此目的的 Windows 批处理文件可能如下所示:

@echo off
PATH=%PATH%;<PATH_TO_YOUR_LIB>
<PATH_TO_YOUR_APP_EXE>

/edit:刚刚看到Luchian's answer 中的@SirDarius 评论描述了这种方式,所以只需将我的批处理代码位作为参考,所有学分都归他所有。

【讨论】:

  • SetDllDirectory 不会在没有延迟加载的 dll 的情况下工作,因为编译器会要求程序在您调用 SetDllDirectory 之前尝试加载 DLL。请参阅下面的答案。
【解决方案5】:

我正在处理的一个应用程序遇到同样的问题。

我不想使用运行时加载,因为有几十个函数需要手动创建函数指针。

Dibling 先生提到清单文件为我打开了一扇新的大门,但我遗憾地发现支持该功能的最旧版本的 Windows 是 Windows 7。它甚至无法在 Vista 上运行。

长话短说,一位熟悉Windows应用程序开发的朋友告诉我查找Delay-Loaded DLL,结果证明可以轻松完美地解决问题。它将 DLL 库的加载延迟到您手动执行的点,或者第一次调用它的函数时。因此,您只需在此之前将您的 DLL 路径添加到搜索路径,SetDllDirectory 会有所帮助。

以下是使其工作的步骤:

1) 指定要延迟加载到链接器的 DLL,通过您的 makefile、cmake 或 VS 属性页(链接器->VS2015 的输入)

2) 在程序开始时调用SetDllDirectory,在调用 DLL 之前。

一直到 VC6 都支持延迟加载的 DLL。 XP SP1 之后支持 SetDllDirectory。

【讨论】:

    【解决方案6】:

    使用指向第 3 方可执行文件的符号链接

    我发现Aaron Margosis 提倡的方法很有用。见:

    Using NTFS Junctions to Fix Application Compatibility Issues on 64-bit Editions of Windows

    基本上,为每个相关的第 3 方可执行文件创建符号链接。将这些符号链接文件放在您自己的相关可执行文件中。除了对目标的文件名更改外,“软”符号链接将解决加载时依赖关系,即使链接的目标被未来的更新更改。

    【讨论】:

      猜你喜欢
      • 2016-12-25
      • 1970-01-01
      • 2014-06-18
      • 1970-01-01
      • 2011-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-03
      相关资源
      最近更新 更多