マネージド例外の使用についての基本概念
このトピックでは、マネージド アプリケーションにおける例外処理について説明します。 つまり、/clr コンパイラ オプションを使用してコンパイルされたアプリケーションです。
このトピックの内容
解説
/clr オプションを使用してコンパイルする場合は、CLR 例外を処理できます。標準の Exception クラスには CLR 例外を処理するための便利なメソッドが多数用意されており、このクラスはユーザー定義の例外クラスの基底クラスとしても推奨されます。
インターフェイスから派生した例外の種類のキャッチは、/clr ではサポートされていません。 また、共通言語ランタイムでは、スタック オーバーフローの例外のキャッチは許可されていません。スタック オーバーフローの例外が発生すると、プロセスが終了します。
マネージド アプリケーションとアンマネージド アプリケーションにおける例外処理の違いの詳細については、Managed Extensions for C++ における例外処理動作の相違点に関するページを参照してください。
/clr での例外のスロー
C++ のスロー式は、CLR 型へのハンドルをスローするために拡張されます。 次の例では、カスタムの例外の種類を作成して、その種類のインスタンスをスローします。
// clr_exception_handling.cpp
// compile with: /clr /c
ref struct MyStruct: public System::Exception {
public:
int i;
};
void GlobalFunction() {
MyStruct^ pMyStruct = gcnew MyStruct;
throw pMyStruct;
}
値の型は、スローされる前にボックス化する必要があります。
// clr_exception_handling_2.cpp
// compile with: /clr /c
value struct MyValueStruct {
int i;
};
void GlobalFunction() {
MyValueStruct v = {11};
throw (MyValueStruct ^)v;
}
CLR 拡張機能の try/catch ブロック
CLR 例外とネイティブの例外の両方をキャッチするために、同じ try
/catch
ブロック構造を使用できます。
// clr_exception_handling_3.cpp
// compile with: /clr
using namespace System;
ref struct MyStruct : public Exception {
public:
int i;
};
struct CMyClass {
public:
double d;
};
void GlobalFunction() {
MyStruct^ pMyStruct = gcnew MyStruct;
pMyStruct->i = 11;
throw pMyStruct;
}
void GlobalFunction2() {
CMyClass c = {2.0};
throw c;
}
int main() {
for ( int i = 1; i >= 0; --i ) {
try {
if ( i == 1 )
GlobalFunction2();
if ( i == 0 )
GlobalFunction();
}
catch ( CMyClass& catchC ) {
Console::WriteLine( "In 'catch(CMyClass& catchC)'" );
Console::WriteLine( catchC.d );
}
catch ( MyStruct^ catchException ) {
Console::WriteLine( "In 'catch(MyStruct^ catchException)'" );
Console::WriteLine( catchException->i );
}
}
}
出力
In 'catch(CMyClass& catchC)'
2
In 'catch(MyStruct^ catchException)'
11
C++ オブジェクトのアンワインドの順序
アンワインドは、スロー元の関数と処理関数の間のランタイム スタック上に存在する可能性のある、デストラクターを持つ C++ オブジェクトに対して発生します。 CLR 型はヒープで割り当てられるため、アンワインドは適用されません。
スローされる例外のイベントの順序は次のとおりです。
ランタイムがスタックで適切な catch 句を検索します。また、SEH の場合は、例外をキャッチするために SEH の例外フィルターを検索します。 catch 句は最初に辞書式順序で検索され、続いて呼び出し履歴が動的に検索されます。
正しいハンドラーが見つかると、スタックがそのポイントまでアンワインドされます。 スタック上の関数呼び出しごとに、そのローカル オブジェクトが破棄され、最も入れ子になった部分から外側に向かって __finally ブロックが実行されます。
スタックがアンワインドされると、catch 句が実行されます。
アンマネージド型のキャッチ
スローされたアンマネージド オブジェクト型は SEHException 型の例外でラップされます。 適切な catch
句を検索する場合は、次に示す 2 つの可能性があります。
ネイティブの C++ 型が検出された場合は、例外がラップ解除されて、検出された型と比較されます。 この比較により、ネイティブの C++ 型を通常の方法でキャッチできます。
ただし、SEHException 型の
catch
句、またはそのいずれかの基底クラスを最初に調べた場合、その句は例外をインターセプトします。 そのため、ネイティブの C++ 型をキャッチする catch 句はすべて、CLR 型の catch 句の前に配置する必要があります。
次の点に注意してください。
catch(Object^)
and
catch(...)
これらはどちらも、SEH 例外を含む、スローされた型をキャッチします。
アンマネージド型が catch(Object^) によってキャッチされた場合は、スローされたオブジェクトが破棄されます。
アンマネージド例外をスローまたはキャッチする場合は、/EHs または /EHa の代わりに /EHsc コンパイラ オプションを使用することをお勧めします。