为什么SetUnhandledExceptionFilter无法捕获某些异常,而AddVectoredExceptionHandler可以?

29
我遇到了一个问题,当异常代码为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;
}

5
这些名称表明SetUnhandledExceptionFilter只会在未处理异常时被调用。你是否考虑过C++运行库内部处理其他类型的异常的可能性? - user743382
@hvd 我认为这是可能的。我会找到一种验证的方法。 - wrongite
2个回答

19

这是由于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链接到你的程序中。


2
请注意,现在似乎无法禁用此功能,并且Visual Studio 2019未附带nohetoc.obj - Jonathan Potter

5

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

我只能得出结论,故意避免使用SEH处理程序--当堆被破坏时,堆栈内容(因此还有SEH注册)也是可疑的;而且应用程序无法合理地从堆破坏中恢复。


1
一个应用程序可能无法从堆损坏中恢复。然而,出于诊断目的,仍然编写minidump非常有价值。此外,64位构建使用基于表的异常处理(而不是基于帧的),因此损坏的堆和/或堆栈不会影响异常处理。我也不明白你的推理:你一直跟踪代码到异常处理程序,只是为了得出“故意避免使用SEH处理程序”的结论。这并不太合理。 - IInspectable

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接