方法: 移行先 /clr
この記事では、 /clr
を使用してネイティブ コードをコンパイルするときに発生する問題について説明します。 (詳細については、「 /clr (共通言語ランタイム のコンパイル)」を参照してください)。 /clr
では、他のネイティブ C++ コードに加えて、ネイティブ C++ コードを呼び出し、.NET アセンブリから呼び出すことができます。 /clr
を使用してコンパイルする利点の詳細については、「Mixed (ネイティブおよびマネージド) アセンブリおよび Native および .NET の相互運用性を参照してください。
ライブラリ プロジェクトのコンパイルに関する既知の問題 /clr
Visual Studio には、 /clr
を使用してライブラリ プロジェクトをコンパイルするときの既知の問題がいくつか含まれています。
コードでは、実行時に
CRuntimeClass::FromName
を使用して型を照会できます。 ただし、型が MSIL DLL (/clr
でコンパイル) にある場合、マネージド DLL で静的コンストラクターが実行される前にFromName
の呼び出しが失敗する可能性があります。 (マネージド 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 にリンクします。
次の手順に従って、 /clr
コンパイルへの最も簡単なパスを指定します。 これらの各手順の後に、プロジェクトをコンパイルして実行することが重要です。
以前のバージョンの Visual Studio からのアップグレード
Visual Studio を以前のバージョンからアップグレードする場合は、Visual Studio の強化された Standard C++ 準拠に関連するコンパイラ エラーが表示されることがあります。
以前のバージョンの Visual Studio でビルドされたプロジェクトも、最初に /clr
せずにコンパイルする必要があります。 Visual Studio では、Standard C++ の準拠といくつかの重大な変更が増加しました。 最も注意が必要となる可能性が高い変更は、CRT の セキュリティ機能です。 CRT を使用するコードでは、非推奨の警告が生成される可能性があります。 これらの警告は抑制できますが、新しい セキュリティ強化バージョンの CRT 関数に移行することをお勧めします セキュリティが強化され、コードにセキュリティの問題が明らかになる可能性があるためです。
C++ マネージド拡張からのアップグレード
Visual Studio 2005 以降のバージョンでは、Managed Extensions for C++ で記述されたコードは、 /clr
でコンパイルされません。
C コードを C++ に変換する
Visual Studio は C ファイルをコンパイルしますが、 /clr
コンパイルのために C++ に変換する必要があります。 実際のファイル名を変更する必要はありません。 /Tp
( /Tc
、 /Tp
、 /TC
、 /TP
(ソース ファイルの種類の指定) を参照) ) を使用できます。 /clr
には C++ ソース コード ファイルが必要ですが、オブジェクト指向パラダイムを使用するようにコードをリファクタリングする必要はありません。
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++ で使用される識別子 ( virtual
、 new
、 delete
、 bool
、 true
、 false
など) の名前を変更する必要があります。 この変更は、通常、単純な検索と置換の操作で行うことができます。
COMObj1->lpVtbl->Method(COMObj, args); // C code
COMObj2->Method(args); // C++ equivalent
プロジェクト設定を再構成する
Visual Studio でプロジェクトをコンパイルして実行したら、既定の構成を変更するのではなく、 /clr
用の新しいプロジェクト構成を作成する必要があります。 /clr
は、一部のコンパイラ オプションと互換性がありません。 個別の構成を作成すると、プロジェクトをネイティブまたはマネージドとしてビルドできます。 [プロパティ ページ] ダイアログ ボックスで /clr
を選択すると、 /clr
と互換性のないプロジェクト設定は無効になります。 (無効なオプションは、後で選択されていない場合 /clr
自動的に復元されません)。
新しいプロジェクト構成を作成する
[新しいプロジェクト構成] ダイアログ ボックス ([ビルド]>[構成マネージャー]>[アクティブ ソリューション構成]>[新規作成]) の [設定のコピー元] オプションを使用すると、既存のプロジェクト設定に基づいてプロジェクト構成を作成できます。 デバッグ構成用に 1 回、リリース構成用に 1 回、構成のコピーを作成します。 それ以降の変更は、 /clr
固有の構成にのみ適用でき、元のプロジェクト構成はそのまま残ります。
カスタム ビルド規則を使用するプロジェクトには、特別な注意が必要な場合があります。
この手順は、メイクファイルを使用するプロジェクトにとって別の意味があります。 この場合は、別のビルド ターゲットを構成することも、元のビルド ターゲットのコピーから /clr
コンパイルに固有のバージョンを作成することもできます。
プロジェクトの設定を変更する
/clr
は、 /clr (共通言語ランタイム コンパイル)の手順に従って、開発環境で選択できます。 既に説明したように、この手順によって競合するプロジェクト設定は自動的に無効になります。
Note
Visual Studio 2003 からマネージド ライブラリまたは Web サービス プロジェクトをアップグレードすると、 /Zl
コンパイラ オプションが Command Line プロパティ ページに追加されます。 これにより、LNK2001 エラーが発生します。 エラーを解決するには、Command Line プロパティ ページから/Zl
を削除します。 詳細については、「 /Zl
(既定のライブラリ名を省略する)」 および コンパイラとビルドのプロパティの設定を参照してください。
メイクファイルを使用してビルドされたプロジェクトでは、 /clr
が追加されたら、互換性のないコンパイラ オプションを手動で無効にする必要があります。 /clr
と互換性のないコンパイラ オプションについては、/clr
制限を参照してください。
プリコンパイル済みヘッダー
プリコンパイル済みヘッダーは、 /clr
でサポートされています。 ただし、 /clr
(残りをネイティブとしてコンパイル) を使用して CPP ファイルの一部のみをコンパイルする場合は、いくつかの変更が必要です。 /clr
で生成されたプリコンパイル済みヘッダーは、/clr
なしで生成されたプリコンパイル済みヘッダーと互換性がありません。/clr
はメタデータを生成して必要とするためです。 /clr
でコンパイルされたモジュールは、メタデータを含まないプリコンパイル済みヘッダーを使用できません。また、/clr
以外のモジュールでは、メタデータを含むプリコンパイル済みヘッダー ファイルを使用できません。
一部のモジュールが /clr
でコンパイルされるプロジェクトをコンパイルする最も簡単な方法は、プリコンパイル済みヘッダーを完全に無効にすることです。 (プロジェクトの [プロパティ ページ] ダイアログで、 を開きます。C/C++ ノードを選択し、 Precompiled Headers を選択します。次に、 Create/Use Precompiled Headers プロパティを "Not using Precompiled Headers") に変更します。
ただし、特に大規模なプロジェクトでは、プリコンパイル済みヘッダーのコンパイル速度が大幅に向上するため、この機能を無効にすることは望ましくありません。 この場合は、個別のプリコンパイル済みヘッダーを使用するように /clr
ファイルと/clr
以外のファイルを構成することをお勧めします。 1 つの手順で構成できます。ソリューション エクスプローラーを使用して、/clr
でコンパイルするモジュールを複数選択します。 グループを右クリックし、 Properties を選択します。 次に、 Create/Use PCH Through File プロパティと Precompiled Header File プロパティの両方を変更して、それぞれ異なるヘッダー ファイル名と PCH ファイルを使用します。
エラーの修復
/clr
を使用してコードをコンパイルすると、コンパイラ、リンカー、またはランタイム エラーが発生する可能性があります。 このセクションでは、最も一般的な問題について説明します。
メタデータのマージ
データ型のバージョンが異なる場合、2 つの型に対して生成されるメタデータが一致しないため、リンカーのエラーが発生することがあります (型のメンバーを条件付きで定義したが、その型を使用するすべての CPP ファイルで条件が同じでない場合、エラーが発生します)。この場合、リンカーは失敗し、シンボル名と、型が定義された 2 番目の OBJ ファイルの名前のみが報告されます。 OBJ ファイルがリンカーに送信される順序をローテーションして、他のバージョンのデータ型の場所を検出すると便利な場合があります。
ローダー ロックのデッドロック
"ローダー ロックのデッドロック" が発生する可能性がありますが、これは確定的であり、実行時に検出および報告されます。 詳細な背景、ガイダンス、ソリューションについては、「混在アセンブリの初期化」を参照してください。
データのエクスポート
DLL データのエクスポートはエラーが発生しやすく、 /clr
コードでは推奨されません。 これは、DLL の一部のマネージド部分が実行されるまで、DLL のデータ セクションの初期化が保証されないためです。 #using
ディレクティブを使用してメタデータを参照。
型の可視性
ネイティブ型は既定で private
されます。 private
ネイティブ型は DLL の外部には表示されません。 このエラーを解決するには、これらの型に public
を追加します。
浮動小数点と配置の問題
__controlfp
は共通言語ランタイムではサポートされていません。 (詳細については、「 _control87
、 _controlfp
、 __control87_2
」を参照してください)。CLR では、 align
も考慮されません。
COM の初期化
共通言語ランタイムは、モジュールが初期化されるときに COM を自動的に初期化します (COM が自動的に初期化されると、MTA として行われます)。 その結果、明示的に COM を初期化すると、COM が既に初期化されていることを示すリターン コードが生成されます。 COM が CLR によって既にいずれかのスレッド モデルに初期化されている場合、別のスレッド モデルを使用して明示的に COM を初期化しようとすると、アプリケーションが失敗するおそれがあります。
共通言語ランタイムは、既定で COM を MTA として起動します。 /CLRTHREADATTRIBUTE
(CLR スレッド属性の設定) を使用して COM モデルを変更します。
パフォーマンスの問題
MSIL に対して生成されたネイティブ C++ メソッドが (仮想関数呼び出しまたは関数ポインターを使用して) 間接的に呼び出されると、パフォーマンスが低下することがあります。 詳細については、「 Double Thunkingを参照してください。
ネイティブから MSIL に移行すると、ワーキング セットのサイズが大きくなることがわかります。 この増加は、共通言語ランタイムには、プログラムが正しく実行されるようにするための多くの機能が用意されているために発生します。 /clr
アプリケーションが正しく実行されていない場合は、既定でオフの Compiler 警告 (レベル 1 および 3) C4793 を有効にできます。
シャットダウン時にプログラムがクラッシュする
場合によっては、マネージド コードの実行が完了する前に CLR がシャットダウンされる場合があります。 std::set_terminate
とSIGTERM
を使用すると、シャットダウンが発生する可能性があります。 詳細については、「 signal
定数 および set_terminate
」を参照してください。
新しい Visual C++ 機能の使用
アプリケーションがコンパイル、リンク、および実行された後、 /clr
でコンパイルされた任意のモジュールで .NET 機能の使用を開始できます。 詳細については、「 Component Extensions for Runtime Platforms」を参照してください。
Visual C++ での .NET プログラミングの詳細については、次を参照してください。