Dynamic-Link 連結庫 Entry-Point 函式
DLL 可以選擇性地指定進入點函式。 如果存在,每當進程或線程載入或卸除 DLL 時,系統就會呼叫進入點函式。 它可以用來執行簡單的初始化和清除工作。 例如,它可以在建立新線程時設定線程本機記憶體,並在線程終止時加以清除。
如果您要連結 DLL 與 C 執行時間連結庫,它可能會為您提供進入點函式,並可讓您提供個別的初始化函式。 如需詳細資訊,請參閱運行時間連結庫的檔。
如果您要提供自己的進入點,請參閱 DllMain 函式。 dllMain 名稱 是使用者定義函式的佔位元。 您必須指定您在建置 DLL 時所使用的實際名稱。 如需詳細資訊,請參閱開發工具隨附的檔。
呼叫 Entry-Point 函式
每當發生下列任何一個事件時,系統就會呼叫進入點函式:
- 進程會載入 DLL。 對於使用載入時間動態連結的進程,DLL 會在進程初始化期間載入。 對於使用運行時間鏈接的進程,DLL 會在 LoadLibrary 或 LoadLibraryEx 傳回之前載入。
- 進程會卸除 DLL。 當進程終止或呼叫 FreeLibrary 函式,且參考計數變成零時,就會卸除 DLL。 如果進程因為 TerminateProcess 或 TerminateThread 函式而終止,則系統不會呼叫 DLL 進入點函式。
- 在已載入 DLL 的進程中,會建立新的線程。 您可以使用 DisableThreadLibraryCalls 函式,在建立線程時停用通知。
- 載入 DLL 的行程線程通常會終止,而不是使用 TerminateThread 或 TerminateProcess。 當行程卸除 DLL 時,只會針對整個進程呼叫進入點函式一次,而不是針對進程的每個現有線程呼叫一次。 您可以使用 DisableThreadLibraryCalls,在線程終止時停用通知。
一次只能呼叫一個線程來呼叫進入點函式。
系統會在導致呼叫函式的進程或線程內容中呼叫進入點函式。 這可讓 DLL 使用其進入點函式,在呼叫進程的虛擬位址空間中配置記憶體,或開啟進程可存取的句柄。 進入點函式也可以使用線程本機記憶體 (TLS) 配置私用到新線程的記憶體。 如需線程本機記憶體的詳細資訊,請參閱 線程本機記憶體。
Entry-Point 函數定義
DLL 進入點函式必須使用標準呼叫慣例來宣告。 如果未正確宣告 DLL 進入點,則不會載入 DLL,而且系統會顯示訊息,指出必須使用 WINAPI 宣告 DLL 進入點。
在函式主體中,您可以處理下列呼叫 DLL 進入點的任何案例組合:
- 進程會載入 DLL (DLL_PROCESS_ATTACH)。
- 目前的進程會建立新的線程 (DLL_THREAD_ATTACH)。
- 線程正常結束 (DLL_THREAD_DETACH)。
- 進程會卸除 DLL (DLL_PROCESS_DETACH)。
進入點函式應該只會執行簡單的初始化工作。 它不得呼叫 LoadLibrary 或 LoadLibraryEx 函式(或呼叫這些函式的函式),因為這可能會在 DLL 載入順序中建立相依性迴圈。 這可能會導致在系統執行初始化程序代碼之前使用 DLL。 同樣地,進入點函式不得在進程終止期間呼叫 FreeLibrary 函式(或 呼叫 freeLibrary的函式),因為這可能會導致在系統執行終止程式代碼之後使用 DLL。
由於呼叫進入點函式時,Kernel32.dll 保證會在進程位址空間中載入,所以呼叫 Kernel32.dll 中的函式不會在初始化程式代碼執行之前使用 DLL。 因此,進入點函式可以建立 同步處理物件,例如重要區段和 mutex,並使用 TLS,因為這些函式位於 Kernel32.dll中。 例如,呼叫登錄函式並不安全,因為它們位於 Advapi32.dll中。
呼叫其他函式可能會導致難以診斷的問題。 例如,呼叫 User、Shell 和 COM 函式可能會導致存取違規錯誤,因為其 DLL 中的某些函式會呼叫 LoadLibrary 載入其他系統元件。 相反地,在終止期間呼叫這些函式可能會導致存取違規錯誤,因為對應的元件可能已經卸除或未初始化。
下列範例示範如何建構 DLL 進入點函式。
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // handle to DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpReserved ) // reserved
{
// Perform actions based on the reason for calling.
switch( fdwReason )
{
case DLL_PROCESS_ATTACH:
// Initialize once for each new process.
// Return FALSE to fail DLL load.
break;
case DLL_THREAD_ATTACH:
// Do thread-specific initialization.
break;
case DLL_THREAD_DETACH:
// Do thread-specific cleanup.
break;
case DLL_PROCESS_DETACH:
// Perform any necessary cleanup.
break;
}
return TRUE; // Successful DLL_PROCESS_ATTACH.
}
Entry-Point 函式傳回值
呼叫 DLL 進入點函式時,因為正在載入進程,函式會傳回 TRUE 表示成功。 對於使用載入時間連結的進程,FALSE 的傳回值 會導致進程初始化失敗,且進程終止。 對於使用運行時間鏈接的進程,FALSE 的傳回值會導致 loadLibrary 或 LoadLibraryEx 函式傳回 NULL,表示失敗。 (系統會立即使用 DLL_PROCESS_DETACH 呼叫進入點函式,並卸除 DLL。呼叫函式時,會因為任何其他原因而忽略進入點函式的傳回值。