方法: /clr に移行する

このトピックでは、ネイティブコードを /clr でコンパイルするときに発生する問題について説明します (詳細については、「 /Clr (共通言語ランタイムのコンパイル) 」を参照してください)。 /clr を使用すると、ネイティブ c++ コードを呼び出し、他のネイティブ c++ コードに加えて .net アセンブリから呼び出すことができます。 /Clrでコンパイルする利点の詳細については、「混在 (ネイティブおよびマネージ) アセンブリ」と「ネイティブと .net の相互運用性」を参照してください。

/clr でライブラリ プロジェクトをコンパイルする場合の既知の問題

Visual Studio には、ライブラリプロジェクトを/clrでコンパイルするときの既知の問題がいくつか含まれています。

  • コードでは、 CRuntimeClass:: FromNameを使用して実行時に型を照会することができます。 ただし、型が ( /clrでコンパイルされた) MSIL .dll にある場合、 マネージ .dll で静的コンストラクターが実行される前にの呼び出しが失敗する可能性があります (コードがマネージ .dll で実行された後に FromName の呼び出しが発生した場合は、この問題は発生しません)。 この問題を回避するには、マネージド静的コンストラクターを強制的に構築します。それには、マネージド .dll で関数を定義してエクスポートし、その関数をネイティブ MFC アプリケーションから呼び出します。 例:

    // MFC extension DLL Header file:
    __declspec( dllexport ) void EnsureManagedInitialization () {
       // managed code that won't be optimized away
       System::GC::KeepAlive(System::Int32::MaxValue);
    }
    

Visual C++ でのコンパイル

プロジェクト内の任意のモジュールで/clrを使用する前に、最初に Visual Studio 2010 を使用してネイティブプロジェクトをコンパイルし、リンクします。

次の手順を順番に実行すると、 /clr コンパイルへの最も簡単なパスが提供されます。 これらの手順では、手順ごとにプロジェクトをコンパイルして実行することが重要です。

Visual Studio 2003 より前のバージョン

Visual Studio 2003 より前のバージョンから Visual Studio 2010 にアップグレードする場合、Visual Studio 2003 での C++ 標準準拠の強化に関連するコンパイラエラーが表示されることがあります。

Visual Studio 2003 からのアップグレード

以前に Visual Studio 2003 でビルドされたプロジェクトは、 /clrを使用せずにコンパイルする必要があります。これにより、ANSI/ISO の準拠といくつかの互換性に影響する変更が Visual Studio ます。 最も注意が必要な変更は、 CRT のセキュリティ機能です。 CRT を使用するコードでは、廃止警告が生成される可能性があります。 これらの警告は抑制できますが、新しい セキュリティが強化されたバージョンの CRT 関数 に移行することをお勧めします。これは、セキュリティが向上し、コード内のセキュリティの問題が明らかになる可能性があるためです。

C++ マネージド拡張からのアップグレード

Visual Studio 2005 以降、Managed Extensions for C++ で記述されたコードは/clrでコンパイルされません。

C コードから C++ への変換

Visual Studio は C ファイルをコンパイルしますが、 /clrコンパイルのために C++ に変換する必要があります。 実際のファイル名を変更する必要はありません。 /tp を使用できます (「 /tc、/tp、/tc、/Tp (ソースファイルの種類を指定する)」を参照してください)。 C++ のソースコードファイルは /clrに必要ですが、オブジェクト指向パラダイムを使用するようにコードを再指定する必要はありません。

C コードは、C++ ファイルとしてコンパイルするときは変更を必要とする可能性があります。 C++ の型保証の規則は厳密なので、型の変換はキャストで明示的に行う必要があります。 たとえば、malloc は void ポインターを返しますが、キャストによる C では任意の型のポインターに割り当てることができます。

int* a = malloc(sizeof(int));   // C code
int* b = (int*)malloc(sizeof(int));   // C++ equivalent

また C++ では関数ポインターの型も厳密に保証されているので、次のような C コードは変更が必要です。 C++ では、関数ポインターの型を定義する typedef を作成し、その後その型を使用して関数ポインターをキャストすることをお勧めします。

NewFunc1 = GetProcAddress( hLib, "Func1" );   // C code
typedef int(*MYPROC)(int);   // C++ equivalent
NewFunc2 = (MYPROC)GetProcAddress( hLib, "Func2" );

また C++ では、関数を参照したり呼び出すには、その前にプロトタイプを作成するか完全に定義する必要があります。

C++ でキーワードになる c コードで使用される識別子 (、、、、、など virtualnewdeletebooltruefalse ) 名前を変更する必要があります。 これは一般的に、単純な検索置換操作だけで行うことができます。

COMObj1->lpVtbl->Method(COMObj, args);  // C code
COMObj2->Method(args);  // C++ equivalent

プロジェクト設定の再構成

Visual Studio 2010 でプロジェクトをコンパイルして実行した後、既定の構成を変更するのではなく、新しいプロジェクト構成を/clr用に作成する必要があります。 /clr は一部のコンパイラオプションと互換性がなく、個別の構成を作成することで、プロジェクトをネイティブまたはマネージとしてビルドできます。 /Clr [プロパティページ] ダイアログボックスで選択されている場合、 /clr と互換性のないプロジェクト設定は無効になります ( /clr を後から選択解除した場合、無効なオプションは自動的には復元されません)。

新しいプロジェクト構成の作成

[新しい Project 構成] ダイアログ Configuration Manager ボックスの [設定のコピー ] オプションを使用すると、 既存のプロジェクト設定に基づいてプロジェクト構成を作成できます。 これを、デバッグ構成に対して 1 回、リリース構成に対して 1 回実行してください。 その後の変更は、元のプロジェクト構成をそのままにして、 /clr 固有の構成にのみ適用できます。

カスタム ビルド規則を使用するプロジェクトには、特別な注意が必要な場合があります。

この手順は、メイクファイルを使用するプロジェクトにとって別の意味があります。 この場合、別のビルドターゲットを構成することも、元ののコピーから /clr コンパイルに固有のバージョンを作成することもできます。

プロジェクトの設定の変更

/clr (共通言語ランタイムのコンパイル)に関する説明に従って、開発環境で/clrを選択できます。 既に説明したように、この手順によって競合するプロジェクト設定は自動的に無効になります。

注意

Visual Studio 2003 からマネージライブラリまたは web サービスプロジェクトをアップグレードすると、[コマンドライン] プロパティページに/zlコンパイラオプションが追加されます。 これによって LNK2001 が発生します。 [コマンドライン] プロパティページから/zlを削除して解決します。 詳細については、「 /zl (既定のライブラリ名の省略) 」および「 コンパイラとビルドプロパティの設定 」を参照してください。 または、msvcrt.dll と msvcmrt.lib をリンカーの [ 追加の依存関係 ] プロパティに追加します。

メイクを使用してビルドされたプロジェクトの場合、 /clr を追加すると、互換性のないコンパイラオプションを手動で無効にする必要があります。 /Clrと互換性のないコンパイラオプションについては、「//Clr の制限事項」を参照してください。

プリコンパイル済みヘッダー

プリコンパイル済みヘッダーは、 /clrでサポートされています。 ただし、一部の CPP ファイルを /clr でコンパイルするだけの場合 (ネイティブとして残りをコンパイルする場合)、 /clr で生成されたプリコンパイル済みヘッダーは /clrを使用して生成されたヘッダーと互換性がないため、一部の変更が必要になります。 この非互換性は、 /clr によってメタデータが生成され、メタデータが必要になるためです。 このため、 /clr でコンパイルされたモジュールでは、メタデータを含まないプリコンパイル済みヘッダーを使用できません。また、非 /clr モジュールでは、メタデータを含むプリコンパイル済みヘッダーファイルを使用できません。

いくつかのモジュールが /clr でコンパイルされたプロジェクトをコンパイルする最も簡単な方法は、プリコンパイル済みヘッダーを完全に無効にすることです。 (プロジェクトの [プロパティ ページ] ダイアログで C/C++ ノードを開き、[プリコンパイル済みヘッダー] を選択します。 次に、[プリコンパイル済みヘッダーの作成/使用] プロパティを 「プリコンパイル済みヘッダーを使用しない」 に変更します)。

しかし、特に大きなプロジェクトの場合、プリコンパイル済みヘッダーを使用するとコンパイルの処理速度がかなり短縮されるので、この機能を無効化することはお勧めしません。 この場合は、別のプリコンパイル済みヘッダーを使用するように /clr ファイルと非 /clr ファイルを構成することをお勧めします。 これを行うには、ソリューションエクスプローラーを使用して/clrコンパイルするモジュールを複数選択し、グループを右クリックして [プロパティ] を選択します。 次に、[ファイルを使用して PCH を作成/使用] プロパティと [プリコンパイル済みヘッダー ファイル] プロパティの両方を変更し、それぞれ異なるヘッダー ファイル名と PCH ファイルを使用するように設定します。

エラーの修正

/Clrを使用してコンパイルすると、コンパイラ、リンカー、またはランタイムエラーが発生する可能性があります。 このセクションでは、最も一般的な問題について説明します。

メタデータのマージ

データ型のバージョンが異なる場合、2 つの型に対して生成されるメタデータが一致しないため、リンカーのエラーが発生することがあります (通常、型のメンバーが条件付きで定義されているのに、その型を使用するすべての CPP ファイルの条件が同じでない場合に発生します)。 このような場合、リンカーのエラーが発生し、シンボル名と、型が定義されている 2 番目の OBJ ファイルの名前だけが報告されます。 多くの場合、OBJ ファイルがリンカーに送られる順序を変更し、他のデータ型バージョンの位置がリンカーにわかるようにすると対処できます。

ローダー ロックのデッドロック

"ローダーロックのデッドロック" は発生する可能性がありますが、決定的であり、実行時に検出および報告されます。 詳細については、「 混合アセンブリの初期化 」を参照してください。

データのエクスポート

DLL データのエクスポートはエラーの原因にもなりやすいため、お勧めしません。 これは、DLL のデータ セクションは、DLL のマネージド部分の一部が実行されるまで初期化されていると保証されないためです。 #Using ディレクティブを使用してメタデータを参照します。

型の可視性

ネイティブ型は、既定ではプライベートです。 これで、ネイティブ型は DLL の外側では見えなくなります。 このエラーを解決するには、これらの型に public を追加します。

浮動小数点とアラインメントの問題

__controlfp は、共通言語ランタイムではサポートされていません (詳細については __controlfp 」を参照してください)。 また、CLR は alignを尊重しません。

COM の初期化

共通言語ランタイムは、モジュールが初期化されるときに COM を自動的に初期化します (COM が自動的に初期化されるときに、 ’ MTA として実行されます)。 その結果、明示的に COM を初期化すると、COM が既に初期化されていることを示すリターン コードが生成されます。 COM が CLR によって既にいずれかのスレッド モデルに初期化されている場合、別のスレッド モデルを使用して明示的に COM を初期化しようとすると、アプリケーションが失敗するおそれがあります。

共通言語ランタイムは、既定で COM を MTA として起動します。これを変更するには、 /CLRTHREADATTRIBUTE (CLR スレッド属性の設定) を使用します。

パフォーマンスの問題

MSIL に対して生成されたネイティブの C++ メソッドが (仮想関数呼び出し、または関数ポインターの使用によって) 間接的に呼び出されると、パフォーマンスが低下することがあります。 詳細については、「 ダブルサンキング」を参照してください。

ネイティブから MSIL に移動すると、ワーキング セットのサイズが増加することがわかります。 これは、共通言語ランタイムが、プログラムを正しく実行するための多くの機能を提供するためです。 /Clrアプリケーションが正常に実行されていない場合は、C4793 (既定ではオフ) を有効にすることができます。詳細については、「コンパイラの警告 (レベル1および 3) C4793 」を参照してください。

シャットダウン時のプログラムの衝突

場合によっては、マネージド コードが実行を終了する前に CLR がシャットダウンすることがあります。 std::set_terminateSIGTERM を使用すると、この問題が発生します。 詳細については、「 Signal 定数 」と「 set_terminate 」を参照してください。

Visual C++ の新機能の使用

アプリケーションのコンパイル、リンク、および実行が完了すると、 /clrでコンパイルされた任意のモジュールで .net 機能を使用できるようになります。 詳細については、「Component Extensions for Runtime Platforms」を参照してください。

Visual C++ での .NET プログラミングの詳細については、以下の項目を参照してください。

関連項目

混合 (ネイティブおよびマネージ) アセンブリ