DllMain エントリ ポイント

ダイナミック リンク ライブラリ (DLL) への省略可能なエントリ ポイント。 システムは、プロセスまたはスレッドを開始または終了すると、プロセスの最初のスレッドを使用して、読み込まれた DLL ごとにエントリ ポイント関数を呼び出します。 システムは、 LOADLibrary 関数と FreeLibrary 関数を使用して 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.
}

ダイナミック リンク ライブラリ 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) インデックスを割り当てたりできます。
lpvReserved パラメーターは、DLL が静的または動的に読み込まれているかどうかを示します。
DLL_PROCESS_DETACH
0
DLL が呼び出し元プロセスの仮想アドレス空間からアンロードされています。これは、読み込みに失敗したか、参照カウントが 0 に達したためです (プロセスが LoadLibrary と呼ばれるたびに、プロセスが FreeLibrary を 1 回終了または呼び出しました)。
lpvReserved パラメーターは、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 の読み込みが失敗し、プロセスが終了している場合は NULL 以外の場合、lpvReservedNULL になります

戻り値

システムがDLL_PROCESS_ATTACH値を使用して DllMain 関数を呼び出すと、関数が成功した場合は TRUE、初期化に失敗した場合は FALSE を返します。 プロセスが LoadLibrary 関数を使用しているために DllMain が呼び出されたときに戻り値が FALSE の場合、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 の読み込み失敗、プロセスの終了、または FreeLibrary の呼び出しの結果として DLL がプロセスからアンロードされた場合、システムはプロセスの個々のスレッドの DLL_THREAD_DETACH 値を使用して DLL のエントリ ポイント関数を呼び出しません。 DLL は、 DLL_PROCESS_DETACH 通知のみを送信します。 DLL はこの機会に、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 が使用されることはありません。 したがって、エントリ ポイント関数は、他の DLL を読み込まないKernel32.dllの関数を呼び出すことができます。 たとえば、 DllMain では、重要なセクションやミューテックスなどの 同期オブジェクト を作成し、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 [デスクトップ アプリのみ]
Header
Process.h

関連項目

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

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

FreeLibrary

GetModuleFileName

Loadlibrary

TlsAlloc

TlsFree

DisableThreadLibraryCalls