LoadLibrary似乎加载了错误的DLL

我在Windows上的LoadLibrary有一个奇怪的问题。 首先是一些背景。 这个应用程序依赖于Qt,而Qt被分成几个库。 我正在尝试升级Qt版本,但没有打破任何人。 较新的Qt库与旧版本是向后兼容的。 这意味着使用较旧版本构build的应用程序可以在加载较新版本的情况下运行。 相反是不正确的 – 如果较旧版本的应用程序已加载,则使用较新版本构build的应用程序将会丢失符号。

Qt DLLs是特定于版本的目录(例如c:\qt\qt-4.5.2\libc:\qt\qt-4.8.1\lib )。 还有一个共同的目录,大多数开发者在其PATH中包含我们使用的所有第三方库(称为c:\common\lib )的“当前”版本。 这是运行应用程序时通常会findQt库的地方。

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

这是奇怪的地方。 一个应用程序依赖于QtCore ,并加载一个依赖于QtXml的库。 应用程序和库build立了与旧版Qt的链接。 如果这个应用程序只用PATH中的公共目录运行,那么所有的东西都可以工作,因为新的Qt版本DLL是从公共目录加载的。 但是,如果PATH包含在公用目录之前存储旧Qt版本DLL的目录,则加载运行时DLL失败并丢失符号。

(这种情况出现在进行自动unit testing时,脚本明确地将PATH设置为使用特定的库版本。)

就我QtCore.dll ,应用程序正在加载旧版本的QtCore.dll ,运行时加载的DLL(以某种方式)加载了新版本的QtXml.dll ,由于已加载的QtCore没有符号需要。

但这似乎是不可能的,因为PATH是类似于c:\qt\qt-4.5.2\lib;c:\common\lib (加上其他不相关的path)。 如果我从公共lib目录中删除较新的QtXml (不是用旧版本replace它,只是删除它),那么LoadLibrary()成功,因为它加载了所有Qt库的4.5.2版本。 但是这不是一个好的长期解决scheme,因为在PATH(common)中运行没有Qt特定的版本目录将无法findQtXml

这怎么可能? LoadLibrary() (或者任何它以recursion方式调用来解决库的依赖性)如何从PATH 稍后加载库? 我找不到任何可能表明公共库目录被特别考虑的事情(它不是一个设置的DLL目录)。 在构build过程中没有提到,这只是开发人员为了方便而在PATH一些东西。

顺便说一句,类似的情况存在于LD_LIBRARY_PATHdlopen() Linux上,它在那里工作得很好。 这是Windows做的不同,我不明白。 有没有人有任何洞察什么可能会出错?

LoadLibrary有许多令人惊讶的行为。 确保你完全赞同MSDN中的所有备注 。

如果已经有一个加载了相同名称的库(任何版本),LoadLibrary只是返回已经加载的DLL的句柄。 这可能会在你的情况下发挥作用。

接下来,如果你已经指定了一个相对路径或只是一个文件名,LoadLibrary会应用神秘的搜索规则 。 PATH变量通常是最后搜索的地方 。 在检查PATH之前,其他规则可能会找到“错误的”DLL。 一个很好的指导方针是始终使用您要加载的文件的绝对路径,以确保其搜索规则不抓取错误的文件。 一个常见的安全缺陷是不能控制LoadLibrary搜索的位置,攻击者说服你的应用程序加载一个篡改的DLL。

最后,安装程序可以应用DLL重定向 ,这可以覆盖您要求的内容以及可能的位置。 我不确定这是否是常见的Qt DLLs,但你可能想检查你的注册表。

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