C Run-Time を使用する

この記事では、C ランタイムの使用方法について説明します。

元の製品バージョン: Visual C++
元の KB 番号: 94248

セクション 1: C Run-Time (CRT) ライブラリの 3 つの形式を使用できます

Win32 SDK には、次の 3 つの形式の C ランタイム ライブラリが用意されています。

  • Libc。LIB は、シングル スレッド プログラム用の静的にリンクされたライブラリです。

  • LIBCMT。LIB は、マルチスレッド プログラムをサポートする静的にリンクされたライブラリです。

  • CRTDLL。LIB は、マルチスレッド プログラムもサポートする CRTDLL.DLL のインポート ライブラリです。 CRTDLL.DLL 自体がWindows NTの一部です。

Microsoft Visual C++ 32 ビット エディションには、これら 3 つの形式も含まれていますが、DLL の CRT は MSVCRT という名前です。Lib。 DLL は再頒布可能です。 その名前は、VC++ のバージョン (つまり、MSVCRT10.DLL または MSVCRT20.DLL) によって異なります。 ただし、CRTDLL では、MSVCRT10.DLL は Win32s ではサポートされていないことに注意してください。LIB は Win32s でサポートされています。 MSVCRT20.DLL には、Windows NT用と Win32s 用の 2 つのバージョンがあります。

セクション 2: DLL のビルド時に CRT ライブラリを使用する

C ランタイム ライブラリのいずれかを使用する DLL をビルドする場合は、CRT が適切に初期化されていることを確認します。

  1. 初期化関数の名前 DllMain() を指定し、リンカー オプションを使用してエントリ ポイントを指定する必要があります -entry:_DllMainCRTStartup@12

    または

  2. DLL のエントリ ポイントは、プロセスアタッチとプロセスデタッチで明示的に呼び出す CRT_INIT() 必要があります。

これにより、プロセスまたはスレッドが DLL にアタッチされているときに C ランタイム データを適切に割り当てて初期化し、プロセスが DLL からデタッチされるときに C ランタイム データを適切にクリーンし、DLL 内のグローバル C++ オブジェクトを適切に構築および破棄できます。

Win32 SDK サンプルはすべて、最初のメソッドを使用します。 それらを例として使用します。 また、 の Win32 プログラマリファレンス DllEntryPoint() と の Visual C++ ドキュメントも参照してください DllMain()。 が存在するDllMainCRTStartup()場合は、 をCRT_INIT()呼び出してアプリケーションの DllMain() を呼び出CRT_INIT()します。

と を使用するのではなく、2 番目のメソッドを使用して CRT 初期化コードを自分で呼び出す場合は、次の DllMainCRTStartup()DllMain()2 つの手法があります。

  1. 初期化コードを実行するエントリ関数がない場合は、DLL のエントリ ポイントとして を指定 CRT_INIT() します。 NTWIN32を含めたと仮定します。@12 として定義DLLENTRYされている MAK は、DLL のリンク行にオプションを追加します-entry:_CRT_INIT$(DLLENTRY)

    または

  2. 独自の DLL エントリ ポイントがある場合は、エントリ ポイントで次の操作を行います。

    1. に対してこのプロトタイプを使用します CRT_INIT()BOOL WINAPI _CRT_INIT(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);

      戻り値の CRT_INIT() 詳細については、DllEntryPoint のドキュメントを参照してください。同じ値が返されます。

    2. および DLL_PROCESS_ATTACHDLL_THREAD_ATTACH (Win32 API リファレンスの DllEntryPoint を参照してください)、C ランタイム関数が呼び出されるか、浮動小数点演算が実行される前に、 を呼び出 CRT_INIT()します。

    3. 独自のプロセス/スレッド初期化/終了コードを呼び出します。

    4. DLL_THREAD_DETACHではDLL_PROCESS_DETACH、すべての C ランタイム関数が呼び出され、すべての浮動小数点演算が完了した後、last を呼び出CRT_INIT()します。

エントリ ポイントCRT_INIT()のすべてのパラメーターにCRT_INIT()渡してください。これらのパラメーターを想定しているため、省略すると確実に動作しない場合があります (特に、プロセスの初期化または終了が必要かどうかを判断するには fdwReason が必要です)。

DLL エントリ ポイントでこれらの呼び出し CRT_INIT() を行うタイミングと方法を示すスケルトン サンプル エントリ ポイント関数を次に示します。

BOOL WINAPI DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason,
LPVOID lpReserved)
{
    if (fdwReason == DLL_PROCESS_ATTACH || fdwReason == DLL_THREAD_ATTACH)
    if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved))
    return(FALSE);

    if (fdwReason == DLL_PROCESS_DETACH || fdwReason == DLL_THREAD_DETACH)
    if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved))
    return(FALSE);
    return(TRUE);
}

注:

-entry:_DllMainCRTStartup@12を使用DllMain()している場合は、これは必要ありません。

セクション 3: NTWIN32の使用。MAK を使用してビルド プロセスを簡略化する

NTWIN32で定義されているマクロがあります。MAK を使用して、メイクファイルを簡略化し、競合を回避するために適切にビルドされるようにすることができます。 このため、Microsoft ではNTWIN32の使用を強くお勧めします。MAK とその中のマクロ。

コンパイルには、 を使用します $(cvarsdll) for apps/DLLs using CRT in a DLL

リンクする場合は、次のいずれかを使用します。

  • $(conlibsdll) for console apps/DLLs using CRT in a DLL
  • $(guilibsdll) for GUI apps using CRT in a DLL

セクション 4: 複数の CRT ライブラリを使用するときに発生する問題

C ランタイムを実行するアプリケーションが C ランタイム呼び出しを行う DLL にリンクする場合は、両方が静的にリンクされた C ランタイム ライブラリ (LIBC) のいずれかにリンクされている場合に注意してください。LIB または LIBCMT。LIB) では、.EXE と DLL には、すべての C ランタイム関数とグローバル変数の個別のコピーが含まれます。 つまり、C ランタイム データは、.EXE と DLL の間で共有できません。 結果として発生する可能性のある問題の一部を次に示します。

  • バッファーされたストリーム ハンドルを .EXE/DLL から他のモジュールに渡す

  • .EXE/DLL で C ランタイム呼び出しを使用してメモリを割り当て、もう一方のモジュールで再割り当てまたは解放する

  • .EXE/DLL 内のグローバル errno 変数の値を確認または設定し、他のモジュールで同じである必要があります。 関連する問題は、errno を使用するためperror()、C ランタイム エラーが発生した反対側のモジュールで を呼び出perror()すことです。

これらの問題を回避するには、.EXE と DLL の両方を CRTDLL にリンクします。LIB または MSVCRT。LIB。これにより、.EXE と DLL の両方で、DLL 内の CRT に含まれる共通の関数とデータのセットを使用でき、ストリーム ハンドルなどの C ランタイム データは、.EXE と DLL の両方で共有できます。

セクション 5: ライブラリの種類の混在

DLL は CRTDLL とリンクできます。LIB/MSVCRT。CRT データ構造を混在させ、CRT ファイル ハンドルまたは CRT FILE* ポインターを他のモジュールに渡すのを避ける場合、.EXE がリンクされている内容に関係なく LIB。

ライブラリの種類を混在させる場合は、次に従います。

  • CRT ファイル ハンドルは、作成した CRT モジュールによってのみ操作できます。

  • CRT FILE* ポインターは、それらを作成した CRT モジュールによってのみ操作できます。

  • CRT 関数 malloc() で割り当てられたメモリは、それを割り当てた CRT モジュールによってのみ解放または再割り当てできます。

これを説明するために、次の例を考えてみましょう。

  • .EXE は MSVCRT とリンクされています。Lib
  • DLL A は LIBCMT とリンクされています。Lib
  • DLL B は CRTDLL とリンクされています。Lib

.EXE が または _open()を使用して CRT ファイル ハンドルを_create()作成する_lseek()場合、このファイル ハンドルは、.EXE ファイル内の 、_read()_write()_close()などに渡すことができます。 この CRT ファイル ハンドルをどちらの DLL にも渡さないでください。 DLL から取得した CRT ファイル ハンドルを他の DLL または .EXE に渡さないでください。

DLL A が を使用してmalloc()メモリ ブロックを割り当てる場合、DLL A は、 を_expand()呼び出すか、または realloc() を呼び出free()してそのブロックを操作できます。 DLL A からを呼び出 malloc() して、そのブロックを .EXE または DLL B から解放することはできません。

注:

3 つのモジュールすべてが CRTDLL とリンクされている場合。LIB または 3 つすべてが MSVCRT にリンクされました。LIb では、これらの制限は適用されません。

DLL を LIBC とリンクする場合。LIB では、このような DLL がマルチスレッド プログラムによって呼び出される可能性がある場合、DLL では同時に実行される複数のスレッドがサポートされないため、大きな問題が発生する可能性があることに注意してください。 DLL がマルチスレッド プログラムによって呼び出される可能性がある場合は、マルチスレッド プログラム (LIBCMT) をサポートするライブラリのいずれかにリンクしてください。LIB、CRTDLL。LIB、または MSVCRT。LIB)。