说我有一个可执行文件: app.exe
我在这个可执行文件中使用2个不同的第三方DLL: foo.dll
bar.dll
和应用程序必须隐式链接到这些DLL,也就是我不能使用::LoadLibrary
来加载它们。
(注意:并不是我无法调用LoadLibrary
,但是这些DLL需要静态链接(C ++ DLL与__declspec(dllexport)
),所以我调用LoadLibrary
没有任何意义,因为可改变的加载器已经调用它了。
这两个DLL 没有任何依赖关系,也就是说,他们的加载顺序是不确定的,据我所知( 应该是不相关的)。 (两者的依赖关系基本上只在标准的windows dll(kernel32,msvcrt等)
我现在有问题,我希望控制这些DLL的加载顺序,那就是我希望foo.dll 总是在bar.dll之前加载( DLL_PROCESS_ATTACH
)。
是不是有可能告诉Windows DLL加载器加载一个DLL之前?
编辑:要检查可执行文件的DLL加载顺序 ,可以使用DUMPBIN.exe
实用程序:(只需启动Visual Studio命令提示符)
编辑:根据这个答案 / 这个博客条目 ,NT加载程序确实按顺序走入导入部分。 (这将导致独立的 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问题。 )
在它的debugging版本中的Microsoft MFC DLL内置了内存泄漏检测。(据我所知,这是_CrtSetDbgFlag和相关工具所使用的机制)。
当它被卸载时,MFCdebuggingDLL将转储所有不确定的内存。 现在,如果您的进程中有第二个DLL,它独立于MFC,并且此第二个DLL在DLL_PROCESS_DETACH上取消分配内存,则MFC报告机制将报告错误的内存泄漏,如果MFC DLL在其他dll之前被卸载。
如果可以确保debugging的MFC DLL是首先加载/卸载所有独立DLL的最后一个,那么所有其他的DLL本身已经清理完毕,并且MFC不会报告错误的泄漏。
这里有一个想法:如何在app.exe
的链接器选项app.exe
它们标记为“Delay Loaded dlls”?
延迟加载将允许您链接“静态”(即没有LoadLibrary()et.al),但不会加载该DLL,并进行连接,直到实际需要。
如果这是一个选项,那么(假设你可以等待这么久,即在main()之前不要访问foo / bar dll函数),你可以在main()中访问一个函数(只需要获取一个函数ptr或其他东西)在foo.dll
首先,这将加载它并绑定所有“静态”链接的功能?
(也许LoadLibrary()触发相同的链接时需要的过程。不知道,它会看起来更清洁,但你的代码。
只需将foo.dll
添加到foo.dll
的导入表中,操作系统加载程序将处理剩下的部分。
你应该可以在没有bar.dll
源代码的情况下做到这bar.dll
,不知道editbin
工具是否有这样的选项,但是这对PE文件来说是一个相当简单的编辑。
你也许可以使用预加载DLL的注册表设置,但是我不这么做,你不想让foo.dll
加载到其他不需要它的进程中。
我还没有发现的是如何影响这个加载顺序…
我不知道为什么我没有尝试过,但看起来模块的导入部分的顺序依赖于lib
文件提供给链接器的顺序。
Configuration Properties -> Linker -> Additional Dependencies ...
这里列出的lib文件也是第一个在导入部分, 这意味着加载器将按顺序导入这些文件(模依赖关系)。
所以,要回答这个部分: 只需要以正确的顺序提供lib文件给链接器。
注意 :我已经在VS2005上试过了,它似乎工作。 我不知道这是否记录在某处,或者如果它更新版本的VC ++中更改。
更新:当它回来的时候,今天我碰到加载顺序不受 lib
文件的链接器命令行顺序影响的情况。 (仍)不知道为什么。 (还是VS2005)
然而,我已经设法通过将有问题的DLL添加到延迟加载的DLL列表(如在Macke的答案中 )来使其工作。
如果不链接导入库(foo.lib和bar.lib),那么加载程序不会在启动时自动加载DLL,您可以随时调用LoadLibrary()。
在一个侧面说明中,我写了一个方便的小型库,用于在运行中封装加载DLL,而不必直接处理LoadLibrary / GetProcAddress。 你可以在这里阅读。