文档中说:“系统卸载一个模块时,它的引用计数达到零,或者当进程终止时(不管引用计数)。”
这导致了一个重大的问题,目前还不清楚如何解决它。关于这个问题。假设我们有一个可执行文件E,它显式地依赖于一个DLL库L1,该库通过LoadLibrary动态加载DLL库L2。当E完成并且进程开始终止时,可以在L2之前卸载L1。如果L1有一个具有静态存储时间的对象(该对象包含从L2获得的一些资源),这可能会造成问题,因为这些资源将在L1卸载时开始破坏,这意味着对已经卸载的L2的调用将导致崩溃。
可能的解决方案之一(甚至是唯一的解决方案)是不销毁具有静态存储持续时间的对象。该解决方案的缺点是L1可能会被动态加载多次,从而增加内存泄漏。这可以通过使用DllMain来缓解,在这里我们可以检查进程是否终止,或者库是否通过FreeLibrary卸载。
到目前为止,一切似乎都很顺利。但还有更多。假设我们希望L1是一个与名为L_SHIM的DLL库链接的静态库。因此,现在可执行文件E与L_SHIM链接,整个DllMain技巧不再工作。实际上,如果不允许我们修改L_SHIM库,那么似乎没有什么是可行的。
有人要解决这样的问题吗?对于可能的解决方案,我将不胜感激。
发布于 2021-06-16 08:54:08
当处理DLL时,只有在动态卸载DLL ( lpReserved
参数为NULL)的情况下,DLL才应该释放堆内存等资源。如果进程正在终止( lpvReserved
参数为非空),除当前线程外,进程中的所有线程要么已经退出,要么通过调用ExitProcess
函数显式结束,这可能会使堆等进程资源处于不一致的状态。在这种情况下,DLL清理资源是不安全的。相反,DLL应该允许操作系统恢复内存。
因此,您需要检查lpvReserved
内部的DllMain
让您拥有全局变量:
bool g_is_terminating = false;
在DllMain
中
case DLL_PROCESS_DETACH: g_is_terminating = (reserved != nullptr);
在DllMain
之后调用全局/静态对象的析构函数。这样您就可以签入析构函数g_is_terminating
;
if (!g_is_terminating) { do_something(); }
另一种总是很有用而且非常简单的方法,使用无文档的api。
//This routine returns the status of process shutdown.
EXTERN_C
DECLSPEC_IMPORT
BOOLEAN
NTAPI
RtlDllShutdownInProgress();
此api从ntdll.dll导出-因此您需要使用或ntdll.lib或ntdllp.lib (以防您不使用crt)
只需从destructror调用RtlDllShutdownInProgress();
,如果返回true --而不是访问另一个dll --这意味着进程正在终止。
if (!RtlDllShutdownInProgress()) { do_something(); }
内部,ntdll中存在全局变量。
BOOLEAN LdrpShutdownInProgress = FALSE;
当ExitProcess
调用- than LdrShutdownProcess
从ntdll.dll
调用并开始设置LdrpShutdownInProgress = TRUE;
时,RtlDllShutdownInProgress()
只是返回LdrShutdownProcess
的值。
以及“当进程终止时,系统卸载一个模块(不管引用计数如何)”。
这是假的。进程开始终止后,系统不会卸载任何dll。即使此时直接调用FreeLibrary/LdrUnloadDll
(在LdrpShutdownInProgress设置为TRUE
之后)- dll也不会被卸载。真的--没什么大不了的--因为很快所有的过程都会被摧毁。
..which意味着对已经卸载的L2的调用将导致崩溃。实际上,L2永远不会在调用ExitProcess
之后卸载eb。但是 DLL_PROCESS_DETACH
将在L2中被调用。这将始终在L1 DLL_PROCESS_DETACH
通知之前(此通知按LIFO顺序发送。因此,如果L1加载L2 - DLL_PROCESS_ATTACH
之前调用L1,则在L2 - L1之前,L2在InInitializationOrderLinks
列表中调用.和进程退出-这个列表(InInitializationOrderLinks
)处理的顺序相反。
因此,尽管L2仍在内存中,但它已经处于"uninit“状态(在DLL_PROCESS_DETACH
之后),在L2中调用某些api可能会导致未定义的结果。它可以工作,没有任何问题,或崩溃或随机结果。更糟糕的是,比较稳定的崩溃
https://stackoverflow.com/questions/67996998
复制相似问题