轉:FreeLibraryAndExitThread DLL中線程的安全退出

來源:互聯網
上載者:User

    首先我們先來看一下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?
  1. #include "stdafx.h"
      
  2. #include <stdio.h>
      
  3. #include <Windows.h>
      
  4.      
  5. HMODULE g_hDll = NULL;  
  6.   
  7. DWORD WINAPI FreeSelfProc(PVOID param)  
  8. {  
  9.     printf("UnloadProc!/n");  
  10.     ::MessageBox(NULL, TEXT("Press ok to unload me."),  
  11.         TEXT("MsgBox in dll"), MB_OK);  
  12. //  FreeLibrary(g_hDll);
      
  13. //  ExitThread(0);
      
  14.     ::FreeLibraryAndExitThread(g_hDll, 0);  
  15.     return 0;  
  16. }  
  17.   
  18. BOOL APIENTRY DllMain( HANDLE hModule,   
  19.                        DWORD  ul_reason_for_call,  
  20.                        LPVOID lpReserved  
  21.                      )  
  22. {  
  23.     if (DLL_PROCESS_ATTACH == ul_reason_for_call)  
  24.     {   
  25.         char szPath[MAX_PATH + 1] = {0};  
  26.         ::GetModuleFileName((HMODULE)hModule, szPath, MAX_PATH);  
  27.         ::LoadLibrary(szPath);  
  28.         printf("DLL_PROCESS_ATTACH!/n");  
  29.         g_hDll = (HMODULE)hModule;  
  30.         HANDLE hThread = ::CreateThread(NULL, 0, FreeSelfProc, NULL, 0, NULL);  
  31.         ::CloseHandle(hThread);  
  32.     }  
  33.     return TRUE;  
  34. }  

#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被真的卸載了,返回空間已經是無效的代碼空間了,程式馬上會崩潰。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.