【问题标题】:C++ DLL Unload Destructor?C++ DLL卸载析构函数?
【发布时间】:2012-04-14 23:56:59
【问题描述】:

所以我有一个用 C++ 编写的 DLL。 但是,它使用 GlobalAlloc() 分配内存。为避免内存泄漏,我想跟踪这些分配并在 DLL 销毁时将它们全部取消分配。

有什么方法可以编写一个在我的 DLL 被卸载时调用的函数? 我能想到的一件事是在我的 DLL 中创建一个全局对象并在其析构函数中编写内存释放调用,但这似乎有点过头了。 我的另一个想法是仅依靠操作系统在 DLL 卸载时释放内存,但这似乎很脏。

谢谢

【问题讨论】:

  • 在调用 LPCSTR GetCSV() 函数时分配内存。基本上,我将内存从本地 std 字符串复制到我使用 GlobalAlloc() 分配的内存中,只是为了确保我的调用者在函数返回后可以访问内存。我添加了一些代码来清理对 GetCSV() 的旧调用的内存,但我不会处理卸载 DLL 的最终情况,因为这显然是为我自动完成的。这个article(由回答者链接)非常好。

标签: c++ windows winapi dll


【解决方案1】:

有什么方法可以编写一个在我的 DLL 被卸载时调用的函数?我能想到的一件事是在我的 DLL 中创建一个全局对象并在其析构函数中编写内存释放调用

这是可能的,尽管我相信你的对象的析构函数将被调用的确切时间是不确定的。

你可能对DLL_PROCESS_DETACH 感兴趣,虽然你应该避免在DllMain 中做任何重要的事情,但似乎在这里释放资源是可以接受的。注意注意事项:

当由于 DLL 加载不成功、进程终止或调用 FreeLibrary 而从进程中卸载 DLL 时,系统不会使用 DLL_THREAD_DETACH 值调用 DLL 的入口点函数进程的各个线程。 DLL 仅发送一个 DLL_PROCESS_DETACH 通知。 DLL 可以借此机会清理 DLL 已知的所有线程的所有资源。

在处理 DLL_PROCESS_DETACH 时,仅当动态卸载 DLL(lpReserved 参数为 NULL)时,DLL 才应释放堆内存等资源。如果进程正在终止(lpvReserved 参数为非 NULL),则除当前线程外,进程中的所有线程要么已经退出,要么已通过调用 ExitProcess 函数显式终止,这可能会留下一些进程资源,例如堆处于不一致的状态。在这种情况下,DLL 清理资源是不安全的。相反,DLL 应该允许操作系统回收内存。

您可能需要详细说明为什么您的 DLL 可以保留内存,如果您有大量由 DLL 创建的对象,它们应该具有定义的生命周期并在其结束时自行清理生活。

如果它们不是对象(即分配内存并通过函数返回给调用者),为什么不将责任归还给正在使用您的 DLL 的人呢?他们可以释放内存。终端服务库遵循这种模式 (WTSFreeMemory)。

如果资源是长期存在的并且必须存在于您的库的生命周期中,则让使用者控制您的库的生命周期。编写两个函数:MyFrameworkStartupMyFrameworkShutdown 酌情编写。 Winsock 遵循这种模式(WSAStartupWSACleanup)。

我的另一个想法是仅依靠操作系统在 DLL 卸载时释放内存,但这似乎很脏。

You'll be okay if the process is exiting:

不用担心释放内存;当进程地址空间被破坏时,它都会消失。不用担心关闭手柄;当进程句柄表被销毁时,句柄会自动关闭。不要尝试调用其他 DLL,因为那些其他 DLL 可能已经收到它们的 DLL_PROCESS_DETACH 通知,在这种情况下,它们的行为可能与 Delphi 对象在其析构函数运行后尝试使用它时的行为异常相同.

在实施“什么都不做”策略之前,请务必阅读整篇文章和 cmets 并理解它。

【讨论】:

  • 谢谢!太棒了。特别是关于无需担心释放内存的说明。
【解决方案2】:

如何/何时分配内存?通常,最明智的选择是尝试保持某种对称性(构造函数分配,析构函数解除分配。或者在加载 DLL 时分配内存,在卸载 DLL 时释放内存)。

无论如何,如果您想在卸载 DLL 时收到通知,请查看 DllMain 函数,特别是 DLL_PROCESS_DETACH 参数。

【讨论】:

  • 根据你给出的链接,运行时库提供的标准DllMain调用了全局对象的构造函数/析构函数。因此,我想只在 DLL 中使用具有适当构造函数/析构函数的全局对象就足够了。
  • 感谢您的回复!调用LPCSTR GetCSV() 函数时分配内存。基本上,我将内存从本地 std 字符串复制到我使用 GlobalAlloc() 分配的内存中,只是为了确保我的调用者在函数返回后可以访问内存。我添加了一些代码来清理对 GetCSV() 的旧调用的内存,但我不会处理卸载 DLL 的最终情况,因为这显然是为我自动完成的。
【解决方案3】:

在卸载 DLL 时调用 DllMain function,并将 fdwReason 设置为 DLL_PROCESS_DETACH。如文档中所述,请确保检查 lpvReserved 的值,并且只有在它为 NULL 时才释放内存;你should not free memory if the process is terminating

【讨论】:

  • 感谢您提供简单而简单的答案!
猜你喜欢
  • 1970-01-01
  • 2018-04-06
  • 1970-01-01
  • 2012-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多