顯式卸載DLL模塊

當進程中的線程不再需要DLL中的引用符號時,可以從進程的地址空間中顯式卸載DLL,方法是調用下面的函數:

    BOOL FreeLibrary(HINSTANCE hinstDll);

必須傳遞HINSTANCE值,以便標識要卸載的DLL。該值是較早的時候調用LoadLibrary(Ex)而返回的值。

也可以通過調用下面的函數從進程的地址空間中卸載DLL:

    VOID FreeLibraryAndExitThread(

        HINSTANCE hinstDll,

        DWORD dwExitCode);

該函數是在Kernel32.dll中實現的,如下所示:

   VOID FreeLibraryAndExitThread(HINSTANCE hinstDll,DWORD dwExitCode) {

        FreeLibrary(hinstDll);

        ExitThread(dwExitCode);

    }

初看起來,這並不是個非常高明的代碼,你可能不明白,爲什麼Microsoft要創建FreeLibraryAndExitThread這個函數。其原因與下面的情況有關:假定你要編寫一個DLL,當它被初次映射到進程的地址空間中時,該DLL就創建一個線程。當該線程完成它的操作時,它通過調用FreeLibrary函數,從進程的地址空間中卸載該DLL,並且終止運行,然後立即調用ExitThread。

但是,如果線程分開調用FreeLibrary和ExitThread,就會出現一個嚴重的問題。這個問題是調用FreeLibrary會立即從進程的地址空間中卸載DLL。當調用的FreeLibrary返回時,包含對ExitThread調用的代碼就不再可以使用,因此線程將無法執行任何代碼。這將導致訪問違規,同時整個進程終止運行。

但是,如果線程調用FreeLibraryAndExitThread,該函數調用FreeLibrary,使DLL立即被卸載。下一個執行的指令是在Kernel32.dll中,而不是在剛剛被卸載的DLL中。這意味着該線程能夠繼續執行,並且可以調用ExitThread。ExitThread使該線程終止運行並且不返回。

一般來說,並沒有很大的必要去調用FreeLibraryAndExitThread函數。我曾經使用過一次,因爲我執行了一個非常特殊的任務。另外,我爲MicrosoftWindows3.1編寫了一個代碼,它並沒有提供這個函數。因此我高興地看到Microsoft將這個函數增加到了較新的Windows版本中。

在實際環境中,LoadLibrary和LoadLibraryEx這兩個函數用於對與特定的庫相關的進程使用計數進行遞增,FreeLibrary和FreeLibraryAndExitThread這兩個函數則用於對庫的每個進程的使用計數進行遞減。例如,當第一次調用LoadLibrary函數來加載DLL時,系統將DLL的文件映像映射到調用進程的地址空間中,並將DLL的使用計數設置爲1。如果同一個進程中的線程後來調用LoadLibrary來加載同一個DLL文件映像,系統並不第二次將DLL映像文件映射到進程的地址空間中,它只是將與該進程的DLL相關的使用計數遞增1。

爲了從進程的地址空間中卸載DLL文件映像,進程中的線程必須兩次調用FreeLibrary函數。第一次調用只是將DLL的使用計數遞減爲1,第二次調用則將DLL的使用計數遞減爲0。當系統發現DLL的使用計數遞減爲0時,它就從進程的地址空間中卸載DLL的文件映像。試圖調用DLL中的函數的任何線程都會產生訪問違規,因爲特定地址上的代碼不再被映射到進程的地址空間中。

系統爲每個進程維護了一個DLL的使用計數,也就是說,如果進程A中的一個線程調用下面的函數,然後進程B中的一個線程調用相同的函數,那麼MyLib.dll將被映射到兩個進程的地址空間中,這樣,進程A和進程B的DLL使用計數都將是1。

HINSTANCE hinstDll = LoadLibrary("MyLib.dll");

如果進程B中的線程後來調用下面的函數,那麼進程B的DLL使用計數將變成0,並且該DLL將從進程B的地址空間中卸載。但是,進程A的地址空間中的DLL映射不會受到影響,進程A的DLL使用計數仍然是1。

FreeLibrary(hinstDll);

如果調用GetModuleHandle函數,線程就能夠確定DLL是否已經被映射到進程的地址空間中:

HINSTANCE GetModuleHandle(PCTSTR pszModuleName);

例如,只有當MyLib.dll尚未被映射到進程的地址空間中時,下面這個代碼才能加載該文件:

HINSTANCE hinstDll = GetModuleHandle("MyLib");//DLL externsion assumed

if(hinstDll == NULL) {

   hinstDll = LoadLibrary("MyLib");//DLL externsion assumed

}

如果只有DLL的HINSTANCE值,那麼可以調用GetModuleFileName函數,確定DLL(或.exe)的全路徑名:

DWORD GetModulefileName(

   HINSTANCE hinstModule,

   PTSTR pszPathName,

   DWORD cchPath);

第一個參數是DLL(或.exe)的HINSTANCE。第二個參數pszPathName是該函數將文件映像的全路徑名放入的緩存的地址。第三參數cchPath用於設定緩存的大小(以字符爲計量單位)。

原文:http://hi.baidu.com/onlinewan/blog/item/245c72f0e54f11c07931aaf4.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章