首先我們先來看一下FreeLibraryAndExitThread這個API
VOID FreeLibraryAndExitThread(
HMODULE hModule,
DWORD dwExitCode
);
MSDN:The FreeLibraryAndExitThread function decrements the reference count of a loaded dynamic-link library (DLL) by one, then calls ExitThread to terminate the calling thread. The function does not return.
這個API會減少動態庫的引用計數,然後調用ExitThread函數結束調用線程,這個API是沒有傳回值的。(插播一點小知識,LoadLibrary和LoadLibraryEx,會增加一個動態庫的引用計數,而且如果在同一個進程對一個完全相同的DLL進行Load操作,只會增加該dll模組的引用計數,而且進入點函數也不會調用,當一個動態庫的引用計數減少到0時,會從進程中將該模組卸載。)
以前遇到的一個比較麻煩的問題是,如果dll裡面啟動了一個線程,外面去FreeLibary這個dll的時候,如果dll裡面的線程還沒還有退出,程式馬上就會崩潰,原因就是FreeLibary的之後,整個DLL模組的代碼空間已經無效了,此時DLL的線程如果繼續運行自然就會崩潰了。這種情況在一個函數裡面載入一個dll是最明顯的,釋放這個dll時機就很廢腦筋了,以前用到的一些方法有向這個dll查詢線程是否退出了,或者強殺dll裡面的線程等等方法,都用得不太爽。
今天發現了FreeLibraryAndExitThread這個API處理這個問題很不錯,這個API的誕生貌似就是為了處理這個問題,方法如下,調用dll的線程流程不變,依然LoadLibrary,再在函數退出前FreeLibary,而在DLL中加入一套卸載機制,代碼如下:
[cpp]
view plaincopyprint?
- #include "stdafx.h"
- #include <stdio.h>
- #include <Windows.h>
-
- HMODULE g_hDll = NULL;
-
- DWORD WINAPI FreeSelfProc(PVOID param)
- {
- printf("UnloadProc!/n");
- ::MessageBox(NULL, TEXT("Press ok to unload me."),
- TEXT("MsgBox in dll"), MB_OK);
- // FreeLibrary(g_hDll);
- // ExitThread(0);
- ::FreeLibraryAndExitThread(g_hDll, 0);
- return 0;
- }
-
- BOOL APIENTRY DllMain( HANDLE hModule,
- DWORD ul_reason_for_call,
- LPVOID lpReserved
- )
- {
- if (DLL_PROCESS_ATTACH == ul_reason_for_call)
- {
- char szPath[MAX_PATH + 1] = {0};
- ::GetModuleFileName((HMODULE)hModule, szPath, MAX_PATH);
- ::LoadLibrary(szPath);
- printf("DLL_PROCESS_ATTACH!/n");
- g_hDll = (HMODULE)hModule;
- HANDLE hThread = ::CreateThread(NULL, 0, FreeSelfProc, NULL, 0, NULL);
- ::CloseHandle(hThread);
- }
- return TRUE;
- }
#include "stdafx.h"<br />#include <stdio.h><br />#include <Windows.h></p><p>HMODULE g_hDll = NULL;</p><p>DWORD WINAPI FreeSelfProc(PVOID param)<br />{<br />printf("UnloadProc!/n");<br /> ::MessageBox(NULL, TEXT("Press ok to unload me."),<br /> TEXT("MsgBox in dll"), MB_OK);<br />//FreeLibrary(g_hDll);<br />// ExitThread(0);<br />::FreeLibraryAndExitThread(g_hDll, 0);<br /> return 0;<br />}</p><p>BOOL APIENTRY DllMain( HANDLE hModule,<br /> DWORD ul_reason_for_call,<br /> LPVOID lpReserved<br /> )<br />{<br />if (DLL_PROCESS_ATTACH == ul_reason_for_call)<br /> {<br />char szPath[MAX_PATH + 1] = {0};<br />::GetModuleFileName((HMODULE)hModule, szPath, MAX_PATH);<br />::LoadLibrary(szPath);<br />printf("DLL_PROCESS_ATTACH!/n");<br /> g_hDll = (HMODULE)hModule;<br /> HANDLE hThread = ::CreateThread(NULL, 0, FreeSelfProc, NULL, 0, NULL);<br /> ::CloseHandle(hThread);<br /> }<br /> return TRUE;<br />}
原理:在dll的進入點再載入一次自己,使自己的引用計數增加1,然後線上程函數的最後調用FreeLibraryAndExitThread來卸載自身並退出線程,這樣的話,如果調用dll的線程先調用了FreeLibary來釋放這個dll,因為引用計數依然大於0,所以這個DLL不會卸載,dll裡面的線程函數可以安全的跑完知道自己卸載自己;而如果dll中的線程函數先調用FreeLibraryAndExitThread,這個時候因為dll的引用技術也大於0,dll還是不會卸載,只是線程退出而已,直到外面調用FreeLibary來將其卸載。
這個地方要注意的是,dll的線程函數裡面不能調用FreeLibary來卸載,因為FreeLibary返回後依然是DLL的代碼空間,而此時如果dll被真的卸載了,返回空間已經是無效的代碼空間了,程式馬上會崩潰。