DllMain 入口

動態連結函式庫(DLL)的可選入口。 當系統啟動或終止一個程序或執行緒時,會使用程序的第一個執行緒呼叫每個載入 DLL 的入口函式。 系統也會在 DLL 載入或卸載時,使用 LoadLibrary 和 FreeLibrary 函式呼叫進入點函式。

Warning

您可以在 DLL 進入點安全地執行哪些動作有重大限制。 請參閱一般最佳實務,以了解在 DllMain 中呼叫不安全的 Windows API。 如果您需要最簡單的初始化以外的任何專案,請在 DLL 的初始化函式中執行此動作。 你可以要求應用程式在 DllMain 執行後、且在 DLL 中呼叫其他函式之前,先呼叫初始化函式。

範例

BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,  // handle to DLL module
    DWORD fdwReason,     // reason for calling function
    LPVOID lpvReserved )  // 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:
        
            if (lpvReserved != nullptr)
            {
                break; // do not do cleanup if process termination scenario
            }
            
         // Perform any necessary cleanup.
            break;
    }
    return TRUE;  // Successful DLL_PROCESS_ATTACH.
}

這是 Dynamic-Link 函式庫 Entry-Point 函式的範例。

語法

BOOL WINAPI DllMain(
  _In_ HINSTANCE hinstDLL,
  _In_ DWORD     fdwReason,
  _In_ LPVOID    lpvReserved
);

Parameters

hinstDLL [在裡面]

一個 DLL 模組的 handle。 該值即為 DLL 的基底位址。 DLL 的 HINSTANCE 與 DLL 的 HMODULE 相同,因此 hinstDLL 可用於呼叫需要模組 handle 的函式。

fdwReason [在]

原因碼表示為什麼會呼叫 DLL 入口點函式。 此參數可以是下列其中一個值。

價值 Meaning
DLL_PROCESS_ATTACH
1
DLL 會因程序啟動或呼叫 LoadLibrary 而被載入到目前程序的虛擬位址空間中。 DLL 可以利用此機會初始化任何實例資料,或使用 TlsAlloc 函式來分配執行緒本地儲存(TLS)索引。
lpvReserved 參數表示 DLL 是靜態載入還是動態載入。
DLL_PROCESS_DETACH
0
DLL 之所以會從呼叫程序的虛擬位址空間中卸載,是因為它載入失敗,或參考計數已歸零(程序每次呼叫 LoadLibrary 時,都要終止或呼叫一次 FreeLibrary)。
lpvReserved 參數表示 DLL 是否因 FreeLibrary 呼叫、載入失敗或程序終止而被卸載。
DLL 可利用此機會呼叫 TlsFree 函式,釋放使用 TlsAlloc 分配的 TLS 索引,並釋放任何執行緒的本地資料。
請注意,收到 DLL_PROCESS_DETACH 通知的執行緒不一定是收到 DLL_PROCESS_ATTACH 通知的同一個執行緒。
DLL_THREAD_ATTACH
2
目前的流程正在建立一個新的執行緒。 當發生這種情況時,系統會呼叫目前所有已連接於程序的 DLL 的入口函數。 這個決定是在新討論串的情境下做出的。 DLL 可以利用這個機會初始化執行緒的 TLS 插槽。 以 DLL_PROCESS_ATTACH 呼叫 DLL 入口點函式的執行緒,並不會呼叫 DLL_THREAD_ATTACH 的 DLL 入口點函式。
請注意,DLL的入口點函式僅由程序載入DLL後建立的執行緒以此值呼叫。 當使用 LoadLibrary 載入 DLL 時,現有執行緒不會呼叫新載入 DLL 的入口函式。
DLL_THREAD_DETACH
3
有一條線程乾淨地退出。 如果 DLL 在 TLS 插槽中儲存了指向已分配記憶體的指標,它應該利用這個機會釋放記憶體。 系統會呼叫所有目前載入 DLL 的入口函式,並以此值進行。 這個決定是在退出執行緒的情境下做出的。

lpvReserved [in]

若 fdwReasonDLL_PROCESS_ATTACH,lpvReserved 對動態載入為 NULL,靜態載入為非 NULL。

若 fdwReasonDLL_PROCESS_DETACH,若呼叫 FreeLibrary,lpvReserved 為 NULL;若程序終止,則為 NULL;若程序終止,則為非 NULL。

返回值

當系統以 DLL_PROCESS_ATTACH 值呼叫 DllMain 函式時,函式成功則回傳 TRUE,初始化失敗則回傳 FALSE。 如果在呼叫 DllMain 時回傳值為 FALSE,因為程序使用 LoadLibrary 函式,LoadLibrary 會回傳 NULL。 (系統會立即呼叫你的入口函式並卸載 DLL,DLL_PROCESS_DETACH若在程序初始化時呼叫 DllMain 時回傳值為 FALSE,程序將以錯誤終止。 若要取得擴充錯誤資訊,請呼叫 GetLastError。

當系統呼叫 DllMain 函式時,該函式的值非 DLL_PROCESS_ATTACH,則忽略回傳值。

Remarks

DllMain 是函式庫定義函式名稱的佔位符。 你必須在建立 DLL 時指定你使用的實際名稱。 更多資訊請參閱開發工具附帶的文件。

在初始程序啟動或呼叫 LoadLibrary 後,系統會掃描已載入的 DLL 清單以查找該程序。 對於每個尚未被呼叫過 DLL_PROCESS_ATTACH 值的 DLL,系統會呼叫 DLL 的入口函數。 此呼叫是在導致程序位址空間變更的執行緒情境中進行,例如程序的主執行緒或呼叫 LoadLibrary 的執行緒。 系統在整個進程層面以序列化方式存取入口點。 DllMain 中的執行緒會保留載入器鎖定,因此無法動態載入或初始化額外的 DLL。

如果 DLL 的入口函式在收到 DLL_PROCESS_ATTACH 通知後回傳 FALSE,則會收到 DLL_PROCESS_DETACH 通知,DLL 會立即卸載。 然而,如果 DLL_PROCESS_ATTACH 碼拋出例外,入口點函式將不會收到 DLL_PROCESS_DETACH 通知。

有些情況是,即使該終端執行緒從未被呼叫過該執行緒的 DLL_THREAD_ATTACH ,也呼叫了該進入點函式:

  • 執行緒是程序中的初始執行緒,因此系統會以 DLL_PROCESS_ATTACH 值呼叫入口點函式。
  • 當呼叫 LoadLibrary 函式時,執行緒已經在執行中,因此系統從未呼叫該函式的入口函式。

當 DLL 因 DLL 載入失敗、程序終止或呼叫 FreeLibrary 而從程序中卸載時,系統不會呼叫 DLL 的入口函式,並以程序中各個執行緒的 DLL_THREAD_DETACH 值。 DLL只會收到 DLL_PROCESS_DETACH 通知。 DLL 可以藉此機會清理所有已知執行緒的資源。

處理 DLL_PROCESS_DETACH時,DLL 只有在 DLL 動態卸載( lpvReserved 參數為 NULL)時才應釋放堆積記憶體等資源。 若程序正在終止( lpvReserved 參數非 NULL),除當前執行緒外,程序中所有執行緒都已退出或已被 ExitProcess 函式明確終止,這可能導致部分程序資源如堆積處於不一致狀態。 在這種情況下,DLL 清理資源是不安全的。 相反地,DLL 應該允許作業系統回收記憶體。

如果你透過呼叫 TerminateProcess 或 TerminateJobObject 終止程序,該程序的 DLL 不會收到 DLL_PROCESS_DETACH 通知。 如果你透過呼叫 TerminateThread 終止執行緒,該執行緒的 DLL 不會收到 DLL_THREAD_DETACH 通知。

入口函式應僅執行簡單的初始化或終止任務。 它不得呼叫 LoadLibrary 或 LoadLibraryEx 函式(或呼叫這些函式的函式),因為這可能會在 DLL 載入順序中產生相依迴圈。 這可能導致 DLL 在系統執行初始化程式碼之前就被使用。 同樣地,入口點函式在程序終止時不得呼叫 FreeLibrary 函式(或呼叫 FreeLibrary 的函式),因為這可能導致系統執行終止程式碼後仍使用 DLL。

由於當呼叫入口點函式時,Kernel32.dll 必定會載入程序位址空間,因此呼叫 Kernel32.dll 函式時,DLL 不會在初始化程式碼執行前被使用。 因此,入口點函式可以呼叫 Kernel32.dll 中不會載入其他 DLL 的函式。 例如, DllMain 可以建立同步 物件 ,如關鍵區段和互斥,並使用 TLS。 遺憾的是,Kernel32.dll中並沒有完整的安全功能清單。

呼叫需要 DLL 的函式 Kernel32.dll 可能導致難以診斷的問題。 例如,呼叫 User、Shell 和 COM 函式可能會造成存取違規錯誤,因為有些函式會載入其他系統元件。 相反地,在終止時呼叫此類函式可能導致存取違規錯誤,因為相應元件可能已經卸載或未初始化。

由於 DLL 通知是序列化的,入口點函式不應嘗試與其他執行緒或程序通訊。 因此可能會出現死結。

關於撰寫 DLL 的最佳實務,請參見 動態連結函式庫最佳實務。

如果你的 DLL 是連結到 C 執行時函式庫(CRT),CRT 提供的入口點會呼叫全域與靜態 C++ 物件的建構子與結構子。 因此, DllMain 的這些限制同樣適用於建構子、解構子,以及從它們中呼叫的任何程式碼。

除非你的 DLL 是用靜態 C 執行時函式庫(CRT)連結的,否則可以考慮在收到 DLL_PROCESS_ATTACH 時呼叫 DisableThreadLibraryCalls。

要求

需求 價值
最低支援的用戶端
Windows XP [僅限傳統型應用程式]
最低支援的伺服器
Windows Server 2003 [僅限傳統型應用程式]
Header
Process.h

參見

Dynamic-Link 圖書館 Entry-Point 功能

Dynamic-Link 連結庫函式

自由圖書館

GetModuleFileName

LoadLibrary

TlsAlloc

免費

DisableThreadLibraryCalls