如何在 Windows x64 C++ 应用程序中捕获堆栈溢出

How to trap stack overflow in a Windows x64 C++ application

我正在尝试将应用程序编译到 Windows 中的 x64 平台架构。几个处理脚本语言解析的线程使用此代码 recommended by Microsoft to trap stack overflows and avoid access violation exceptions:

__try
{
    DoSomethingThatMightUseALotOfStackMemory();
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
    LPBYTE lpPage;
    static SYSTEM_INFO si;
    static MEMORY_BASIC_INFORMATION mi;
    static DWORD dwOldProtect;

    // Get page size of system
    GetSystemInfo(&si);

    // Find SP address
    _asm mov lpPage, esp;

    // Get allocation base of stack
    VirtualQuery(lpPage, &mi, sizeof(mi));

    // Go to page beyond current page
    lpPage = (LPBYTE)(mi.BaseAddress)-si.dwPageSize;

    // Free portion of stack just abandoned
    if (!VirtualFree(mi.AllocationBase,
                    (LPBYTE)lpPage - (LPBYTE)mi.AllocationBase,
                     MEM_DECOMMIT))
    {
        exit(1);
    }

    // Reintroduce the guard page
    if (!VirtualProtect(lpPage, si.dwPageSize, 
                        PAGE_GUARD | PAGE_READWRITE, 
                        &dwOldProtect))
    {
        exit(1);
    }
    Sleep(2000);
}

不幸的是,它使用一行内联汇编程序来获取堆栈指针。 Visual Studio 不支持 x64 模式的内联汇编,我也找不到 compiler intrinsic 来获取堆栈指针。

是否可以以 x64 友好的方式执行此操作?

正如问题评论中所指出的,上面的整个 "hack" 可以用 _resetstkoflw 函数代替。这在 x86 和 x64 模式下都能正常工作。

上面的代码片段变成:

// 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;
}

void example()
{
    int result = 0;
    __try
    {
        DoSomethingThatMightUseALotOfStackMemory();
    }
    __except(stack_overflow_exception_filter(GetExceptionCode()))
    {
        // Here, it is safe to reset the stack.
        result = _resetstkoflw();
    }

    // Terminate if _resetstkoflw failed (returned 0)
    if (!result)
        return 3;

    return 0;
}