DllMain 進入點

動態連結程式庫的選擇性進入點, (DLL) 。 當系統啟動或終止進程或執行緒時,它會使用進程的第一個執行緒,針對每個載入的 DLL 呼叫進入點函式。 當使用 LoadLibraryFreeLibrary 函式載入或卸載 DLL 時,系統也會呼叫 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 Library Entry-Point 函式中的範例。

警告

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

語法

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

參數

hinstDLL [in]

DLL 模組的控制碼。 此值是 DLL 的基底位址。 DLL 的 HINSTANCE 與 DLL 的 HMODULE 相同,因此 hinstDLL 可用於呼叫需要模組控制碼的函式。

fdwReason [in]

原因代碼,指出呼叫 DLL 進入點函式的原因。 此參數可以是下列其中一個值。

意義
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]

如果 fdwReason是DLL_PROCESS_ATTACH則動態載入的 lpvReservedNull ,靜態載入則為非 Null。

如果fdwReason是DLL_PROCESS_DETACH,如果呼叫FreeLibrary或 DLL 載入失敗,則lpvReservedNull;如果進程已終止,則為非Null

傳回值

當系統使用DLL_PROCESS_ATTACH值呼叫DllMain函式時,如果成功,則函式會傳回TRUE;如果初始化失敗,則傳回FALSE。 如果呼叫DllMain時傳回值為FALSE,因為進程使用LoadLibrary函式,LoadLibrary會傳回 Null。 (系統會立即使用DLL_PROCESS_DETACH呼叫進入點函式,並卸載 DLL.) 如果在進程初始化期間呼叫DllMain時傳回值為FALSE,進程就會終止併發生錯誤。 若要取得擴充的錯誤資訊,請呼叫 GetLastError

當系統以DLL_PROCESS_ATTACH以外的任何值呼叫DllMain函式時,會忽略傳回值。

備註

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_THREAD_DETACH 值呼叫 DLL 的進入點函式。 DLL 只會傳送 DLL_PROCESS_DETACH 通知。 DLL 可以有機會清除 DLL 已知所有線程的所有資源。

處理 DLL_PROCESS_DETACH時,只有在動態卸載 DLL (lpvReserved 參數為 Null) 時,DLL 才應該釋放堆積記憶體等資源。 如果進程正在終止 (lpvReserved 參數為非Null) ,除了目前線程以外的所有線程都已經結束,或已由 ExitProcess 函式的呼叫明確終止,這可能會讓某些進程資源,例如堆積處於不一致的狀態。 在此情況下,DLL 無法安全地清除資源。 相反地,DLL 應該允許作業系統回收記憶體。

如果您藉由呼叫 TerminateProcessTerminateJobObject終止進程,該進程的 DLL 不會收到 DLL_PROCESS_DETACH 通知。 如果您藉由呼叫 TerminateThread終止執行緒,該執行緒的 DLL 不會收到 DLL_THREAD_DETACH 通知。

進入點函式應該只會執行簡單的初始化或終止工作。 它不得呼叫LoadLibrary 或 LoadLibraryEx函式 (或呼叫這些函式的函式) ,因為這可能會在 DLL 載入順序中建立相依性迴圈。 這可能會導致在系統執行初始化程式碼之前使用 DLL。 同樣地,進入點函式不得呼叫 FreeLibrary 函式 (,或是在進程終止期間呼叫 FreeLibrary) 的函式,因為這可能會導致在系統執行終止程式碼之後使用 DLL。

由於呼叫進入點函式時,保證會在進程位址空間中載入Kernel32.dll,因此呼叫 Kernel32.dll 中的函式,不會在執行 DLL 初始化程式碼之前使用 DLL。 因此,進入點函式可以在不會載入其他 DLL 的Kernel32.dll中呼叫函式。 例如, DllMain 可以建立 同步處理物件 ,例如重要區段和 Mutex,並使用 TLS。 可惜的是,Kernel32.dll中沒有完整的安全函式清單。

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

由於 DLL 通知已序列化,進入點函式不應嘗試與其他執行緒或進程通訊。 死結可能會因故發生。

如需撰寫 DLL 時的最佳做法資訊,請參閱 動態連結程式庫最佳做法

如果您的 DLL 與 C 執行時間程式庫 (CRT) ,CRT 所提供的進入點會呼叫全域和靜態 C++ 物件的建構函式和解構函式。 因此, DllMain 的這些限制也適用于建構函式和解構函式,以及從其中呼叫的任何程式碼。

除非您的 DLL 與靜態 C 執行時間程式庫連結 ( CRT) ,否則在接收DLL_PROCESS_ATTACH時,請考慮呼叫DisableThreadLibraryCalls

規格需求

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

另請參閱

動態連結程式庫Entry-Point函式

動態連結程式庫函式

FreeLibrary

GetModuleFileName

LoadLibrary

TlsAlloc

TlsFree

DisableThreadLibraryCalls