[Windows]_[中级]_[崩溃报告的中级解决方案]
场景
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.
参考
[Windows]_[中级]_[崩溃报告的中级解决方案]的更多相关文章
- Google Breakpad 在 windows下捕获程序崩溃报告
http://blog.csdn.net/goforwardtostep/article/details/56304285
- [Windows]_[0基础]_[Release程序的崩溃报告minidump解决方式]
场景: 1. Release的程序崩溃时,崩溃报告能够让开发者查明代码哪里出了问题,用处大大的. 2. 仅仅实用VS的编译器才支持,所以MinGW就无缘了. 3. 使用了未处理异常过滤处理函数. 4. ...
- 关于Windows文件读写_暗涌_新浪博客
关于Windows文件读写_暗涌_新浪博客 这几天在研究怎么才能加快windows文件读写速度,搜了很多文章,MSDN也看了不少.稍微给大家分享一下. 限制windows文件读写速度的 ...
- 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 ...
- 看懂 游戏《Minecraft》的崩溃报告 服务端/客户端
如何看懂Minecraft报错的关键信息. 让你如何看懂Minecraft报错 前言 一些俏皮话 寻找崩溃日志 打开崩溃日志 重要的事说三遍 下载文本编辑器 开始分析 深度分析 得出结论 修复报错 解 ...
- 宏定义中的##操作符和... and _ _VA_ARGS_ _
1.Preprocessor Glue: The ## Operator 预处理连接符:##操作符 Like the # operator, the ## operator can be used i ...
- Lock锁_线程_线程域
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...
- C++框架_之Qt的开始部分_概述_安装_创建项目_快捷键等一系列注意细节
C++框架_之Qt的开始部分_概述_安装_创建项目_快捷键等一系列注意细节 1.Qt概述 1.1 什么是Qt Qt是一个跨平台的C++图形用户界面应用程序框架.它为应用程序开发者提供建立艺术级图形界面 ...
- 软件测试_Loadrunner_APP测试_性能测试_脚本优化_脚本回放
本文主要写一下在使用Loadrunner录制完毕APP脚本之后如何对脚本进行回放,如有不足,欢迎评论补充. 如没有安装Loadrunner软件,请查看链接:软件测试_测试工具_LoadRunner: ...
随机推荐
- 使用 PowerShell 创建和修改 ExpressRoute 线路
开始之前 安装最新版本的 Azure Resource Manager PowerShell cmdlet. 有关详细信息,请参阅 Azure PowerShell 概述. 在开始配置之前,请查看先决 ...
- Python学习---Django的request.post源码分析
request.post源码分析: 可以看到传递json后会帮我们dumps处理一次最后一字节形式传递过去
- Linux 系统磁盘挂载信息文件
设置文件系统挂载信息的文件(etc/fstab),使得开机能够自动挂载磁盘分区 文件系统挂载 方法一: 直接挂在,临时生效 # 格式化系统(没有格式化就没有文件系统,放不了数据) dd if=/dev ...
- JDBC方式执行SQL,支持CRUD返回LIST
背景: 用惯了Mybatis,接收一个老项目使用Hibernate,特别不习惯.新的功能需要系统后台定时执行任务,顾使用JDBC封装工具类执行 源代码 import java.sql.Connecti ...
- 解决华为交换机S5700无法解除ip/Mac绑定的问题
今天同事离职,需要解除他的个人笔记本Mac与ip的绑定 首先进入系统用户视图,然后进入vlanif4,解除151绑定 system-view interface vlanif 4 undo dhcp ...
- 【2】python3字符串的比较(辨析is与==的区别)
PYTHON3基本数据类型(二.字符串) Python3字符串 ①字符串比较 1.比较字符串是否相同: ==:使用==来比较两个字符串内的value值是否相同 is:比较两个字符串的id值. 2.字符 ...
- apache ActiveMQ之初体验
版权声明: https://blog.csdn.net/zdp072/article/details/27237549 一. 开篇语 继上一篇weblogic中使用jms发送和接受消息的文章后, 本文 ...
- P3694 邦邦的大合唱站队
题目背景 BanG Dream!里的所有偶像乐队要一起大合唱,不过在排队上出了一些问题. 题目描述 N个偶像排成一列,他们来自M个不同的乐队.每个团队至少有一个偶像. 现在要求重新安排队列,使来自同一 ...
- 如何用代码而非事件触发PBO
通常我们通过抛出事件触发PBO,但若没有事件发生时,我们其实也可以用代码强制发出命令. 写法如下: CL_GUI_CFW=>SET_NEW_OK_CODE( NEW_CODE = <uco ...
- BigDecimalUtil 工具类
一.为什么要用BigDecimal? 涉及到加减乘除,用int,double 会出现数据丢失,这个时候就要用BigDecimal. 注意:在new BigDecimal(Double.toString ...