マネージド例外の使用についての基本概念

このトピックでは、マネージド アプリケーションにおける例外処理について説明します。 つまり、/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 型はヒープで割り当てられるため、アンワインドは適用されません。

スローされる例外のイベントの順序は次のとおりです。

  1. ランタイムがスタックで適切な catch 句を検索します。また、SEH の場合は、例外をキャッチするために SEH の例外フィルターを検索します。 catch 句は最初に辞書式順序で検索され、続いて呼び出し履歴が動的に検索されます。

  2. 正しいハンドラーが見つかると、スタックがそのポイントまでアンワインドされます。 スタック上の関数呼び出しごとに、そのローカル オブジェクトが破棄され、最も入れ子になった部分から外側に向かって __finally ブロックが実行されます。

  3. スタックがアンワインドされると、catch 句が実行されます。

アンマネージド型のキャッチ

スローされたアンマネージド オブジェクト型は SEHException 型の例外でラップされます。 適切な catch 句を検索する場合は、次に示す 2 つの可能性があります。

  • ネイティブの C++ 型が検出された場合は、例外がラップ解除されて、検出された型と比較されます。 この比較により、ネイティブの C++ 型を通常の方法でキャッチできます。

  • ただし、SEHException 型の catch 句、またはそのいずれかの基底クラスを最初に調べた場合、その句は例外をインターセプトします。 そのため、ネイティブの C++ 型をキャッチする catch 句はすべて、CLR 型の catch 句の前に配置する必要があります。

次の点に注意してください。

catch(Object^)

and

catch(...)

これらはどちらも、SEH 例外を含む、スローされた型をキャッチします。

アンマネージド型が catch(Object^) によってキャッチされた場合は、スローされたオブジェクトが破棄されます。

アンマネージド例外をスローまたはキャッチする場合は、/EHs または /EHa の代わりに /EHsc コンパイラ オプションを使用することをお勧めします。

関連項目

例外処理
safe_cast
例外処理