DllMain エントリ ポイント

ダイナミック リンク ライブラリ (DLL) への省略可能なエントリ ポイント。 システムは、プロセスまたはスレッドを開始または終了するときに、プロセスの最初のスレッドを使用して、読み込まれた DLL ごとにエントリ ポイント関数を呼び出します。 システムは、 LOADLibrary 関数と FreeLibrary 関数を使用して DLL を読み込みまたはアンロードするときにも、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 のエントリ ポイントで安全に実行できる処理には大きな制限があります。 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
プロセスの起動または LoadLibrary の呼び出しの結果として、DLL が現在のプロセスの仮想アドレス空間に読み込まれています。 DLL では、この機会を使用してインスタンス データを初期化したり、 TlsAlloc 関数を使用してスレッド ローカル ストレージ (TLS) インデックスを割り当てることができます。
lpReserved パラメーターは、DLL が静的または動的に読み込まれているかどうかを示します。
DLL_PROCESS_DETACH
0
DLL は、読み込みに失敗したか、参照カウントが 0 に達したために、呼び出し元プロセスの仮想アドレス空間からアンロードされています (プロセスは LoadLibrary を呼び出すたびに 1 回 FreeLibrary を終了または呼び出しました)。
lpReserved パラメーターは、FreeLibrary 呼び出し、読み込み失敗、または終了処理の結果として DLL がアンロードされているかどうかを示します。
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 が呼び出されたか、DLL の読み込みが失敗した場合は lpvReservedNULL になり、プロセスが終了している場合は NULL 以外になります。

戻り値

システムがDLL_PROCESS_ATTACH値を持つ DllMain 関数を呼び出すと、関数が成功した場合は TRUE を返し、初期化に失敗した場合は FALSE を返します。 プロセスが LoadLibrary 関数を使用しているために DllMain が呼び出されたときに戻り値が FALSE の場合、LoadLibrary は NULL を返します。 (システムは、DLL_PROCESS_DETACHを使用してエントリ ポイント関数を直ちに呼び出し、DLL をアンロードします)。プロセスの初期化中に DllMain が呼び出されたときに戻り値が FALSE の場合、プロセスはエラーで終了します。 詳細なエラー情報を得るには、GetLastError を呼び出します。

システムが dllMain 関数を DLL_PROCESS_ATTACH 以外の値で呼び出すと、戻り値は無視されます。

注釈

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 の読み込み失敗、プロセスの終了、または FreeLibrary の呼び出しの結果としてプロセスから DLL がアンロードされた場合、システムはプロセスの個々のスレッドの DLL_THREAD_DETACH 値を持つ DLL のエントリ ポイント関数を呼び出しません。 DLL には、 DLL_PROCESS_DETACH 通知のみが送信されます。 DLL は、DLL に認識されているすべてのスレッドのすべてのリソースをクリーンアップする機会を得ることができます。

DLL_PROCESS_DETACHを処理する場合、DLL は、DLL が動的にアンロードされている場合にのみ、ヒープ メモリなどのリソースを解放する必要があります (lpReserved パラメーターは NULL です)。 プロセスが終了している場合 ( lpvReserved パラメーターが NULL 以外の場合)、現在のスレッドを除くプロセス内のすべてのスレッドが既に終了しているか、 ExitProcess 関数の呼び出しによって明示的に終了されているため、ヒープなどの一部のプロセス リソースが不整合な状態になる可能性があります。 この場合、DLL がリソースをクリーンアップしても安全ではありません。 代わりに、DLL を使用して、オペレーティング システムがメモリを再利用できるようにする必要があります。

TerminateProcess または TerminateJobObject を呼び出してプロセスを終了した場合、そのプロセスの DLL はDLL_PROCESS_DETACH通知を受け取りません。 TerminateThread を呼び出してスレッドを終了した場合、そのスレッドの DLL はDLL_THREAD_DETACH通知を受け取りません。

エントリ ポイント関数は、単純な初期化タスクまたは終了タスクのみを実行する必要があります。 これは DLL の読み込み順序で依存関係ループを作成する可能性があるため、LoadLibrary 関数または LoadLibraryEx 関数 (またはこれらの関数を呼び出す関数) を呼び出してはなりません。 これにより、システムが初期化コードを実行する前に DLL が使用される可能性があります。 同様に、エントリ ポイント関数は、プロセスの終了時に FreeLibrary 関数 (または FreeLibrary を呼び出す関数) を呼び出してはなりません。これは、システムが終了コードを実行した後に DLL が使用される可能性があるためです。

エントリ ポイント関数が呼び出されると、Kernel32.dllがプロセス アドレス空間に読み込まれると保証されるため、Kernel32.dllで関数を呼び出しても、初期化コードが実行される前に DLL が使用されることはありません。 したがって、エントリ ポイント関数は、他の DLL を読み込まないKernel32.dllで関数を呼び出すことができます。 たとえば、 DllMain では、重要なセクションやミューテックスなどの 同期オブジェクト を作成し、TLS を使用できます。 残念ながら、Kernel32.dllには安全な機能の包括的なリストはありません。

Kernel32.dll以外の DLL を必要とする関数を呼び出すと、診断が困難な問題が発生する可能性があります。 たとえば、User、Shell、COM 関数を呼び出すと、他のシステム コンポーネントを読み込む関数があるため、アクセス違反エラーが発生する可能性があります。 逆に、終了時にこのような関数を呼び出すと、対応するコンポーネントが既にアンロードまたは初期化されていない可能性があるため、アクセス違反エラーが発生する可能性があります。

DLL 通知はシリアル化されるため、エントリ ポイント関数は他のスレッドやプロセスとの通信を試みてはなりません。 その結果、デッドロックが発生する可能性があります。

DLL を記述する際のベスト プラクティスについては、次を参照してください https://docs.microsoft.com/windows/win32/dlls/dynamic-link-library-best-practices

DLL が C ランタイム ライブラリ (CRT) にリンクされている場合、CRT によって提供されるエントリ ポイントは、グローバルおよび静的 C++ オブジェクトのコンストラクターとデストラクターを呼び出します。 したがって、 DllMain に対するこれらの制限は、コンストラクターとデストラクター、およびそれらから呼び出されるコードにも適用されます。

DLL が静的 C ランタイム ライブラリ (CRT) とリンクされていない限り、DLL_PROCESS_ATTACHを受信するときに DisableThreadLibraryCalls を呼び出す方法を検討してください。

要件

要件
サポートされている最小のクライアント
Windows XP [デスクトップ アプリのみ]
サポートされている最小のサーバー
Windows Server 2003 [デスクトップ アプリのみ]
Header
Process.h

こちらもご覧ください

ダイナミック リンク ライブラリ Entry-Point関数

ダイナミック リンク ライブラリ関数

FreeLibrary

GetModuleFileName

Loadlibrary

TlsAlloc

TlsFree

DisableThreadLibraryCalls