65.9K
CodeProject 正在变化。 阅读更多。
Home

SetUnhandledExceptionFilter 与 C/C++ 运行时库

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.79/5 (26投票s)

2011 年 2 月 7 日

Zlib

2分钟阅读

viewsIcon

147778

downloadIcon

3391

本文介绍了一种修复方法,使 SetUnhandledExceptionFilter 能够与 CRT 协同工作。

引言

Windows 提供了一种方法,允许应用程序通过 SetUnhandledExceptionFilter 函数覆盖默认的应用程序“崩溃”处理功能。

通常,SetUnhandledExceptionFilter 函数与崩溃报告活动结合使用。能够精确定位导致崩溃的代码行,对于事后调试来说是无价的。

事后调试已经在 CodeProject 上的其他文章中讨论过,不属于本文的范围。

下面是一个简单的未处理异常过滤器(它只在控制台中显示 “Gotcha!”)的示例:

bool g_showCrashDialog = false;

LONG WINAPI OurCrashHandler(EXCEPTION_POINTERS * /*ExceptionInfo*/)
{
    std::cout << "Gotcha!" << std::endl;

    return g_showCrashDialog ? EXCEPTION_CONTINUE_SEARCH : EXCEPTION_EXECUTE_HANDLER;
}

如果崩溃处理函数返回 EXCEPTION_EXECUTE_HANDLER,操作系统将显示默认的崩溃对话框,或者如果系统上安装了即时 (JIT) 调试器,则调用 JIT 调试器。

为了测试代码,我们将模拟一个空指针无效访问,如下所示:

int main()
{
    ::SetUnhandledExceptionFilter(OurCrashHandler);

    std::cout << "Normal null pointer crash" << std::endl;

    char *p = 0;
    *p = 5;
}

程序应该显示:

Normal null pointer crash
Gotcha!

C/C++ 运行时库

在某些情况下,C/C++ 运行时库会移除任何自定义崩溃处理程序,并且我们的崩溃处理程序将永远不会被调用。

例如:

abort() 函数

void AbortCrash()
{
    std::cout << "Calling Abort" << std::endl;
    abort();
}

将显示:

Calling Abort

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

abort 在 CRT 内部被调用,我们也需要捕获所有这些情况。

越界向量访问

void OutOfBoundsVectorCrash()
{
    std::cout << "std::vector out of bounds crash!" << std::endl;

    std::vector<int> v;
    v[0] = 5;
}

将显示:

std::vector out of bounds crash!

纯虚函数调用

void VirtualFunctionCallCrash()
{
    struct B
    {
        B()
        {
            std::cout << "Pure Virtual Function Call crash!" << std::endl;
            Bar();
        }

        virtual void Foo() = 0;

        void Bar()
        {
            Foo();
        }
    };

    struct D: public B
    {
        void Foo()
        {
        }
    };

    B* b = new D;
    // Just to silence the warning C4101:
    //    'VirtualFunctionCallCrash::B::Foo' : unreferenced local variable
    b->Foo(); 
}

将显示:

Pure Virtual Function Call crash!
R6025
- pure virtual function call

修复方法

为了捕获上述情况,我们需要将 SetUnhandledExceptionFilter 函数重定向到一个虚拟函数,这样当 CRT 调用 SetUnhandledExceptionFilter(0) 以移除任何自定义崩溃处理程序时,它将调用我们的虚拟函数。

重定向是通过 Jeffrey Richter 和 Christophe Nasarre 撰写的 Microsoft Press 出版 (c) 2008 的书 Windows via C/C++,第五版第 22 章:DLL 注入和 API 挂钩 中介绍的 CAPIHook 类完成的。

代码如下:

LONG WINAPI RedirectedSetUnhandledExceptionFilter(EXCEPTION_POINTERS * /*ExceptionInfo*/)
{
    // When the CRT calls SetUnhandledExceptionFilter with NULL parameter
    // our handler will not get removed.
    return 0;
}

int main()
{
    ::SetUnhandledExceptionFilter(OurCrashHandler);

    CAPIHook apiHook("kernel32.dll", 
      "SetUnhandledExceptionFilter", 
      (PROC)RedirectedSetUnhandledExceptionFilter);

    // Code that crashes
}

64 位程序

重定向过程适用于 32 位代码和 64 位代码。示例代码提供了 32 位和 64 位编译器目标。

静态与动态 CRT 链接

该代码仅适用于动态 CRT 链接(默认行为)。

历史

  • 2011 年 02 月 07 日:初始发布。
© . All rights reserved.