_resetstkoflw
スタック オーバーフローから復元します。
重要
この API は、Windows ランタイムで実行するアプリケーションでは使用できません。 詳細については、「ユニバーサル Windows プラットフォーム アプリでサポートされていない CRT 関数」を参照してください。
構文
int _resetstkoflw( void );
戻り値
関数が成功した場合は 0 以外、失敗した場合は 0。
解説
_resetstkoflw
関数は、スタック オーバーフローが発生した状態からプログラムを回復させ、実行中の操作を致命的な例外エラーが発生したとして失敗させるのではなく、続行できるようにします。 _resetstkoflw
関数が呼び出されないと、前回の例外の後にガード ページは作成されません。 次にスタック オーバーフローが発生したときに例外は発生しないので、プロセスは警告なしで終了します。
アプリケーションのスレッドにより EXCEPTION_STACK_OVERFLOW
の例外が発生した場合、そのスレッドによってスタックは破損したままの状態になります。 この例外は、スタックが破損していない EXCEPTION_ACCESS_VIOLATION
や EXCEPTION_INT_DIVIDE_BY_ZERO
などの他の例外とは異なります。 プログラムが最初に読み込まれたときには、スタックには小さい値が設定されます。 その後、スタックはスレッドのニーズに対応して必要に応じて大きくなります。 オンデマンド拡張は、現在のスタックの末尾に PAGE_GUARD
アクセス権を持つページを配置することによって実装されます。 詳細については、「 Creating guard pages」を参照してください。
コードによってスタック ポインターがこのページのアドレスをポイントすると、例外が発生し、システムは次の 3 つのことを実行します。
スレッドがメモリにデータを読み書きできるように、ガード ページの
PAGE_GUARD
保護を削除します。最後のページの下のページにある新しいガード ページを割り当てます。
例外を発生させた命令を再実行します。
この方法では、システムがスレッドに対するスタック サイズを自動的にインクリメントできます。 プロセスの各スレッドには最大スタック サイズがあります。 スタック サイズは、コンパイル時に /STACK
(スタック割り当て) オプション、またはプロジェクトの.def
ファイル内の STACKSIZE
ステートメントによって設定されます。
この最大スタック サイズを超えると、システムは次の 3 つのことを実行します。
前述のようにガード ページの PAGE_GUARD の保護を削除します。
最後のページの下に新しいガード ページの割り当てを試みます。 ただし、最大スタック サイズを超えたため、割り当てが失敗します。
例外を発生させて、スレッドが例外ブロックで処理できるようにします。
その時点ではスタックにガード ページはありません。 プログラムが次にスタックの末尾を超えて書き込む場所にスタックを拡張すると、アクセス違反が発生します。
_resetstkoflw
を呼び出して、スタック オーバーフロー例外の後で回復が行われるたびにガード ページを復元します。 この関数は、__except
ブロックのメイン部分の内側から、または __except
ブロックの外側から呼び出すことができます。 ただし、使用する必要がある場合には、いくつかの制限があります。 _resetstkoflw
は、次の場所から呼び出す必要があります。
フィルター式。
フィルター関数。
フィルター関数から呼び出される関数。
A
catch
ブロック。A
__finally
ブロック。
これらの時点では、スタックは完全にはアンワインドされていません。
スタック オーバーフロー例外は構造化例外として (C++ 例外ではない) 生成されます。そのため、_resetstkoflw
が通常の catch
ブロックで役に立たなくなります。これはスタック オーバーフロー例外をキャッチできないためです。 ただし、_set_se_translator
を使用して C++ 例外をスローする構造化例外の変換を実装すると (2 番目の例のように)、スタック オーバーフロー例外によって C ++ 例外が発生します。これは C++ の catch ブロックで処理できます。
構造化例外変換関数によってスローされる例外から到達した C ++ catch ブロックでの _resetstkoflw
の呼び出しは安全ではありません。 この場合、catch ブロックの前で消滅させられるオブジェクトに対してデストラクターが呼び出された場合でも、catch ブロックの外部に出るまでスタック領域は解放されず、スタック ポインターはリセットされません。 この関数は、スタック領域が解放され、スタック ポインターがリセットされるまで呼び出さないようにする必要があります。 したがって、catch ブロックが終了した後でのみ呼び出すようにします。 できるだけ少ないスタック領域を catch ブロックで使用する必要があります。 catch ブロック内で発生したスタック オーバーフロー自体が以前のスタック オーバーフローから復旧しようとしている場合は、復旧できません。 catch ブロック内のオーバーフローによって、同じ catch ブロックによってそれ自体が処理される例外がトリガーされるため、プログラムが応答を停止する可能性があります。
_resetstkoflw
が __except
ブロック内などの適切な位置で使用されていても、エラーになる場合があります。 スタックをアンワインドした後でも、スタックの最後のページに書き込まずに _resetstkoflw
を実行するのに十分なスタック領域が残っていない可能性があります。 その後、 _resetstkoflw
はスタックの最後のページをガード ページとしてリセットできず、エラーを示す 0 を返します。 この関数を安全に使用するには、スタックを使用しても安全であると想定するのではなく、戻り値をチェックする必要があります。
アプリケーションが /clr
をしてコンパイルされている場合、構造化例外処理では STATUS_STACK_OVERFLOW
例外をキャッチしません (「/clr
(共通言語ランタイムのコンパイル)」を参照)。
既定では、この関数のグローバル状態の適用対象は、アプリケーションになります。 この動作を変更するには、「CRT でのグローバル状態」を参照してください。
要件
ルーチンによって返される値 | 必須ヘッダー |
---|---|
_resetstkoflw |
<malloc.h> |
互換性の詳細については、「 Compatibility」を参照してください。
Libraries: CRT ライブラリ機能のすべてのバージョン。
例
_resetstkoflw
関数の推奨する使用例を次に示します。
// crt_resetstkoflw.c
// Launch program with and without arguments to observe
// the difference made by calling _resetstkoflw.
#include <malloc.h>
#include <stdio.h>
#include <windows.h>
void recursive(int recurse)
{
_alloca(2000);
if (recurse)
recursive(recurse);
}
// Filter for the stack overflow exception.
// This function traps the stack overflow exception, but passes
// all other exceptions through.
int stack_overflow_exception_filter(int exception_code)
{
if (exception_code == EXCEPTION_STACK_OVERFLOW)
{
// Do not call _resetstkoflw here, because
// at this point, the stack isn't yet unwound.
// Instead, signal that the handler (the __except block)
// is to be executed.
return EXCEPTION_EXECUTE_HANDLER;
}
else
return EXCEPTION_CONTINUE_SEARCH;
}
int main(int ac)
{
int i = 0;
int recurse = 1, result = 0;
for (i = 0 ; i < 10 ; i++)
{
printf("loop #%d\n", i + 1);
__try
{
recursive(recurse);
}
__except(stack_overflow_exception_filter(GetExceptionCode()))
{
// Here, it is safe to reset the stack.
if (ac >= 2)
{
puts("resetting stack overflow");
result = _resetstkoflw();
}
}
// Terminate if _resetstkoflw failed (returned 0)
if (!result)
return 3;
}
return 0;
}
プログラム引数がないサンプル出力:
loop #1
プログラムはこれ以上のイテレーションを実行せずに応答を停止します。
プログラムの引数があります:
loop #1
resetting stack overflow
loop #2
resetting stack overflow
loop #3
resetting stack overflow
loop #4
resetting stack overflow
loop #5
resetting stack overflow
loop #6
resetting stack overflow
loop #7
resetting stack overflow
loop #8
resetting stack overflow
loop #9
resetting stack overflow
loop #10
resetting stack overflow
説明
構造化例外が C++ 例外に変換されるプログラムにおける _resetstkoflw
の推奨される使用法を次に示します。
コード
// crt_resetstkoflw2.cpp
// compile with: /EHa
// _set_se_translator requires the use of /EHa
#include <malloc.h>
#include <stdio.h>
#include <windows.h>
#include <eh.h>
class Exception { };
class StackOverflowException : Exception { };
// Because the overflow is deliberate, disable the warning that
// this function will cause a stack overflow.
#pragma warning (disable: 4717)
void CauseStackOverflow (int i)
{
// Overflow the stack by allocating a large stack-based array
// in a recursive function.
int a[10000];
printf("%d ", i);
CauseStackOverflow (i + 1);
}
void __cdecl SEHTranslator (unsigned int code, _EXCEPTION_POINTERS*)
{
// For stack overflow exceptions, throw our own C++
// exception object.
// For all other exceptions, throw a generic exception object.
// Use minimal stack space in this function.
// Do not call _resetstkoflw in this function.
if (code == EXCEPTION_STACK_OVERFLOW)
throw StackOverflowException ( );
else
throw Exception( );
}
int main ( )
{
bool stack_reset = false;
bool result = false;
// Set up a function to handle all structured exceptions,
// including stack overflow exceptions.
_set_se_translator (SEHTranslator);
try
{
CauseStackOverflow (0);
}
catch (StackOverflowException except)
{
// Use minimal stack space here.
// Do not call _resetstkoflw here.
printf("\nStack overflow!\n");
stack_reset = true;
}
catch (Exception except)
{
// Do not call _resetstkoflw here.
printf("\nUnknown Exception!\n");
}
if (stack_reset)
{
result = _resetstkoflw();
// If stack reset failed, terminate the application.
if (result == 0)
exit(1);
}
void* pv = _alloca(100000);
printf("Recovered from stack overflow and allocated 100,000 bytes"
" using _alloca.");
return 0;
}
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
Stack overflow!
Recovered from stack overflow and allocated 100,000 bytes using _alloca.