この記事では、C ランタイムの使用方法について説明します。
元の製品バージョン: Visual C++
元の KB 番号: 94248
セクション 1: 3 つの形式の C ランタイム (CRT) ライブラリを使用できる
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 が適切に初期化されていることを確認します。
初期化関数には
DllMain()
という名前を付け、リンカー オプションを使用してエントリ ポイントを指定する必要があります-entry:_DllMainCRTStartup@12
または
DLL のエントリ ポイントは、プロセスのアタッチとプロセスのデタッチ時に
CRT_INIT()
を明示的に呼び出す必要があります。
これにより、プロセスまたはスレッドが DLL にアタッチされているときに C ランタイム データを適切に割り当てて初期化したり、プロセスが DLL からデタッチされるときに C ランタイム データを適切にクリーンアップしたり、DLL 内のグローバル C++ オブジェクトを適切に構築および破棄したりできます。
Win32 SDK のサンプルはすべて、最初のメソッドを使用します。 例として使用します。 DllEntryPoint()
の Win32 プログラマ リファレンスと、DllMain()
の Visual C++ ドキュメントも参照してください。 DllMainCRTStartup()
呼び出しCRT_INIT()
し、CRT_INIT()
が存在する場合はアプリケーションの DllMain() を呼び出します。
2 番目のメソッドを使用して CRT 初期化コードを自分で呼び出す場合は、 DllMainCRTStartup()
と DllMain()
を使用する代わりに、次の 2 つの手法があります。
初期化コードを実行するエントリ関数がない場合は、dll のエントリ ポイントとして
CRT_INIT()
を指定します。 NTWIN32を含めたと仮定します。を @12 として定義する MAK は、DLL のリンク行: -entry:_CRT_INIT$(DLLENTRY)
にオプションを追加します。または
独自の DLL エントリ ポイントがある場合は、エントリ ポイントで次の操作を行います。
次のプロトタイプを
CRT_INIT()
に使用します。BOOL WINAPI _CRT_INIT(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);
戻り値
CRT_INIT()
詳細については、DllEntryPoint のドキュメントを参照してください。同じ値が返されます。DLL_PROCESS_ATTACH
とDLL_THREAD_ATTACH
で (これらのフラグの詳細については、Win32 API リファレンスの DllEntryPoint を参照)、C ランタイム関数が呼び出されるか、浮動小数点演算が実行される前に、CRT_INIT()
を呼び出します。独自のプロセス/スレッド初期化/終了コードを呼び出します。
DLL_PROCESS_DETACH
およびDLL_THREAD_DETACH
では、すべての C ランタイム関数が呼び出され、すべての浮動小数点演算が完了した後、最後に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);
}
Note
これは、 DllMain()
と -entry:_DllMainCRTStartup@12
を使用している場合は必要ありません。
セクション 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 変数の値を確認または設定し、他のモジュールで同じである必要があります。 関連する問題は、
perror()
が errno を使用するため、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が _create()
または _open()
を使用して CRT ファイル ハンドルを作成する場合、このファイル ハンドルは、.EXE ファイル内の _lseek()
、 _read()
、 _write()
、 _close()
などにだけ渡すことができます。 この CRT ファイル ハンドルをどちらの DLL にも渡さないでください。 DLL から取得した CRT ファイル ハンドルを他の DLL または.EXEに渡さないでください。
DLL A が malloc()
を使用してメモリ ブロックを割り当てる場合、そのブロックを操作するために free()
、 _expand()
、または realloc()
を呼び出できるのは DLL A だけです。 DLL A から malloc()
を呼び出して、.EXEまたは DLL B からそのブロックを解放することはできません。
Note
3 つのモジュールすべてが CRTDLL にリンクされている場合。LIB または 3 つすべてが MSVCRT にリンクされました。LIb では、これらの制限は適用されません。
DLL を LIBC とリンクする場合。LIB では、このような DLL がマルチスレッド プログラムによって呼び出される可能性がある場合は、DLL で同時に実行されている複数のスレッドがサポートされないため、大きな問題が発生する可能性があることに注意してください。 DLL がマルチスレッド プログラムによって呼び出される可能性がある場合は、マルチスレッド プログラム (LIBCMT) をサポートするライブラリの 1 つと DLL をリンクしてください。LIB、CRTDLL。LIB または MSVCRT。LIB)。