场景

1.在Windows上用C/C++开发软件, 经常会出现软件级别的崩溃情况, 如果用户看到这种崩溃报告, 那么一般会认为软件质量不高, 从而不想用. Windows上就会有崩溃报告这种噢给你工具来生成dump文件来分析, 可以让用户发送崩溃报告过来解决问题进而提高软件质量.

2.一般情况下有些崩溃无法捕捉, 这是因为CRT调用SetUnhandledExceptionFilter(0)来移除自定义处理器.

例子

1.以下例子使用了部分[Windows][初级][Release程序的崩溃报告minidump解决方案] 代码. 大多数情况下生成的崩溃报告时需要打包的, 这样方便传输. 这里为了说明主要问题忽略打包部分. CAPIHook部分参考了 SetUnhandledExceptionFilter and the C/C++ Runtime Library.

2.这种方式能捕捉大部分异常和崩溃, 但是有个问题, 就是通过CAPIHook转发的dmp文件不能获取到准确的出错行,(有人知道的话留个言)

3.项目地址在项目源码. 注意, 这个test-crash.cpp项目代码不是最新, 用以下的.[20170103]

// test-crash.cpp : 定义控制台应用程序的入口点。
//

#include <SDKDDKVer.h>

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <Windows.h>
#include <DbgHelp.h>
#include <string>
#include <vector>
#include <iostream>
#include "APIHook.h"

static std::wstring gDumpPath;
static std::wstring gDumpZipPath;
static BOOL IsDataSectionNeeded(const WCHAR* pModuleName)
{
    if(pModuleName == NULL)
    {
        return FALSE;
    }    

    WCHAR szFileName[_MAX_FNAME] = L"";
    _wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);    

    if(wcscmp(szFileName, L"ntdll") == 0)
        return TRUE;    

    return FALSE;
}   

static BOOL CALLBACK MiniDumpCallback(PVOID                            pParam,
                                      const PMINIDUMP_CALLBACK_INPUT   pInput,
                                      PMINIDUMP_CALLBACK_OUTPUT        pOutput)
{
    if(pInput == 0 || pOutput == 0)
        return FALSE;    

    switch(pInput->CallbackType)
    {
    case ModuleCallback:
        if(pOutput->ModuleWriteFlags & ModuleWriteDataSeg)
            if(!IsDataSectionNeeded(pInput->Module.FullPath))
                pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg);
    case IncludeModuleCallback:
    case IncludeThreadCallback:
    case ThreadCallback:
    case ThreadExCallback:
        return TRUE;
    default:;
    }    

    return FALSE;
}   

static LONG WINAPI TopLevelUnhandledExceptionFilter(PEXCEPTION_POINTERS pExInfo)
{
    HANDLE hFile = ::CreateFile( gDumpPath.c_str(), GENERIC_WRITE, 0, NULL,
            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if( hFile != INVALID_HANDLE_VALUE)
    {
        MINIDUMP_EXCEPTION_INFORMATION einfo;
        einfo.ThreadId = ::GetCurrentThreadId();
        einfo.ExceptionPointers = pExInfo;
        einfo.ClientPointers = FALSE;  

        MINIDUMP_CALLBACK_INFORMATION mci;
        mci.CallbackRoutine     = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;
        mci.CallbackParam       = NULL;    

        ::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile,MiniDumpNormal,&einfo, NULL, &mci);
        ::CloseHandle(hFile);
    }

    std::cout << "TopLevelUnhandledExceptionFilter" << std::endl;

    return EXCEPTION_EXECUTE_HANDLER;
}  

static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(
    LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{
return NULL;
}  

LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
{
    std::cout << "VectoredExceptionHandler" << std::endl;

    HANDLE hFile = ::CreateFile( gDumpPath.c_str(), GENERIC_WRITE, 0, NULL,
            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if( hFile != INVALID_HANDLE_VALUE)
    {
        MINIDUMP_EXCEPTION_INFORMATION einfo;
        einfo.ThreadId = ::GetCurrentThreadId();
        einfo.ExceptionPointers = pExceptionInfo;
        einfo.ClientPointers = FALSE;  

        MINIDUMP_CALLBACK_INFORMATION mci;
        mci.CallbackRoutine     = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;
        mci.CallbackParam       = NULL;    

        ::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile,MiniDumpNormal,&einfo, NULL, &mci);
        ::CloseHandle(hFile);
    }

    exit(-1);
    return EXCEPTION_CONTINUE_EXECUTION ;
}

LONG WINAPI RedirectedSetUnhandledExceptionFilter(EXCEPTION_POINTERS * pExceptionInfo)
{
    // When the CRT calls SetUnhandledExceptionFilter with NULL parameter
    // our handler will not get removed.
    std::cout << "RedirectedSetUnhandledExceptionFilter" << std::endl;
    return EXCEPTION_CONTINUE_SEARCH ;
}

const wchar_t* GetAppModule()
{
    static wchar_t szbuf[MAX_PATH];
    ::GetModuleFileNameW(NULL,szbuf,MAX_PATH);
    return szbuf;
}

void TestNULLPointerExceptionCrash()
{
    std::cout << "TestNULLPointerExceptionCrash" << std::endl;
    int* i = NULL;
    *i = 9;
}

void TestRaiseExceptionCrash()
{
    std::cout << "TestRaiseExceptionCrash" << std::endl;
    RaiseException(0xc0000374, 0, 0, NULL);
}

void TestCorruptCrash()
{
    std::cout << "TestCorruptCrash" << std::endl;
    std::string* str = new std::string("hello");
    delete str;
    str->append("worldasdfasdfasdfasdfasdfasdfasdfasdfasdfas");
    delete str;
    //std::cout << str << std::endl;
}

void TestfreadCrash()
{
    std::cout << "TestfreadCrash" << std::endl;
    fread("hello",5,1,NULL);
}

void TestAbortCrash()
{
    std::cout << "TestAbortCrash" << std::endl;
    abort();
}

void TestOutOfBoundsVectorCrash()
{
    std::cout << "std::vector out of bounds crash!" << std::endl;
    std::vector<int> v;
    v[0] = 5;
}

int _tmain(int argc, _TCHAR* argv[])
{
    gDumpPath = GetAppModule();
    gDumpPath.append(L"-minidump.dmp");

    AddVectoredExceptionHandler(1, VectoredExceptionHandler);
    SetUnhandledExceptionFilter(TopLevelUnhandledExceptionFilter);
    CAPIHook apiHook("kernel32.dll",
      "SetUnhandledExceptionFilter",
      (PROC)RedirectedSetUnhandledExceptionFilter);

    // 第一种 TopLevelUnhandledExceptionFilter
    //TestNULLPointerExceptionCrash();

    // 第二种 TopLevelUnhandledExceptionFilter
    //TestRaiseExceptionCrash();

    // 第三种 CAPIHook
    // 问题事件名称: BEX
    //TestfreadCrash();

    // 第四种 AddVectoredExceptionHandler
    TestCorruptCrash();

    // 第五种 CAPIHook
    // 问题事件名称: APPCRASH
    //TestAbortCrash();

    // 第六种 TopLevelUnhandledExceptionFilter
     //TestOutOfBoundsVectorCrash();
    std::cout << "End..." << std::endl;
    return 0;
}

备注:

EXCEPTION_CONTINUE_EXECUTION (–1) Exception is dismissed. Continue execution at the point where the exception occurred.

EXCEPTION_CONTINUE_SEARCH (0) Exception is not recognized. Continue to search up the stack for a handler, first for containing try-except statements, then for handlers with the next highest precedence.

EXCEPTION_EXECUTE_HANDLER (1) Exception is recognized. Transfer control to the exception handler by executing the __except compound statement, then continue execution after the __except block.

参考

SetUnhandledExceptionFilter and the C/C++ Runtime Library

try-except Statement

[Windows]_[中级]_[崩溃报告的中级解决方案]的更多相关文章

  1. Google Breakpad 在 windows下捕获程序崩溃报告

    http://blog.csdn.net/goforwardtostep/article/details/56304285

  2. [Windows]_[0基础]_[Release程序的崩溃报告minidump解决方式]

    场景: 1. Release的程序崩溃时,崩溃报告能够让开发者查明代码哪里出了问题,用处大大的. 2. 仅仅实用VS的编译器才支持,所以MinGW就无缘了. 3. 使用了未处理异常过滤处理函数. 4. ...

  3. 关于Windows文件读写_暗涌_新浪博客

    关于Windows文件读写_暗涌_新浪博客     这几天在研究怎么才能加快windows文件读写速度,搜了很多文章,MSDN也看了不少.稍微给大家分享一下.     限制windows文件读写速度的 ...

  4. Windows基础环境_安装配置教程(Windows7 64、JDK1.8、Android SDK23.0、TortoiseSVN 1.9.5)

    Windows基础环境_安装配置教程(Windows7 64.JDK1.8.Android SDK23.0.TortoiseSVN 1.9.5) 安装包版本 1)     JDK版本包 地址: htt ...

  5. 看懂 游戏《Minecraft》的崩溃报告 服务端/客户端

    如何看懂Minecraft报错的关键信息. 让你如何看懂Minecraft报错 前言 一些俏皮话 寻找崩溃日志 打开崩溃日志 重要的事说三遍 下载文本编辑器 开始分析 深度分析 得出结论 修复报错 解 ...

  6. 宏定义中的##操作符和... and _ _VA_ARGS_ _

    1.Preprocessor Glue: The ## Operator 预处理连接符:##操作符 Like the # operator, the ## operator can be used i ...

  7. Lock锁_线程_线程域

    using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...

  8. C++框架_之Qt的开始部分_概述_安装_创建项目_快捷键等一系列注意细节

    C++框架_之Qt的开始部分_概述_安装_创建项目_快捷键等一系列注意细节 1.Qt概述 1.1 什么是Qt Qt是一个跨平台的C++图形用户界面应用程序框架.它为应用程序开发者提供建立艺术级图形界面 ...

  9. 软件测试_Loadrunner_APP测试_性能测试_脚本优化_脚本回放

    本文主要写一下在使用Loadrunner录制完毕APP脚本之后如何对脚本进行回放,如有不足,欢迎评论补充. 如没有安装Loadrunner软件,请查看链接:软件测试_测试工具_LoadRunner: ...

随机推荐

  1. Vue2学习笔记:v-on

    Vue的事件: v-on: click/mouseover/mouseover/mousedown/dbclick/... 下面是点击事件介绍: 1.点击事件 <!DOCTYPE html> ...

  2. TreeSet的自然排序(自定义对象 compareTo方法)

    >要实现自然排序,对象集合必须实现Comparable接口,并重写compareTo()方法 >一般需求中描述的是"主要条件",如:按姓名长度排序.  需注意次要条件 ...

  3. September 21st 2017 Week 38th Thursday

    What fire does not destroy, it hardens. 烈火摧毁不了的东西,只会变得更坚固. The true gold can stand the test of fire, ...

  4. APUE 12.7 取消选项

  5. 从0开始搭建Element项目

    第一步:安装 Node.js/NPM 下载Node.js:https://nodejs.org/zh-cn/download/ 下载安装即可. 第二步:安装 vue-cli 打开 cmd 创建,在命令 ...

  6. JavaScript基础进阶之常用字符串方法总结

    前面三篇文章简单的把JavaScript基础内容过了一遍,我们已经可以用JavaScript写一些简单的代码了. 今天主要总结一下JavaScript中String对象中自带的一些方法,来帮助我们处理 ...

  7. 【原创】uwsgi中多进程+多线程原因以及串行化accept() - thunder_lock说明

    如有不对,请详细指正. 最近再研究uwsgi如何部署python app,看uwsgi的文档,里面有太多的参数,但每个参数的解释太苍白,作为菜鸟的我实在是不懂.想搞清楚uwsgi的工作原因以及里面的一 ...

  8. 解密虚拟 DOM——snabbdom 核心源码解读

    本文源码地址:https://github.com/zhongdeming428/snabbdom 对很多人而言,虚拟 DOM 都是一个很高大上而且远不可及的专有名词,以前我也这么认为,后来在学习 V ...

  9. oracle删除用户及其表空间

    oracle删除用户及其表空间 删除表空间:可以先将其offlinealter tablespace xx offline;将磁盘上的数据文件一同删除drop tablespace xxx inclu ...

  10. 将jpg压缩成webp格式的图片

    cwebp名称 cwebp -压缩图像文件为的WebP文件概要 cwebp [选项] INPUT_FILE -o output_file.webp描述 cwebp压缩使用的WebP格式的图像.输入格式 ...