简述
- 在
Windows
中,DLL
(动态链接库)可以请求操作系统卸载自己,但这样做是有风险的 - 要确保在
DLL
被卸载后不再访问任何DLL
内部的资源或函数
FreeLibrary
- 如果在
test.dll
的某个函数里面,获取到test.dll
的句柄后,调用FreeLibrary
释放了test.dll
,会发生什么? - 当你调用
FreeLibrary
,它会减少DLL
的引用计数。如果引用计数达到0
,系统会开始卸载该DLL
- 这意味着该DLL的代码、数据和其他资源都将被从内存中删除。
- 如果在
FreeLibrary
之后还有代码需要执行,那么这些代码所在的内存位置可能已经被操作系统回收或已被标记为无效- 尝试在已经卸载的内存位置执行代码会导致未定义的行为,通常这会导致程序崩溃
解决
- 可以创建一个新线程来释放
DLL
,并在该线程中调用FreeLibraryAndExitThread
- 这样,新线程会释放
DLL
并安全地退出,而不会影响DLL
中其他正在运行的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include <windows.h> BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: break; } return TRUE; } extern "C" __declspec(dllexport) void UnloadMyself() { // 获取当前DLL的句柄 HMODULE hModule = NULL; GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)UnloadMyself, &hModule); // 创建一个新线程来卸载该DLL CloseHandle(CreateThread(NULL, 0, [](LPVOID lpModule) -> DWORD { Sleep(100); // 为调用方留出一些时间来完成其工作 FreeLibraryAndExitThread((HMODULE)lpModule, 0); return 0; }, hModule, 0, NULL)); } |
注意
- 应该让调用
DLL
的程序负责加载和卸载DLL
- 如果确实需要在
DLL
内部释放自己,确保在调用FreeLibrary
后不再执行任何代码
为什么要新起线程
- 不会阻塞原先线程
- 通过在新的线程中设定了等待时间,确保原始线程在
DLL
卸载之前有更多的时间完成其工作
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ Windows 核心编程 _ 用户模式:线程同步一07/15
- ♥ Windbg:命令实践详解一03/27
- ♥ Windows调试相关简记12/13
- ♥ WinDbg相关01/12
- ♥ WindowsHOOK相关03/17
- ♥ Windows 核心编程 _ 线程内幕07/06