LoadLibrary看似加载了错误的DLL

时间:2012-06-19 12:24:47

标签: c++ windows loadlibrary

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

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

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

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

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

尽可能接近,应用程序正在加载旧版本的QtXml.dll,并且运行时加载的DLL(以某种方式)加载新版本的QtCore,因为已经加载c:\qt\qt-4.5.2\lib;c:\common\lib {1}}没有所需的符号。

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

怎么会这样? PATH(或者它以递归方式调用以解析库的依赖关系)如何在PATH中加载以后的库?我找不到任何表明公共库目录被特别考虑的东西(它不是一个设置的DLL目录)。在构建过程中没有提到它,它只是开发人员在他们的LD_LIBRARY_PATH中为了方便起见而已。

顺便说一句,在dlopen()和{{1}}的Linux上存在类似的情况,它在那里工作得很好。这是Windows不同的做法,我不明白。有没有人对可能出现的问题有任何见解?

1 个答案:

答案 0 :(得分:3)

LoadLibrary有许多令人惊讶的行为。确保您完全了解MSDN中的所有Remarks

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

接下来,如果您指定了相对路径或仅指定了文件名,则LoadLibrary将应用arcane search rules。您的PATH变量通常是最后搜索的地方。一些其他规则可能在它甚至检查PATH之前找到“错误的”DLL。一个好的指导方针是始终使用要加载的文件的绝对路径,以确保其搜索规则不会获取错误的文件。一个常见的安全漏洞是无法控制LoadLibrary搜索的位置,攻击者会说服您的应用程序加载已修改的DLL。

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

我偶尔会在加载DLL时使用ProcMon from SysInternals来观察程序。你可以看到它检查的每个地方,这可能会给你一个关于它为什么找到错误版本的线索。