Share via


_resetstkoflw

更新 : 2007 年 11 月

スタック オーバーフローが発生した状態からプログラムを回復させます。

int _resetstkoflw ( void );

戻り値

正常終了した場合は 0 以外を返します。それ以外の場合は 0 を返します。

解説

_resetstkoflw 関数は、スタック オーバーフローが発生した状態からプログラムを回復させ、実行中の操作を致命的な例外エラーが発生したとして失敗させるのではなく、続行できるようにします。_resetstkoflw 関数を呼び出さなければ、前回の例外の後にガード ページは作成されません。次にスタック オーバーフローが発生したときに例外は発生しないので、プロセスは警告なしで終了します。

アプリケーションのスレッドによって EXCEPTION_STACK_OVERFLOW 例外が発生すると、スレッドのスタックは破損した状態で残されます。これは、スタックが破損しない EXCEPTION_ACCESS_VIOLATIONEXCEPTION_INT_DIVIDE_BY_ZERO などのその他の例外とは異なります。プログラムが最初にロードされるとき、スタックは小さな値に設定されます。スタックは、スレッドの要求に応じて拡張されます。これは、現在のスタックの最後に PAGE_GUARD アクセスのページを配置することによって実装します。詳細については、「ガード ページの作成」を参照してください。

スタック ポインタがこのページのアドレスを指すと例外が発生し、システムは次の 3 つの処理を実行します。

  • ガード ページの PAGE_GUARD の保護を解除し、スレッドがメモリでデータを読み書きできるようにします。

  • このページの後に新しいガード ページを割り当てます。

  • 例外を発生させた命令を返します。

このようにして、システムはスレッドのスタックのサイズを自動的に増やすことができます。プロセスの各スレッドには、最大スタック サイズがあります。スタック サイズはコンパイル時に /STACK (スタック割り当て) によって設定するか、またはプロジェクトの .def ファイルの STACKSIZE ステートメントで設定します。

この最大スタック サイズを超えると、システムは次の 3 つの処理を実行します。

  • 前に説明したように、ガード ページの PAGE_GUARD 保護を解除します。

  • このページの後に新しいガード ページの割り当てを試みます。しかし、最大スタック サイズを超えているので、これは失敗します。

  • スレッドがこれを例外ブロックで処理できるように例外を発生します。

スタックは、この時点でガード ページを持っていないことに注意してください。次にプログラムがガード ページがあるはずの終端までスタックを拡張すると、スタックの終端を超えて書き込むことになるので、アクセス違反が発生します。

スタック オーバーフローの例外が発生した後に回復処理が実行されるたびに _resetstkoflw を呼び出してガード ページを回復します。この関数は、__except ブロックの本体または __except ブロックの外部から呼び出すことができます。ただし、この関数を使用する時期には制限があります。_resetstkoflw は次の項目から呼び出さないでください。

  • フィルタ式。

  • フィルタ関数。

  • フィルタ関数から呼び出される関数。

  • catch ブロック。

  • __finally ブロック。

これらの場所では、スタックが十分にアンワインドされていません。

スタック オーバーフローの例外は C++ 例外ではなく構造化例外として生成され、_resetstkoflw は通常の catch ブロックでスタック オーバーフロー例外をキャッチできないので、これを使用することはできません。ただし、2 番目の例にあるように C++ 例外をスローする構造化例外変換関数を実装する _set_se_translator を使用すると、スタック オーバーフロー例外は C++ キャッチ ブロックで処理できる C++ 例外になります。

構造化例外変換関数によってスローされる例外から到達できる C++ キャッチ ブロックで _resetstkoflw を呼び出すのは安全ではありません。この場合、破壊可能なオブジェクトに対してキャッチ ブロックの前にデストラクタが呼び出されている場合にも、スタック スペースは解放されず、スタック ポインタはキャッチ ブロックを出るまでリセットされません。この関数は、スタック スペースが解放され、スタック ポインタがリセットされるまで呼び出さないでください。したがって、この関数は必ずキャッチ ブロックを出てから呼び出します。キャッチ ブロックでのオーバーフローは、同じキャッチ ブロックによって処理される例外を生成するので、キャッチ ブロックで発生した前のスタック オーバーフローから回復する際に発生したスタック オーバーフローが回復不能になり、プログラムの応答不能の原因になることがあります。したがって、キャッチ ブロックでは使用するスタック スペースをできるだけ小さくする必要があります。

_resetstkoflw は、__except ブロック内などの正しい場所で使用した場合にも失敗することがあります。スタックのアンワインド後であっても、最後のページに書き込む前に _resetstkoflw を実行するために十分なスタック スペースがなくなった場合、_resetstkoflw はスタックの最後のページをガード ページとして再設定できずに、失敗を示す 0 を返します。したがって、この関数を安全に使用するには、スタックが安全に使用できることを前提にせずに、戻り値のチェックを含める必要があります。

/clr または /clr:pure スイッチを使用してコンパイルしたアプリケーションの構造化例外処理は STATUS_STACK_OVERFLOW 例外をキャッチできません (/clr (共通言語ランタイムのコンパイル) を参照)。

必要条件

ルーチン

必須ヘッダー

_resetstkoflw

<malloc.h>

互換性の詳細については、「C ランタイム ライブラリ」の「互換性」を参照してください。

ライブラリ :C ランタイム ライブラリ のすべてのバージョン。

使用例

_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 is not 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.

.NET Framework の相当するアイテム

適用できません。標準 C 関数を呼び出すには、PInvoke を使用します。詳細については、「プラットフォーム呼び出しの例」を参照してください。

参照

参照

_alloca