说我有一个可执行文件: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就不会报告错误泄漏。
答案 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。