更改Windows DLL加载顺序? (加载订单,而不是搜索订单)

时间:2011-06-16 12:27:36

标签: c++ windows dll loader

说我有一个可执行文件:app.exe

我在此可执行文件中使用了2个不同的第三方DLL:foo.dll bar.dll并且应用程序必须隐式链接到这些DLL,即我无法使用::LoadLibrary加载它们。

(注意:这不是我不能调用LoadLibrary,但是这些DLL需要静态链接(C ++ DLL与__declspec(dllexport)),所以我调用LoadLibrary没有任何意义,因为可驱动的加载器已经调用它。)

这两个的DLL彼此之间存在任何依赖关系,也就是说,就我所知,它们的加载顺序是未定义的(并且应该无关紧要)。 (两者的相关性基本上只在标准的Windows dll(kernel32,msvcrt等)上。

我现在遇到的问题是我希望控制这些DLL的加载顺序,我希望在bar.dll之前foo.dll 始终加载(DLL_PROCESS_ATTACH)。

是否有可能告诉Windows DLL Loader在另一个DLL之前加载一个DLL?

编辑:要检查可执行文件的DLL加载顺序,可以使用DUMPBIN.exe实用程序:(只需启动Visual Studio命令提示符)

编辑:根据this answer / this blog entry,NT Loader 按顺序遍历导入部分。 (这将导致独立的 DLL按照它们在导入部分中出现的顺序加载。)

C:\path\to\program> dumpbin /IMPORTS app.exe | grep -i \.dll
  MSVCR80D.dll
  KERNEL32.dll
  OLEAUT32.dll
  MSVCP80D.dll
  foo.dll
  bar.DLL

此输出表示将首先加载MSVCR80D.dll(及其依赖 [a] ),最后将加载bar.DLL。卸载将以相反的顺序发生。

尚未发现的如何影响此加载订单 ...


(注)

[a]:这当然意味着,例如将首先加载kernel32.dll,因为msvcr80d.dll将依赖于kernel32.dll。


根据一些要求,我正在为此添加一个理由:(但,我仍然对此感兴趣。我知道如何解决MFC问题。

其中的Microsoft MFC DLL的调试版本内置了内存泄漏检测。(据我所知,它与_CrtSetDbgFlag及相关工具使用的机制相同。)

MFC调试DLL将在卸载时转储所有未使用的内存。现在,如果您的进程中有第二个DLL,它独立于MFC,并且第二个DLL在DLL_PROCESS_DETACH上释放内存,则MFC报告机制将报告错误的内存泄漏,如果MFC DLL在另一个dll之前卸载。

如果可以确保首先加载调试MFC DLL /卸载所有独立DLL的最后一个,那么所有其他DLL本身就已经清理完了,MFC就不会报告错误泄漏。

4 个答案:

答案 0 :(得分:6)

  

我还没有发现如何影响这个加载顺序...

我不知道为什么我没有尝试过这个,但似乎结果模块的导入部分顺序依赖于lib文件提供给链接器的顺序。

Configuration Properties -> Linker -> Additional Dependencies ...

首先列出的lib文件也是导入部分的第一个,meaning加载器将按顺序导入这些文件(模数依赖项)。

因此,要回答这一部分:只需按正确的顺序向链接器提供lib文件。

注意:我在VS2005上尝试了它,它似乎工作。我不知道是否在某处记录了这些内容,或者在较新版本的VC ++中是否有所改变。


更新:当它恢复正常时,今天我遇到负载顺序受链接器命令行顺序影响的情况lib个文件。 (仍然)不知道为什么。 (仍然是VS2005)

但我设法通过将有问题的DLL添加到延迟加载的DLL列表(如Macke's answer中)来使其工作。


答案 1 :(得分:4)

这是一个想法:如何在app.exe的链接器选项中将它们标记为“Delay Loaded dlls”?

延迟加载将允许您“静态”链接(即没有LoadLibrary()et.al)但不会加载DLL并在实际需要之前进行链接。

如果这是一个选项,那么(假设你可以等待这么久,即不要在main()之前访问foo / bar dll函数),你可以在main()中访问一个函数(只需获取函数ptr)首先在foo.dll中加载它并绑定所有“静态”链接函数?

(也许LoadLibrary()触发相同的链接 - 当需要的程序。不确定。虽然你的代码看起来会更干净。)

答案 2 :(得分:4)

只需将foo.dll添加到bar.dll的导入表中,操作系统加载程序将处理其余的工作。

你应该能够在没有bar.dll源代码的情况下做到这一点,不确定editbin工具是否有这样的选项,但这对PE文件来说是一个相当简单的编辑。

您可能可以使用预加载DLL的注册表设置,但我不会这样做,您不希望foo.dll加载到其他不需要它的进程中。

答案 3 :(得分:1)

如果你没有链接导入库(foo.lib& bar.lib),那么加载程序将不会在启动时自动加载DLL,你可以随时调用LoadLibrary()。

在旁注中,我编写了一个方便的小库,用于在运行时封装加载DLL,而无需直接处理LoadLibrary / GetProcAddress。你可以阅读它here