C++ 在所有错误情况下,我需要采取什么措施来获取崩溃转储?

C++ 在所有错误情况下,我需要采取什么措施来获取崩溃转储?,c++,windows,visual-c++,crash-dumps,C++,Windows,Visual C++,Crash Dumps,我们在Windows上,我们希望为所有应用程序意外退出的场景获得崩溃转储(可能使用MiniDumpWriteDump) 到目前为止,我们已经确定并建立了以下内容: >代码> SununHANDLeDeXCEPTIONSUMENTROWS/,用于未处理异常(Win32,以及“正常”C++)。 \u set\u无效\u参数\u处理程序用于CRT无效参数处理 \u设置\u abort\u行为加上一个SIGABRT处理程序,以说明调用abort() 我们错过了什么吗?(对一些非合法调用ExitPr

我们在Windows上,我们希望为所有应用程序意外退出的场景获得崩溃转储(可能使用
MiniDumpWriteDump

到目前为止,我们已经确定并建立了以下内容:

    >代码> SununHANDLeDeXCEPTIONSUMENTROWS/<代码>,用于未处理异常(Win32,以及“正常”C++)。
  • \u set\u无效\u参数\u处理程序
    用于CRT无效参数处理
  • \u设置\u abort\u行为
    加上一个
    SIGABRT
    处理程序,以说明调用
    abort()
我们错过了什么吗?(对一些非合法调用
ExitProcess
TerminateProcess
或其中一个
exit
变体的代码进行模化。)


我将注意到,这里的这个问题与如何获得崩溃转储是正交的。例如,如果您想在
中止
的情况下进行崩溃转储,则必须始终使用
\u set\u abort\u behavior
,否则中止只需
退出
s即可


我还要注意的是,在Windows7+上,不设置SetUHEF,而只设置通常是一种可行的方法。

简单来说,这是一个很高的要求:


  • 您不需要使用任何其他的
    \u set*
    函数,
    SetUnhandledExceptionFilter
    就足够了
  • C运行时函数如
    abort
    将禁用全局异常处理程序,您可以使用
    SetUnhandledExceptionFilter
    设置全局异常处理程序。CRT将简单地调用相同的函数will
    NULL
    参数,如果CRT导致崩溃,您的异常处理程序将被禁用(未调用)!你能做什么[X]
  • 在调用exption处理程序时禁用所有其他正在运行的线程。只需使用
    CreateToolhelp32Snapshot
    和其他函数查找所有线程。查找此进程,并挂起所有其他正在运行的线程(当然,当前线程除外)
  • 使用SEH或不使用SEH,除非CRT干涉器,否则将调用全局异常处理程序。不用担心(在大多数情况下)
  • 不要在两者之间使用任何CLR,如果任何CLR/托管调用(来自C/C++)介于两者之间,它将不允许异常处理程序调用
  • 您无法处理一个异常-堆栈溢出!想想看!在调试器下运行是唯一的解决方案,请参见下文
还有更多,我还没有尝试过(没有发现有用的)向量异常处理

另一种方法是将应用程序运行到调试器中,您可以自己制作调试器!在调试器中,您可以捕获所有异常,就像VS调试器捕获一样。看我的。但是,你知道,这不是正确的方法

编辑:只需阅读有关进程终止的最后内容。你不应该控制它。在任何情况下,您都可以挂接所需的API,它将充当您的代码(如显示消息框)

[X]您需要使用API挂钩。我手头没有链接和详细信息。您可以钩住其他相关的API,但主要是
SetUnhandledExceptionFilter
(在您为自己调用它之后)。虚拟(挂钩)函数将如下所示:

xxx SetUnhandledExceptionFilter_DUMMY(xxx)
{
  // Dont do any thing
  return NULL;
}
我手头没有API挂钩的链接和详细信息


为什么不尝试让你的应用程序更安全呢

  • 更正所有警告(是,甚至级别4)
  • 使用静态分析。VS本身有(但在更高的版本中,除了2012,所有的变体都有)。还提供了其他SA工具
  • 仔细检查代码。这是值得的
  • 从调试器运行并调试发布版本。使用所有功能
  • 检查并纠正所有可能的内存泄漏
  • 使用防御性的编程方法。与其检查是否为null,不如使用ASSERT或您自己的ASSERT来保护它。使用断言、日志和函数返回将其打包

我将添加一个变通方法,在Windows 7上运行时可以在某些情况下使用:

Windows错误报告(WER)提供了以下选项:


所以,如果你对此没意见,你“只是”必须确保你真正感兴趣的崩溃场景会触发。。。这真的让我们回到了这个问题上,但仍然…

你可以抓住WER的任何异常。正如您所看到的,CRT有时会强制调用WER


如果希望始终捕获进程中的Exception,则需要防止从CRT调用
SetUnhandledExceptionFilter(NULL)
。有关更多信息,请参阅我的博客:

我使用的正是您列出的那些,再加上
\u set\u purecall\u handler
,再加上这个方便的代码片段:

void EnableCrashingOnCrashes()
{
    typedef BOOL (WINAPI *tGetPolicy)(LPDWORD lpFlags);
    typedef BOOL (WINAPI *tSetPolicy)(DWORD dwFlags);
    static const DWORD EXCEPTION_SWALLOWING = 0x1;

    const HMODULE kernel32 = LoadLibraryA("kernel32.dll");
    const tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy");
    const tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
    if(pGetPolicy && pSetPolicy)
    {
        DWORD dwFlags;
        if(pGetPolicy(&dwFlags))
        {
            // Turn off the filter
            pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
        }
    }
}
资料来源:

他的网站上的这些其他文章也帮助我理解了这一点:
SetUnhandledExceptionFilter显然不足以捕获所有意外出口。如果应用程序意外调用纯虚拟函数,则会弹出一个对话框。应用程序将挂起,但不会崩溃。由于没有异常,SetUnhandledExceptionFilter和WER都无法提供帮助。这个主题有一些变化

更糟糕的是,如果在内核回调(如WindowProc)中崩溃,会出现奇怪的情况。如果在64位Windows上的32位应用程序中发生这种情况,则操作系统会捕获异常并继续执行。是的,坠机是无声的。我觉得这很可怕


应详细说明处理这些异常情况所需的所有技巧。

要详细介绍所有答案,我发现以下内容最适合100万次以上的安装:

  • 以防止出现任何WER对话框
  • 对于系统异常
  • 对于CRT,参数处理无效
  • 加上一个SIGABRT处理程序来解释对abort()的调用
也许也应该提到

最重要的是要把事情做好:

  • 所有这些处理程序都应该调用在互斥体/关键部分下执行的函数,以确保sa的其他线程中是否发生任何其他崩溃
    #include <signal.h>
    #include <windows.h>
    #include <boost/thread/mutex.hpp>
    
    void EnableCrashingOnCrashes();
    void PreventSetUnhandledExceptionFilter();
    
    static void exceptionHandler(EXCEPTION_POINTERS* excpInfo)
    {
        // your code to handle the exception. Ideally it should
        // marshal the exception for processing to some other
        // thread and waif for the thread to complete the job
    }
    
    static boost::mutex unhandledExceptionMx;
    static LONG WINAPI unhandledException(EXCEPTION_POINTERS* excpInfo = NULL)
    {
        boost::mutex::scoped_lock lock(unhandledExceptionMx);
        if (!excpInfo == NULL)
        {
            __try // Generate exception to get proper context in dump
            {
                RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
            }
            __except (exceptionHandler(GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER)
            {
            }
        }
        else
        {
            exceptionHandler(excpInfo);
        }
    
        return 0;
    }
    
    static void invalidParameter(const wchar_t* expr, const wchar_t* func,
        const wchar_t* file, unsigned int line, uintptr_t reserved)
    {
        unhandledException();
    }
    
    static void pureVirtualCall()
    {
        unhandledException();
    }
    
    static void sigAbortHandler(int sig)
    {
        // this is required, otherwise if there is another thread
        // simultaneously tries to abort process will be terminated
        signal(SIGABRT, sigAbortHandler);
        unhandledException();
    }
    
    static void setExceptionHandlers()
    {
        SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
        SetUnhandledExceptionFilter(unhandledException);
        _set_invalid_parameter_handler(invalidParameter);
        _set_purecall_handler(pureVirtualCall);
        signal(SIGABRT, sigAbortHandler);
        _set_abort_behavior(0, 0);
        EnableCrashingOnCrashes();
        PreventSetUnhandledExceptionFilter();
    }