【问题标题】:Why SetUnhandledExceptionFilter cannot capture some exception but AddVectoredExceptionHandler can do为什么 SetUnhandledExceptionFilter 无法捕获某些异常但 AddVectoredExceptionHandler 可以
【发布时间】:2013-11-08 12:47:12
【问题描述】:

我遇到了一个问题,当异常代码 c0000374 引发时,我传递给 SetUnhandledExceptionFilter 的函数没有被调用。但它适用于异常代码 c0000005。 然后我尝试改用AddVectoredExceptionHandler,它没有问题,处理函数被正确调用。

是 API 错误吗?我可以到处使用 AddVectoredExceptionHandler 而不是 SetUnhandledExceptionFilter 吗?

这两个函数都可以正常工作

// Exception code c0000005
int* p1 = NULL;
*p1 = 99;

只有 AddVectoredExceptionHandler 可以捕获此异常。 (为了证明它不依赖于运行时库,我手动引发了异常,结果是一样的。)

// Exception code c0000374
RaiseException(0xc0000374, 0, 0, NULL);

测试程序。

#include <tchar.h>
#include <fstream>
#include <Windows.h>

LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
{
    std::ofstream f;
    f.open("VectoredExceptionHandler.txt", std::ios::out | std::ios::trunc);
    f << std::hex << pExceptionInfo->ExceptionRecord->ExceptionCode << std::endl;
    f.close();

    return EXCEPTION_CONTINUE_SEARCH;
}

LONG WINAPI TopLevelExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
{
    std::ofstream f;
    f.open("TopLevelExceptionHandler.txt", std::ios::out | std::ios::trunc);
    f << std::hex << pExceptionInfo->ExceptionRecord->ExceptionCode << std::endl;
    f.close();

    return EXCEPTION_CONTINUE_SEARCH;
}


int _tmain(int argc, _TCHAR* argv[])
{
    AddVectoredExceptionHandler(1, VectoredExceptionHandler);
    SetUnhandledExceptionFilter(TopLevelExceptionHandler);

    // Exception code c0000374
    RaiseException(0xc0000374, 0, 0, NULL);     

    // Exception code c0000005
    // int* p1 = NULL;
    // *p1 = 99;        


    return 0;
}

【问题讨论】:

  • 这些名称表明不同之处在于 SetUnhandledExceptionFilter 仅在未处理的异常时被调用。您是否考虑过 C++ 运行时库在内部处理其他异常类型的可能性?
  • @hvd 我认为这是可能的。我会想办法验证的。

标签: c++ windows winapi exception-handling


【解决方案1】:

这是因为 MSVC CRT 启动中的这段代码:

    /*
     * Enable app termination when heap corruption is detected on
     * Windows Vista and above. This is a no-op on down-level OS's
     * and enabled by default for 64-bit processes.
     */

    if (!_NoHeapEnableTerminationOnCorruption)
    {
        HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
    }

如果您想禁用它(不推荐),请将nohetoc.obj 链接到您的程序。

【讨论】:

  • 请注意,现在似乎无法禁用此功能,并且 Visual Studio 2019 未附带 nohetoc.obj。
【解决方案2】:

该异常实际上是直接在其源中捕获的,在RtlReportCriticalFailure 中,一旦检测到堆损坏,堆管理器就会调用该异常。在此函数中注册的 SEH 处理程序调用RtlReportException,紧随其后的是NtTerminateProcess。

我只能得出结论,SEH 处理程序是故意避免使用的——由于堆损坏,堆栈内容(以及因此 SEH 注册)也值得怀疑;并且应用程序无论如何都无法从堆损坏中合理地恢复。

【讨论】:

  • 应用程序可能无法从堆损坏中恢复。但是,出于诊断目的,仍然编写小型转储非常有价值。此外,64 位构建无论如何都使用基于表的异常处理(相对于基于帧),因此损坏的堆和/或堆栈不会影响异常处理。我也没有遵循您的推理:您已将代码跟踪到异常处理程序,只是得出结论“故意避免使用 SEH 处理程序”。这不太合理。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-10
  • 1970-01-01
  • 1970-01-01
  • 2010-10-09
  • 1970-01-01
相关资源
最近更新 更多