在处理DLL_PROCESS_DETACH 时,DLL 应该释放资源,例如
仅当动态卸载 DLL(
lpReserved 参数为 NULL)。如果进程正在终止(lpvReserved 参数为 non-NULL),则进程中的所有线程
当前线程以外的进程要么已经退出,要么已经退出
通过调用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(); }
另一种总是有用且非常简单的方法,使用 undocumented api
//This routine returns the status of process shutdown.
EXTERN_C
DECLSPEC_IMPORT
BOOLEAN
NTAPI
RtlDllShutdownInProgress();
从 ntdll.dll 导出的这个 api - 所以你需要使用或 ntdll.lib 或 ntdllp.lib (以防你不使用crt)
只需从析构函数调用RtlDllShutdownInProgress();,如果返回true - 不访问另一个dll - 这意味着进程正在终止
if (!RtlDllShutdownInProgress()) { do_something(); }
在内部,在ntdll中存在全局变量
BOOLEAN LdrpShutdownInProgress = FALSE;
当ExitProcess 被调用时 - 比LdrShutdownProcess 从ntdll.dll 调用并在开始时设置LdrpShutdownInProgress = TRUE;
RtlDllShutdownInProgress() 只返回 LdrShutdownProcess 的值
关于
“当进程终止时系统会卸载一个模块(不管引用计数如何)。”
这是错误的。进程开始终止后系统不会卸载任何 dll。即使此时直接调用FreeLibrary/LdrUnloadDll(在LdrpShutdownInProgress 设置为TRUE 之后)- dll 也不会被卸载。真的 - 这样做没什么意义 - 因为很快所有进程都会被破坏。
..这意味着调用已经卸载的 L2 将导致崩溃。
真的 L2 永远不会在调用 ExitProcess 后卸载 eb。 但是DLL_PROCESS_DETACH会在L2中被调用。这将始终是 before L1 DLL_PROCESS_DETACH 通知(此通知按 LIFO 顺序发送。所以如果 L1 加载 L2 em> - DLL_PROCESS_ATTACH 在 L2 之前在 L1 中调用 - L1 在 InInitializationOrderLinks 列表中 L2 之前。并在进程退出时 - 此列表 (InInitializationOrderLinks) 以相反的顺序处理。
所以 L2 ,尽管仍在内存中,但已经处于“uninit”状态(在 DLL_PROCESS_DETACH 之后)调用 L2 中的某些 api 可能会导致未定义的结果。它可能没有任何问题,或者崩溃或随机结果。这更糟糕,比较稳定的崩溃