场景

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. ASP.NET Core 系列[1]:ASP.NET Core 初识

    ASP.NET Core 是一个跨平台的高性能开源框架,是一个用于连接到互联网的基于云的现代应用程序. ASP.NET Core 用于构建如 Web 应用.物联网(IoT)应用和移动后端应用,这些应用 ...

  2. Kubernetes简述

    一.Kubernetes特性 1.自动装箱 建构于容器之上,基于资源依赖及其他约束自动完成容器部署且不影响其可用性,并通过调度机制混合关键型应用和非关键型应用的工作负载于一点以提高资源利用率. 2.自 ...

  3. [IIS] 配置PHP的过程与坑

    * 32位与64位程序的兼容性问题 如果64位的IIS内的处理程序需要使用32位程序或者扩展,必须在ApplicationPool里面的高级设置里,将AppPool设置为允许32位.否则32位的程序将 ...

  4. 一些简单的SQL Server服务器监控

    1:磁盘活动的一些监控 指标 吞吐量:IOPS,存储子系统每秒能提供多少次I/O 吞吐量,MB/S,I/O子系统每秒能提供多少MB 延时:每个I/O请求占用多少时间 队列长度:队列中有多少IO请求在等 ...

  5. Oracle 18c新特性一览

    1. 一般新特性 1.1. Shadow Lost Write Protection Shadow lost write protection检测到一个丢失的写,它会导致一个主要的数据损坏.可以在不需 ...

  6. Maven构建时跳过部分测试

    当遇到以下场景: 其他人写的单元测试影响统计结果 一些需要调用外部接口的测试暂不运行 需要在非本机环境上运行一些不回滚的单元测试 则有必要选择以下方法跳过部分测试. 在测试用例前加上注解 @Ignor ...

  7. 记Git报错-Everything up-to-date

    文:铁乐与猫 今天git push 到github远程仓库的时候,出现报错"Everything up-to-date",严格来说也不算报错,它只是在告诉你,提交区所有的东西都是最 ...

  8. memcache知识梳理

    定义: memcache是一套分布式的高速缓存系统,由LiveJournal的Brad Fitzpatrick开发,但目前被许多网站使用以提升网站的访问速度,尤其对于一些大型的.需要频繁访问数据库的网 ...

  9. in有两种用法:

    # in有两种用法: 1. 在for中. 是把每一个元素获取到赋值给前⾯的变量. 2. 不在for中. 判断xxx是否出现在str中. #len() 为内置函数,输出为1,2,3,4....., 长度 ...

  10. 个人技术博客二之apk反编译与加密

    根据原文郭霖大神的博客Android安全攻防战,反编译与混淆技术完全解析 本人亲测反编译真的没有什么卵用,个人纯属好奇就去搜了一下,偷窃有罪,抄袭可耻. 1.手机上的apk都是打包好的,直接安装使用. ...