加载库似乎加载了错误的 DLL

LoadLibrary seemingly loading wrong DLL

本文关键字:加载 DLL 错误      更新时间:2023-10-16

我在Windows上的LoadLibrary上遇到了一个奇怪的问题。 首先是一些背景。 这个应用程序依赖于Qt,Qt被分成几个库。 我正在尝试升级Qt的版本,但不会破坏任何人。 较新的Qt库向后兼容旧的Qt库。 这意味着,使用较旧版本构建的应用程序在加载较新版本时可以运行。 反之则不然 - 如果加载较旧的版本,则使用较新版本构建的应用程序将缺少符号。

Qt DLL位于特定于版本的目录中(例如c:qtqt-4.5.2libc:qtqt-4.8.1lib)。 大多数开发人员在其 PATH 中还有一个通用目录,其中包含我们使用的所有第三方库的"当前"版本(称之为 c:commonlib )。 这是运行应用程序时通常可以找到Qt库的地方。

我将新的Qt版本库放在公共位置,除了一种情况外,一切似乎都运行良好。 有问题的应用程序被拆分为多个库,其中一些库是通过调用LoadLibrary()加载的。 其中一些运行时加载的DLL依赖于Qt库。 在一种情况下,加载的DLL依赖于QtXml,而本身取决于QtCore

这就是它变得奇怪的地方。 应用程序依赖于QtCore,并且还加载依赖于QtXml的库。 应用程序和库是与旧版本的Qt链接构建的。 如果此应用程序仅使用 PATH 中的公共目录运行,则一切正常,因为新的 Qt 版本 DLL 是从公共目录加载的。 但是,如果 PATH 在公共目录之前包含存储旧 Qt 版本 DLL 的目录,则加载运行时 DLL 将失败并缺少符号。

(在执行自动单元测试时会出现这种情况,脚本显式设置 PATH 以使用特定的库版本。

据我所知,应用程序正在加载旧版本的QtCore.dll,运行时加载的 DLL 正在(以某种方式)加载新版本的 QtXml.dll ,这失败了,因为已经加载的QtCore没有它需要的符号。

但这似乎是不可能的,因为 PATH 类似于 c:qtqt-4.5.2lib;c:commonlib(加上其他不相关的路径)。 如果我从公共 lib 目录中删除较新的QtXml(不是用旧版本替换它,只是删除它),那么LoadLibrary()成功,因为它加载了所有 Qt 库的 4.5.2 版本。 但这不是一个很好的长期解决方案,因为在 PATH(通用)中没有 Qt 特定版本目录的情况下运行将无法找到QtXml

这怎么可能? LoadLibrary()(或任何递归调用以解析库的依赖项)如何在PATH后面加载库? 我找不到任何表明公共库目录受到特殊考虑的内容(它不是设置的 DLL 目录)。 在构建过程中没有提到它,它只是开发人员为了方便起见而PATH的东西。

顺便说一句,在 Linux 上也存在类似的情况 LD_LIBRARY_PATHdlopen() ,它在那里工作得很好。 这是Windows正在做的不同的事情,我不明白。 有没有人知道可能出现什么问题?

LoadLibrary有很多令人惊讶的行为。 确保在 MSDN 中完全了解它的所有备注。

如果已加载具有相同名称的库(任何版本),则 LoadLibrary 只需返回已加载 DLL 的句柄。 这可能会在您的场景中发挥作用。

接下来,如果您指定了相对路径或仅指定了文件名,则 LoadLibrary 将应用晦涩难懂的搜索规则。 您的 PATH 变量通常是最后一个搜索位置。 很可能其他一些规则在检查 PATH 之前就找到了"错误"的 DLL。 一个好的准则是始终使用要加载的文件的绝对路径,以确保其搜索规则不会抓取错误的文件。 一个常见的安全漏洞是不控制 LoadLibrary 的搜索位置,攻击者诱使应用程序加载篡改的 DLL。

最后,安装程序可以应用 DLL 重定向,该重定向可以覆盖您请求的内容以及可能找到它的位置。 我不确定这是否适用于Qt DLL,但您可能需要检查注册表。

我偶尔会使用 SysInternals 的 ProcMon 来观察加载 DLL 的程序。 您可以看到它检查的每个位置,这可能会为您提供有关为什么找到错误版本的线索。