構造化例外処理 (C/C++)

構造化例外処理 (Standard Edition H) は、ハードウェア 障害などの特定の例外的なコード状況を適切に処理するための C および C++ の Microsoft 拡張機能です。 Windows および Microsoft C++ では Standard Edition H がサポートされていますが、C++ コードでは ISO 標準の C++ 例外処理を使用することをお勧めします。 これにより、コードの移植性と柔軟性が向上します。 ただし、既存のコードや特定の種類のプログラムを維持するために、SEH を使用する必要がある場合もあります。

Microsoft 固有の仕様:

構文

try-except-statement :
__try compound-statement __except ( filter-expression ) compound-statement

try-finally-statement :
__try compound-statement __finally compound-statement

解説

SEH を使うと、実行が予期せずに終了した場合にメモリ ブロックやファイルなどのリソースが適切にリリースされることを確認できます。 また、メモリ不足などの特定の問題を、goto ステートメントやリターン コードの複雑なテストに依存しない簡潔な構造化されたコードを使用して処理することもできます。

try-exceptこの記事で参照されているステートメントはtry-finally、C および C++ 言語に対する Microsoft の拡張機能です。 これらは、イベント後にプログラムの制御をアプリケーションが取得するようにし、そうでない場合は実行を終了させることによって SEH をサポートします。 SEH は C++ ソース ファイルと連携しますが、C++ 向けに設計されていません。 または/EHscオプションを使用してコンパイルする C++ プログラムで Standard Edition H を使用/EHaする場合、ローカル オブジェクトのデストラクターが呼び出されますが、他の実行動作が予期した動作ではない可能性があります。 概要については、この記事で後述する例を参照してください。 ほとんどの場合、Standard Edition H ではなく、ISO 標準の C++ 例外処理を使用することをお勧めします。 C++ 例外処理を使用すると、コードの移植性が高くなり、すべての種類の例外を処理できるようになります。

SEH を使用する C コードがある場合は、C++ 例外処理を使用する C++ コードと混在できます。 詳細については、「C++ での構造化例外の処理」を参照してください。

SEH メカニズムには次の 2 つがあります。

  • 値に基づいてfilter-expression例外に応答または無視できる例外ハンドラーまたは__exceptブロック。 詳細については、ステートメントを参照してください。try-except

  • 例外によって終了が発生するかどうかに関係なく、常に呼び出される終了ハンドラー、または __finally ブロック。 詳細については、ステートメントを参照してください。try-finally

これら 2 種類のハンドラーは区別されますが、"スタックのアンワインド" というプロセスを通じて密接に関係しています。 構造化例外が発生すると、Windows は現在アクティブである最も新しくインストールされた例外ハンドラーを検索します。 ハンドラーは、次の 3 つのうちの 1 つを行うことができます。

  • 例外を認識し、他のハンドラー (EXCEPTION_CONTINUE_SEARCH) に制御を渡すことができませんでした。

  • 例外を認識しますが、無視します (EXCEPTION_CONTINUE_EXECUTION)。

  • 例外を認識し、それを処理します (EXCEPTION_EXECUTE_HANDLER)。

例外を認識する例外ハンドラーは、例外が発生したときに実行中だった関数内にない場合があります。 スタックのかなり上位の関数内にあります。 現在実行中の関数と、スタック フレーム上の他のすべての関数が終了します。 このプロセス中、スタックはアンラウンドされます。 つまり、終了した関数のローカルの非静的変数はスタックからクリアされます。

これがスタックをアンワインドするため、オペレーティング システムは、各関数に書き込んだ終了ハンドラーを呼び出します。 終了ハンドラーを使用して、異常終了のために開いたままになっているリソースをクリーンアップします。 クリティカル セクションに入った場合、終了ハンドラーで終了できます。 プログラムのシャットダウン時に、一時ファイルを閉じたり削除するなどの他のハウスキーピング タスクを実行できます。

次のステップ

前に説明したように、C++ プログラムで SEH を使用し、/EHa または /EHsc オプションを使用してコンパイルすると、ローカル オブジェクトのデストラクターが呼び出されます。 ただし、C++ 例外も使用している場合は、実行時の動作は予期したとおりにならない可能性があります。 この例は、これらの動作の違いを示しています。

#include <stdio.h>
#include <Windows.h>
#include <exception>

class TestClass
{
public:
    ~TestClass()
    {
        printf("Destroying TestClass!\n");
    }
};

__declspec(noinline) void TestCPPEX()
{
#ifdef CPPEX
    printf("Throwing C++ exception\n");
    throw std::exception("");
#else
    printf("Triggering SEH exception\n");
    volatile int *pInt = 0x00000000;
    *pInt = 20;
#endif
}

__declspec(noinline) void TestExceptions()
{
    TestClass d;
    TestCPPEX();
}

int main()
{
    __try
    {
        TestExceptions();
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        printf("Executing SEH __except block\n");
    }

    return 0;
}

/EHsc を使用してこのコードをコンパイルするが、ローカル テスト コントロール マクロ CPPEX が未定義の場合、TestClass デストラクターは実行されません。 出力は次のようになります。

Triggering SEH exception
Executing SEH __except block

/EHsc を使用してコードをコンパイルし、/DCPPEX を使用して CPPEX が定義されている場合 (C++ 例外がスローされる)、TestClass デストラクターが実行され、出力は次のようになります。

Throwing C++ exception
Destroying TestClass!
Executing SEH __except block

/EHa を使用してコードをコンパイルした場合、TestClass デストラクターは、標準 C++ throw 式を使用して例外がスローされたか、SEH を使用してスローされたかに関わらず実行されます。 つまり、CPPEX が定義されているかどうかを示します。 出力は次のようになります。

Throwing C++ exception
Destroying TestClass!
Executing SEH __except block

詳細については、「/EH (例外処理モデル)」を参照してください。

Microsoft 固有の仕様の終了

関連項目

例外処理
キーワード
<exception>
エラーと例外の処理
構造化例外処理 (Windows)