采用标准Windows应用程序。它使用LoadLibrary加载DLL来调用其中的函数(我们称之为DLL_A)。该函数加载另一个DLL(我们称之为DLL_B)。应用程序现在使用FreeLibrary卸载DLL_A DLL,因为它不再需要它。
问题是: DLL_B是否仍在内存中并加载?
这是我可以依赖的东西,还是没有文件记录?
答案 0 :(得分:11)
没有。 DLL_B
将不会被卸载。 LoadLibrary()
进行的DLL_A
调用将增加DLL_B
的加载计数。由于FreeLibrary()
没有相应的DLL_B
调用,因此引用计数不会归零。
来自LoadLibrary()docs:
系统维护每个进程 所有已加载模块的引用计数。 调用LoadLibrary会增加 引用计数。打电话给 FreeLibrary或 FreeLibraryAndExitThread函数 减少引用计数。该 系统卸载模块时 引用计数达到零或何时 该过程终止(无论如何 引用计数)。
答案 1 :(得分:3)
在这种情况下你会遇到手柄泄漏:
Program -Load> Dll A
-Load> Dll B
-Unload> Dll A
卸载的模块不会隐式执行代码来卸载它加载的模块。
由于没有执行代码来减少引用计数,因此永远不会卸载模块B.
以下是加载/卸载dll的规则:
仍然在内存中仍然加载:
当参考值达到0时,无法保证模块将在某个时间从内存中释放。但是当参考计数达到0时,您应该将模块视为已卸载。
停止卸载DLL:
要强制卸载DLL,您可以尝试
修改:
您提到您的目标是将代码绑定到正在运行的程序中,并且您希望故意泄漏句柄。
没关系,但是如果你经常运行这个操作,可能会导致源程序崩溃,因为会使用太多的句柄,或者最终会使用太多的内存。
您可以从DllMain返回FALSE以阻止它被加载,这样您就不会浪费内存。当fdwReason是DLL_PROCESS_ATTACH时,你这样做。你可以read more about it here。
如果您尝试模拟DLL并添加自己的额外功能,则需要实现源DLL实现的所有函数,并将每个调用委托回源DLL。
答案 2 :(得分:1)
阅读备注部分以获取详细说明。
需要注意的关键是:
系统维护每个已加载模块的每进程引用计数
并进一步向下
当模块的引用计数达到零或进程终止时,系统从进程的地址空间卸载模块
来自MSDN:
释放加载的动态链接库(DLL)模块,并在必要时减少其引用计数。当引用计数达到零时,模块将从调用进程的地址空间中卸载,并且句柄不再有效。
答案 3 :(得分:1)
Windows中的DLL是引用计数的。当A被卸载时,你将A上的引用计数递减,如果它达到零,它将卸载,并且(假设代码中没有错误)减少B上的引用计数。如果B上的引用计数变为零,则它将被卸载。可能DLL C在B上有引用,卸载A不会卸载B。