【发布时间】:2010-10-11 13:33:49
【问题描述】:
使用标准的 Windows 应用程序。它使用 LoadLibrary 加载一个 DLL 以调用其中的一个函数(我们称之为 DLL_A)。该函数加载另一个 DLL(我们称之为 DLL_B)。该应用程序现在使用 FreeLibrary 卸载 DLL_A DLL,因为它不再需要它。
问题是: DLL_B 是否仍在内存中并已加载?
这是我可以依赖的东西,还是没有记录?
【问题讨论】:
使用标准的 Windows 应用程序。它使用 LoadLibrary 加载一个 DLL 以调用其中的一个函数(我们称之为 DLL_A)。该函数加载另一个 DLL(我们称之为 DLL_B)。该应用程序现在使用 FreeLibrary 卸载 DLL_A DLL,因为它不再需要它。
问题是: DLL_B 是否仍在内存中并已加载?
这是我可以依赖的东西,还是没有记录?
【问题讨论】:
没有。 DLL_B 不会被卸载。由DLL_A 发出的LoadLibrary() 调用将增加DLL_B 的加载计数。由于没有对应的FreeLibrary() 调用DLL_B,所以引用计数不会归零。
来自 LoadLibrary() 文档:
系统维护每个进程 所有加载模块的引用计数。 调用 LoadLibrary 会增加 参考计数。调用 免费图书馆或 FreeLibraryAndExitThread 函数 减少引用计数。这 系统在其卸载模块时 引用计数达到零或何时 进程终止(无论 引用计数)。
【讨论】:
在这种情况下你会有一个句柄泄漏:
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。
【讨论】:
阅读备注部分了解详细说明。
要注意的关键是:
系统为每个加载的模块维护每个进程的引用计数
再往下
当模块的引用计数达到零或进程终止时,系统从进程的地址空间卸载模块
来自MSDN:
释放加载的动态链接库 (DLL) 模块,并在必要时减少其引用计数。当引用计数为零时,模块从调用进程的地址空间中卸载,句柄不再有效。
【讨论】:
Windows 中的 DLL 是引用计数的。当 A 被卸载时,您将减少 A 上的引用计数,如果它达到零,它将卸载,并且(假设代码中没有错误)减少 B 上的引用计数。如果 B 上的引用计数变为零,它将被卸载.可能 DLL C 在 B 上有一个引用计数,卸载 A 不会卸载 B。
【讨论】: