【问题标题】:Does a DLL get removed if the DLL that loaded it is unloaded?如果加载它的 DLL 被卸载,DLL 是否会被删除?
【发布时间】:2010-10-11 13:33:49
【问题描述】:

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

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

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

【问题讨论】:

    标签: windows winapi dll


    【解决方案1】:

    没有。 DLL_B 不会被卸载。由DLL_A 发出的LoadLibrary() 调用将增加DLL_B 的加载计数。由于没有对应的FreeLibrary() 调用DLL_B,所以引用计数不会归零。

    来自 LoadLibrary() 文档:

    系统维护每个进程 所有加载模块的引用计数。 调用 LoadLibrary 会增加 参考计数。调用 免费图书馆或 FreeLibraryAndExitThread 函数 减少引用计数。这 系统在其卸载模块时 引用计数达到零或何时 进程终止(无论 引用计数)。

    【讨论】:

    • 问题是您不应该在进程分离时调用 DLLMain 中的 FreeLibrary(),那么您将如何实际清理 DLL_B?
    • 一种常见的方法是调用 DLL_A 的 init() 和 deinit() 来加载/卸载 DLL_B(并执行其他初始化/清理功能)。这是侵入性的,但它很简单。
    • 在这种情况下,我希望 DLL_B 被留下,直到主应用程序关闭。确实,这对我来说是理想的。我的意图是将 DLL 和它将创建的线程注入到应用程序中。
    • 我不想建议这个,因为你想要做的事情听起来很难看(但有时这是现实) - 你可能想让 DLL_B 在线程处于活动状态时对其自身执行 LoadLibrary() 所以无论其他人如何加载/卸载它,它都会保持加载状态。
    • 或者您可以让主应用在 DLL_B 上执行加载库...这将确保 DLL_B 的引用计数大于零,直到应用关闭。
    【解决方案2】:

    在这种情况下你会有一个句柄泄漏:

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

    正在卸载的模块不会隐式执行任何代码来卸载它加载的模块。

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

    以下是加载/卸载 dll 的规则:

    • 对 LoadLibrary 和 LoadLibraryEx 的每次调用都会增加该模块的引用计数。这仅在调用进程的上下文中,而不是跨进程边界。
    • 每次调用 FreeLibrary 或 FreeLibraryAndExitThread 都会减少引用计数。
    • 当引用计数达到 0 时,将被卸载。
    • 当 Windows 发现您的程序关闭时,任何泄露的卸载模块都将被卸载。
    • 根据您的工作,DllCanUnloadNow 可能对您有用。

    仍在内存中与仍在加载:

    不能保证你的模块会在引用达到0时从内存中释放出来。但是你应该认为模块在引用计数达到0时就被卸载了。

    阻止 DLL 被卸载:

    要强制卸载 DLL,您可以尝试

    • 系统调用带有 DLL_PROCESS_DETACH 标志的 DllMain。您可以尝试不通过某种阻塞操作从这里返回。
    • 您可以尝试从您希望无法卸载的 DLL 中调用 LoadLibrary。 (自加载)

    编辑:

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

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

    您可以从 DllMain 返回 FALSE 以阻止它被加载,这样您就不会浪费内存。当 fdwReason 为 DLL_PROCESS_ATTACH 时执行此操作。你可以read more about it here

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

    【讨论】:

    • 我很高兴泄露句柄,因为 DLL_B 实际上将执行一个有用的功能。我想将它“注入”到应用程序中。
    • 更新了我的回复以包含此内容。
    • TVM - 我会考虑多负载问题。
    【解决方案3】:

    阅读备注部分了解详细说明。

    要注意的关键是:

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

    再往下

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

    来自MSDN

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-30
        • 2011-01-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多