Dynamic-Link ライブラリ Entry-Point関数

DLL では、必要に応じてエントリ ポイント関数を指定できます。 存在する場合、システムは、プロセスまたはスレッドが DLL を読み込んだりアンロードしたりするたびに、エントリ ポイント関数を呼び出します。 これを使用して、単純な初期化タスクとクリーンアップ タスクを実行できます。 たとえば、新しいスレッドの作成時にスレッド ローカル ストレージを設定し、スレッドが終了したときにクリーンできます。

DLL を C ランタイム ライブラリとリンクしている場合は、エントリ ポイント関数が提供され、個別の初期化関数を提供できる場合があります。 詳細については、ランタイム ライブラリのドキュメントを参照してください。

独自のエントリ ポイントを指定する場合は、 DllMain 関数を参照してください。 DllMain という名前は、ユーザー定義関数のプレースホルダーです。 DLL をビルドするときに使用する実際の名前を指定する必要があります。 詳細については、開発ツールに含まれているドキュメントを参照してください。

Entry-Point関数の呼び出し

システムは、次のいずれかのイベントが発生するたびに、エントリ ポイント関数を呼び出します。

  • プロセスによって DLL が読み込まれます。 読み込み時の動的リンクを使用するプロセスの場合、プロセスの初期化中に DLL が読み込まれます。 ランタイム リンクを使用するプロセスの場合、 LoadLibrary または LoadLibraryEx が返される前に DLL が読み まれます。
  • プロセスによって DLL がアンロードされます。 プロセスが終了するか FreeLibrary 関数を呼び出すと、DLL はアンロードされ、参照カウントは 0 になります。 TerminateProcess 関数または TerminateThread 関数の結果としてプロセスが終了した場合、システムは DLL エントリ ポイント関数を呼び出しません。
  • DLL を読み込んだプロセスに新しいスレッドが作成されます。 DisableThreadLibraryCalls 関数を使用すると、スレッドの作成時に通知を無効にすることができます。
  • DLL を読み込んだプロセスのスレッドは、 TerminateThread または TerminateProcess を使用せず、正常に終了します。 プロセスが DLL をアンロードすると、プロセスの既存のスレッドごとに 1 回ではなく、プロセス全体に対してエントリ ポイント関数が 1 回だけ呼び出されます。 DisableThreadLibraryCalls を使用すると、スレッドが終了したときに通知を無効にすることができます。

エントリ ポイント関数を呼び出すことができるスレッドは一度に 1 つだけです。

システムは、関数を呼び出す原因となったプロセスまたはスレッドのコンテキストでエントリ ポイント関数を呼び出します。 これにより、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 が使用されることはありません。 したがって、エントリ ポイント関数では、重要なセクションやミューテックスなどの 同期オブジェクト を作成し、TLS を使用できます。これらの関数はKernel32.dllにあります。 たとえば、レジストリ関数はAdvapi32.dllにあるため、安全に呼び出す必要はありません。

他の関数を呼び出すと、診断が困難な問題が発生する可能性があります。 たとえば、ユーザー関数、シェル関数、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 をアンロードします。エントリ ポイント関数の戻り値は、他の理由で関数が呼び出されたときに無視されます。