如果加载它的DLL被卸载,DLL会被删除吗?

采取标准的Windows应用程序。 它使用LoadLibrary加载一个DLL来调用一个函数(我们称之为DLL_A)。 该函数加载另一个DLL(我们称之为DLL_B)。 应用程序现在使用FreeLibrary卸载DLL_A DLL,因为它不再需要它。

问题是: DLL_B仍然在内存中并加载?

这是我可以依赖的东西,还是没有logging?

不会, DLL_B不会被卸载。 由DLL_A进行的LoadLibrary()调用将增加DLL_A的负载计数。 由于DLL_B没有对应的FreeLibrary()调用,因此DLL_B不会为零。

从LoadLibrary()文档:

系统在所有加载的模块上维护每个进程的引用计数。 调用LoadLibrary会增加引用计数。 调用FreeLibrary或FreeLibraryAndExitThread函数会减少引用计数。 系统在其引用计数达到零时或过程终止时(不管引用计数如何),卸载模块。

在这种情况下,你将有一个手柄泄漏:

 Program -Load> Dll A -Load> Dll B -Unload> Dll A 

没有代码被卸载的模块隐式地执行以卸载它加载的模块。

由于没有执行代码来减少引用计数,所以模块B将永远不会被卸载。

这里是加载/卸载DLL的规则:

  • 每次调用LoadLibrary和LoadLibraryEx都会增加该模块的引用计数。 这只是在调用过程的上下文中,而不是跨越流程边界。
  • 每次调用FreeLibrary或FreeLibraryAndExitThread都会减少引用计数。
  • 当引用计数达到0时,它将被卸载。
  • 当Windows看到你的程序关闭时 ,任何泄漏的卸载模块将被卸载。
  • 根据你在做什么, DllCanUnloadNow可能对你有用。

仍在内存vs仍然加载:

不能保证当参考值达到0时,模块将在某个特定的时间从内存中释放。但是当引用计数达到0时,您应该考虑模块是否被卸载。

阻止DLL被卸载:

强制DLL被卸载,你可以尝试

  • 系统用DLL_PROCESS_DETACH标志调用DllMain。 你可以尝试不通过某种阻塞操作返回。
  • 你可以尝试从你想不能卸载的DLL中调用LoadLibrary。 (自负荷)

编辑:

你提到你的目标是将代码注入正在运行的程序中,并且你想故意泄漏这个句柄。

这很好,但是如果你经常运行这个操作,会导致源程序崩溃,因为会使用太多的句柄,或者最终会使用太多的内存。

你可以从你的DllMain返回FALSE来阻止它被加载,这样你就不会浪费内存。 当fdwReason是DLL_PROCESS_ATTACH时,你可以这样做。 你可以在这里阅读更多 。

如果您试图模拟DLL并添加自己的额外功能,则需要实现源DLL所实现的所有功能,并将每个调用委托给源DLL。

阅读备注部分以获得详细的解释。

关键要注意的是:

系统为每个加载的模块维护每个进程的引用计数

并进一步下降

当模块的引用计数达到零或进程终止时,系统将从进程的地址空间卸载模块

来自MSDN :

释放加载的动态链接库(DLL)模块,并在必要时减少其引用计数。 当引用计数达到零时,模块将从调用进程的地址空间中卸载,并且句柄不再有效。

Windows中的DLL是引用计数。 当A被卸载时,你将递减A上的引用计数,如果它达到零,它将卸载,并且(假设没有代码中的错误)递减B上的引用计数。如果B上的引用计数变为零,则它将被卸载。 有可能DLL C在B上有一个refcount,卸载A不会卸载B.