DllMain

exe有個main或者WinMain入口函式一樣,DLL也有一個入口函式,就是DllMain。以“DllMain”為關鍵字,來看看MSDN幫助文檔怎么介紹這個函式的。

基本介紹

簡介,何時調用,進程映射,進程卸載,執行緒映射,執行緒卸載,換名,使用,

簡介

The DllMain function is an optional method of entry into a dynamic-link library (DLL)。(簡要翻譯:對於動態程式庫,DllMain是一個可選的入口函式。)這句話很重要,很多初學者可能都認為一個動態程式庫肯定要有DllMain函式。其實不然,像很多僅僅包含資源信息的DLL是沒有DllMain函式的。
函式定義:
BOOL WINAPI DllMain(  _In_ HINSTANCE hinstDLL, // 指向自身的句柄  _In_ DWORD fdwReason, // 調用原因  _In_ LPVOID lpvReserved // 隱式載入和顯式載入);// 以上內容來自MSDN

何時調用

系統是在什麼時候調用DllMain函式的呢?靜態連結時,或動態連結時調用LoadLibraryFreeLibrary都會調用DllMain函式。DllMain的第二個參數fdwReason指明了系統調用Dll的原因,它可能是::
DLL_PROCESS_ATTACH、
DLL_PROCESS_DETACH、
DLL_THREAD_ATTACH、
DLL_THREAD_DETACH。
以下從這四種情況來分析系統何時調用了DllMain。

進程映射

DLL_PROCESS_ATTACH
大家都知道,一個程式要調用Dll里的函式,首先要先把DLL檔案映射到進程的地址空間。要把一個DLL檔案映射到進程的地址空間,有兩種方法:靜態連結動態連結的LoadLibrary或者LoadLibraryEx。
當一個DLL檔案被映射到進程的地址空間時,系統調用該DLL的DllMain函式,傳遞的fdwReason參數為DLL_PROCESS_ATTACH,這種調用只會發生在第一次映射時。如果同一個進程後來為已經映射進來的DLL再次調用LoadLibrary或者LoadLibraryEx,作業系統只會增加DLL的使用次數,它不會再用DLL_PROCESS_ATTACH調用DLL的DllMain函式。不同進程用LoadLibrary同一個DLL時,每個進程的第一次映射都會用DLL_PROCESS_ATTACH調用DLL的DllMain函式。
可參考DllMainTest的DLL_PROCESS_ATTACH_Test函式。

進程卸載

DLL_PROCESS_DETACH
當DLL被從進程的地址空間解除映射時,系統調用了它的DllMain,傳遞的fdwReason值是DLL_PROCESS_DETACH。當DLL處理該值時,它應該執行進程相關的清理工作。
那么什麼時候DLL被從進程的地址空間解除映射呢?兩種情況:
◆FreeLibrary解除DLL映射(有幾個LoadLibrary,就要有幾個FreeLibrary)
◆進程結束而解除DLL映射,在進程結束前還沒有解除DLL的映射,進程結束後會解除DLL映射。(如果進程的終結是因為調用了TerminateProcess,系統就不會用DLL_PROCESS_DETACH來調用DLL的DllMain函式。這就意味著DLL在進程結束前沒有機會執行任何清理工作。)
注意:當用DLL_PROCESS_ATTACH調用DLL的DllMain函式時,如果返回FALSE,說明沒有初始化成功,系統仍會用DLL_PROCESS_DETACH調用DLL的DllMain函式。因此,必須確保清理那些沒有成功初始化的東西。
可參考DllMainTest的DLL_PROCESS_DETACH_Test函式。

執行緒映射

DLL_THREAD_ATTACH
當進程創建一執行緒時,系統查看當前映射到進程地址空間中的所有DLL檔案映像,並用值DLL_THREAD_ATTACH調用DLL的DllMain函式。
新創建的執行緒負責執行這次的DLL的DllMain函式,只有當所有的DLL都處理完這一通知後,系統才允許進程開始執行它的執行緒函式。
注意跟DLL_PROCESS_ATTACH的區別,我們在前面說過,第n(n>=2)次以後地把DLL映像檔案映射到進程的地址空間時,是不再用DLL_PROCESS_ATTACH調用DllMain的。而DLL_THREAD_ATTACH不同,進程中的每次建立執行緒,都會用值DLL_THREAD_ATTACH調用DllMain函式,哪怕是執行緒中建立執行緒也一樣。

執行緒卸載

DLL_THREAD_DETACH
如果執行緒調用了ExitThread來結束執行緒(執行緒函式返回時,系統也會自動調用ExitThread),系統查看當前映射到進程空間中的所有DLL檔案映像,並用DLL_THREAD_DETACH來調用DllMain函式,通知所有的DLL去執行執行緒級的清理工作。
注意:如果執行緒的結束是因為系統中的一個執行緒調用了TerminateThread,系統就不會用值DLL_THREAD_DETACH來調用所有DLL的DllMain函式。

換名

在早期的SDK版本中,DllMain是叫做DllEntryPoint。其實有一件鮮為人知的事:一個Dll的入口函式名是可以自己定義的。下面我將以VC++6.0為例來演示如何更改。首先要說明一點,雖然DllMain可以換成其他函式名,但函式的參數和返回值必須和DllMain一樣。而且這個函式要為__stdcall類型(DllMain本身也是__stdcall類型)。
打開VC++選單Project\Settings\Link tab\ Output in the Category box,如下圖,在Entry-point symbol中輸入要替換DllMain的函式名(當然這個函式名是你程式中已經實現的函式)。Entry-point symbol是乾么的呢?可以以關鍵字“Entry-point symbol”搜尋MSDN幫助文檔查看,搜尋時,打鉤“僅搜尋標題”會更快定位。

使用

DllMain函式是DLL模組的默認入口點。當Windows載入DLL模組時調用這一函式。系統首先調用全局對象的構造函式,然後調用全局函式DLLMain。DLLMain函式不僅在將DLL連結載入到進程時被調用,在DLL模組與進程分離時(以及其它時候)也被調用。下面是一個框架DLLMain函式的例子。
如果我們在DllMain中寫入下面的代碼(在原來的gandll.c中添加下面的代碼):
BOOL APIENTRY DllMain(HANDLEhModule, DWORD ul_reason_for_call, LPVOIDlpReserved)
{
printf("hModule.%p lpReserved.%p \n", hModule,lpReserved);
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
printf("Process attach. \n");
break;
case DLL_PROCESS_DETACH:
printf("Process detach. \n");
break;
case DLL_THREAD_ATTACH:
printf("Thread attach. \n");
break;
case DLL_THREAD_DETACH:
printf("Thread detach. \n");
break;
}
return (TRUE);
}
同時將dlltest\dlltest.c修改為:
#include<stdio.h>
#include"dlltest.h"
intmain(int argc, char**argv)
{
printf("Simple DLL test start. \n");
printf("Call DLL function: \n");
printf("Test DLL values: %d \n", add2(1, 2));
printf("Call DLL function end. \n");
printf("Simple DLL test end. \n");
return (0);
}
我簡單的測試一下輸出結果為:
C:\gandll\dlltest>dlltest
hModule.10000000lpReserved.0012FD30
Process attach.
Simple DLL teststart.
Call DLLfunction:
Test DLL values:3
Call DLL functionend.
Simple DLL testend.
hModule.10000000lpReserved.00000001
Process detach.
也就是說DLL載入和應用程式退出的使用都會調用該函式(DllMain)的喔,是應用程式一上來就調用的,不是用到該函式時才調用的!

相關詞條

熱門詞條

聯絡我們