Dynamic-Link程式庫Entry-Point函式

DLL 可以選擇性地指定進入點函式。 如果存在,每當進程或執行緒載入或卸載 DLL 時,系統會呼叫進入點函式。 它可用來執行簡單的初始化和清除工作。 例如,它可以在建立新執行緒時設定執行緒本機儲存體,並線上程終止時加以清除。

如果您要將 DLL 連結至 C 執行時間程式庫,它可能會為您提供進入點函式,並允許您提供個別的初始化函式。 如需詳細資訊,請參閱執行時間程式庫的檔。

如果您提供自己的進入點,請參閱 DllMain 函 式。 DllMain名稱是使用者定義函式的預留位置。 您必須指定您在建置 DLL 時所使用的實際名稱。 如需詳細資訊,請參閱開發工具隨附的檔。

呼叫 Entry-Point 函式

每當發生下列任何一個事件時,系統就會呼叫進入點函式:

  • 進程會載入 DLL。 對於使用載入時間動態連結的進程,DLL 會在進程初始化期間載入。 對於使用執行時間連結的進程,DLL 會在 LoadLibraryLoadLibraryEx 傳回之前載入。
  • 進程會卸載 DLL。 當進程終止或呼叫 FreeLibrary 函式且參考計數變成零時,就會卸載 DLL。 如果進程因 TerminateProcessTerminateThread 函式而終止,則系統不會呼叫 DLL 進入點函式。
  • 新的執行緒會在已載入 DLL 的進程中建立。 您可以使用 DisableThreadLibraryCalls 函式在建立執行緒時停用通知。
  • 載入 DLL 的進程執行緒會正常終止,而不是使用 TerminateThreadTerminateProcess。 當進程卸載 DLL 時,進入點函式只會針對整個進程呼叫一次,而不是針對進程的每個現有線程呼叫一次。 您可以使用 DisableThreadLibraryCalls 線上程終止時停用通知。

一次只能有一個執行緒呼叫進入點函式。

系統會在導致呼叫函式的進程或執行緒內容中呼叫進入點函式。 這可讓 DLL 使用其進入點函式,在呼叫進程的虛擬位址空間中配置記憶體,或開啟進程可存取的控制碼。 進入點函式也可以使用執行緒本機儲存體 (TLS) ,配置私用給新執行緒的記憶體。 如需執行緒本機儲存體的詳細資訊,請參閱 執行緒本機儲存體

Entry-Point函式定義

DLL 進入點函式必須使用標準呼叫慣例來宣告。 如果未正確宣告 DLL 進入點,則不會載入 DLL,而且系統會顯示訊息,指出 DLL 進入點必須使用 WINAPI 宣告。

在函式主體中,您可以處理下列呼叫 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 初始化程式碼之前使用 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.) 因為任何其他原因呼叫函式時會忽略進入點函式的傳回值。