構造化例外のフィルタ処理(C++)
Win32 の構造化例外(C++) への移動
フィルタ式でフィルタ関数を呼び出すことはできますが、フィルタ関数で GetExceptionInformation を呼び出すことはできません。 GetExceptionInformation の戻り値をパラメータとしてフィルタ関数に渡すことは可能です。
EXCEPTION_POINTERS 情報を例外ハンドラに渡すには、フィルタ式またはフィルタ関数で、GetExceptionInformation から返されたポインタやデータをハンドラが後でアクセスできる場所にコピーしなければなりません。
try-except 文がネストしている場合には、EXCEPTION_EXECUTE_HANDLER または EXCEPTION_CONTINUE_EXECUTION が見つかるまで、各ステートメントのフィルタ式が評価されます。 フィルタ式では GetExceptionInformation を呼び出して例外情報を取得することができます。
__except に渡された式の中で直接 GetExceptionInformation または GetExceptionCode を呼び出している限り、複雑な C++ の式を作成しなくても、例外にどう対処するべきかを関数を使って判断することができます。例外を処理するために必要な情報は、ほとんどすべて、GetExceptionInformation の結果から取り出すことができます。 GetExceptionInformation は、次に示す EXCEPTION_POINTERS 構造体のポインタを返します。
struct EXCEPTION_POINTERS {
EXCEPTION_RECORD *ExceptionRecord;
CONTEXT *Context;
};
EXCEPTION_RECORD には、マシンに依存しない次のような状態が含まれます。
struct EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct EXCEPTION_RECORD *ExceptionRecord;
void *ExceptionAddress;
DWORD NumberParameters;
DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
};
通常、フィルタ関数は ExceptionRecord の情報を見て、どう応答するかを判断します。 さらに具体的な情報が必要な場合もあります(特に、行わなければならないアクションが EXCEPTION_CONTINUE_EXECUTION の場合。何も行われない場合には、例外の原因となったコードが再実行されます)。この場合、EXCEPTION_POINTERS 構造体のもう 1 つのフィールドから、例外発生時のプロセッサの状態がわかります。 この構造体が変更されていて、フィルタから EXCEPTION_CONTINUE_EXCEPTION が返される場合には、これを使ってスレッドの状態を設定し、それから実行を継続することができます。 以下に例を示します。
static int xfilter(EXCEPTION_POINTERS *xp) {
int rc;
EXCEPTION_RECORD *xr = xp->ExceptionRecord;
CONTEXT *xc = xp->Context;
switch (xr->ExceptionCode) {
case EXCEPTION_BREAKPOINT:
// 誰かが組み込みブレークポイントを残していた
// 1 つずつ進める(x86 上では 1 バイト)
++xc->Eip;
rc = EXCEPTION_CONTINUE_EXECUTION;
break;
case EXCEPTION_ACCESS_VIOLATION:
rc = EXCEPTION_EXECUTE_HANDLER;
break;
default:
// あきらめる
rc = EXCEPTION_CONTINUE_SEARCH;
break;
};
return rc;
}
// ...
EXCEPTION_POINTERS * xp;
try {
func();
}
__except (xfilter(xp = GetExceptionInformation())) {
abort();
}