简单使用SetUnhandledExceptionFilter()函数让程序优雅崩溃

虽然是大公司的产品,QQ它还是会在我们的折腾下崩溃的,但是它总是崩溃的很优雅,还要弹出自己的对话框来结束。并且发送报告,去掉了系统默认的发送报告的对话框。

所以一拍脑袋,想让自己的程序崩溃的体面一点。

自己想了大概的思路,觉得可以用一个进程来监控目标程序。的确也可以拿到了目标程序崩溃的信息,知道它什么时候崩溃的,也可以做额外的操作,但是这样是没办法把默认的发送错误的对话框去掉的。

然后又有人说是不是采用了类似钩子的方法把这个东西在哪里勾掉了。

最后网上查了一番,发现SetUnhandledExceptionFilter这个函数解决了一切。

总结了下搜到的资料,这个函数的返回值有三种情况:

EXCEPTION_EXECUTE_HANDLERequ1表示我已经处理了异常,可以优雅地结束了
EXCEPTION_CONTINUE_SEARCHequ0表示我不处理,其他人来吧,于是windows调用默认的处理程序显示一个错误框,并结束
EXCEPTION_CONTINUE_EXECUTIONequ-1表示错误已经被修复,请从异常发生处继续执行

具体使用方法如下:

#include <windows.h>

long __stdcall callback(_EXCEPTION_POINTERS* excp)
{
MessageBox(0,"Error","error",MB_OK);
printf("Error address %x/n",excp->ExceptionRecord->ExceptionAddress);
printf("cpu register:/n");
printf("eax %x ebx %x ecx %x edx %x/n",excp->ContextRecord->Eax,
excp->ContextRecord->Ebx,excp->ContextRecord->Ecx,
excp->ContextRecord->Edx);
return EXCEPTION_EXECUTE_HANDLER;
}

int main(int argc,char* argv[])
{
SetUnhandledExceptionFilter(callback);
_asm int 3//只是为了让程序崩溃
return 0;
}

vs调试dump

貌似只有vs 05以上的版本,能调试dump文件。

1. dump文件和pdb文件的匹配问题

>> 发布二进制文件时生成的pdb文件一定要保留,只有当发布的二进制文件和pdb文件是同时生成的才好正确调试。

2. dump文件和pdb文件放在哪里的问题

>> 如果dump文件和pdb文件放在同一个目录,则可直接运行调试;当然也可以不是同一个目录,那么在启动dmp文件后,需要设置一下vs的符号文件路径:Tools->Options->Debugging->Symbols. 如果需要调试windows自带的一些dll或者exe,则可以在这里添加windows的pdb文件服务器:http://msdl.microsoft.com/download/symbols

3. 二进制文件放在哪里的问题

>> 现场恢复需要二进制文件,但不必所有的二进制文件都需要,所以即使你的机器和用户的机器操作系统不一样也没关系;出问题的如果是你发布的二进制文件,则只需要你发布的二进制文件就可以了。vs在加载二进制的文件失败的时候会打印出其详细路径,但这是用户机器上的路径,没有必要一定要跟这个路径一样,把你发布的二进制文件放到dump文件目录就可以了。

4. 显示不了源代码的问题

>> 首先需要设置源代码目录,右键solution:Properties->Common Properties->Debug Source Files,里边加入你的本地源代码目录就是了;但是如果代码已经改过了,恢复不到当时的状态,vs显示不了源码怎么办?只要设置:Tools->Options->Debugging->General->Require source files to exactly match the original version 这个复选框钩掉就可以了

Minidump方式保留程序崩溃现场

在Windows平台下用C++开发应用程序,最不想见到的情况恐怕就是程序崩溃,而要想解决引起问题的bug,最困难的应该就是调试release版本了。因为release版本来就少了很多调试信息,更何况一般都是发布出去由用户使用,crash的现场很难保留和重现。目前有一些方法可以解决:崩溃地址+ MAP文件;MAP文件;SetUnhandledExceptionFilter + Minidump。本文重点解决Minidump方式。

一、Minidump文件生成

1、Minidump概念

minidump(小存储器转储)可以理解为一个dump文件,里面记录了能够帮助调试crash的最小有用信息。实际上,如果你在系统属性->高级->启动和故障恢复->设置->写入调试信息中选择“小内存转储(64 KB)”的话,当系统意外停止时都会在C:\Windows\Minidump\路径下生成一个.dmp后缀的文件,这个文件就是minidump文件,只不过这个是内核态的minidump。

我们要生成的是用户态的minidump,文件中包含了程序运行的模块信息、线程信息、堆栈调用信息等。而且为了符合其mini的特性,dump文件是压缩过的。

2、生成minidump文件

通过drwtsn32、NTSD、CDB等调试工具生成Dump文件,drwtsn32存在的缺点虽然NTSD、CDB可以完全解决,但并不是所有的操作系统中都安装了NTSD、CDB等调试工具。根据MiniDumpWriteDump接口,完全可以程序自动生成Dump文件。

3、自动生成Minidump文件

当程序遇到未处理异常(主要指非指针造成)导致程序崩溃死,如果在异常发生之前调用了SetUnhandledExceptionFilter()函数,异常交给函数处理。MSDN中描述为:

Issuing SetUnhandledExceptionFilter replaces the existing top-level exception filter for all existing and all future threads in the calling process.

因而,在程序开始处增加SetUnhandledExceptionFilter()函数,并在函数中利用适当的方法生成Dump文件,即可实现需要的功能。

生成dump文件类(minidump.h)#pragma once

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
<em id= "__mceDel" >
#include <windows.h>
#include <imagehlp.h>
#include <stdlib.h>
#pragma comment(lib,"dbghelp.lib")
inline BOOL IsDataSectionNeeded( const WCHAR * pModuleName)
{
if (pModuleName == 0)
{
return FALSE;
}
szFileName[_MAX_FNAME] = L "" ;
_wsplitpath(pModuleName,NULL,szFileName,NULL);
(wcsicmp(szFileName,L "ntdll" ) == 0)
TRUE;
FALSE;
}
CALLBACK MiniDumpCallback( PVOID pParam,
const PMINIDUMP_CALLBACK_INPUT pInput,
PMINIDUMP_CALLBACK_OUTPUT pOutput)
{
(pInput == 0 || pOutput == 0)
FALSE;
switch (pInput->CallbackType)
{
case ModuleCallback: <br>    {
   (pOutput->ModuleWriteFlags & ModuleWriteDataSeg)
(!IsDataSectionNeeded(pInput->Module.FullPath))
   pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg); <br>    
  }<br>     break ;
IncludeModuleCallback:
IncludeThreadCallback:
ThreadCallback:
ThreadExCallback:
TRUE;
default :<br>       ;
}
FALSE;
}
//创建Dump文件
inline void CreateMiniDump(EXCEPTION_POINTERS* pep, LPCTSTR strFileName)
{
HANDLE hFile = CreateFile(strFileName,GENERIC_WRITE,CREATE_ALWAYS,FILE_ATTRIBUTE_norMAL,NULL);
((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
{
MINIDUMP_EXCEPTION_informatION mdei;
mdei.ThreadId = GetCurrentThreadId();
mdei.ExceptionPointers = pep;
mdei.ClientPointers = FALSE;
MINIDUMP_CALLBACK_informatION mci;
mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;
mci.CallbackParam = 0;
MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)0x0000ffff;
MiniDumpWriteDump(GetCurrentProcess(),GetCurrentProcessId(),hFile,MiniDumpnormal,&mdei,&mci);
CloseHandle(hFile);
}
}
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{
NULL;
}
PreventSetUnhandledExceptionFilter()
{
HMODULE hKernel32 = LoadLibrary(_T( "kernel32.dll" ));
(hKernel32 == NULL)
FALSE;
*pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter" );
(pOrgEntry == NULL)
FALSE;
unsigned char newJump[ 100 ];
DWORD dwOrgEntryAddr = ( ) pOrgEntry;
dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far
*pNewFunc = &MyDummySetUnhandledExceptionFilter;
dwNewEntryAddr = ( ) pNewFunc;
dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr;
newJump[ 0 ] = 0xE9; // JMP absolute
memcpy (&newJump[ 1 ],&dwRelativeAddr, sizeof (pNewFunc));
SIZE_T bytesWritten;
bRet = WriteProcessMemory(GetCurrentProcess(),pOrgEntry,newJump,0);">(pNewFunc) + 1,&bytesWritten);
bRet;
}
LONG WINAPI UnhandledExceptionFilterEx( struct _EXCEPTION_POINTERS *pException)
{
TCHAR szMbsFile[MAX_PATH] = { 0 };
::GetmodulefileName(NULL,szMbsFile,MAX_PATH);
* pFind = _tcsrchr(szMbsFile,monospace;min-height:auto;white-space:Nowrap;color:#0000FF;">'\\' );
(pFind)
{
*(pFind+1) = 0;
_tcscat(szMbsFile,_T( "CreateMiniDump.dmp" ));
CreateMiniDump(pException,szMbsFile);
}
// Todo: MiniDumpWriteDump
FatalAppExit(-1,monospace;min-height:auto;white-space:Nowrap;color:#0000FF;">"Fatal error" ));
EXCEPTION_CONTINUE_SEARCH;
}
//运行异常处理
runcrashHandler()
{
SetUnhandledExceptionFilter(UnhandledExceptionFilterEx);
PreventSetUnhandledExceptionFilter();
}
</em>

  


//测试实现文件

//一个有函数调用的类

31
class CrashTest
{
public :
test()
{
Crash();
}
private :
Crash()
{
strcpy (NULL,monospace;min-height:auto;white-space:Nowrap;color:#0000FF;">"adfadfg" );
}
};
int _tmain( argc,_TCHAR* argv[])
{
//设置异常处理函数
runcrashHandler();
CrashTest test;
test.test();
getchar ();
0;
}

window下使用SetUnhandledExceptionFilter捕获让程序的崩溃的更多相关文章

  1. ios – 伞框架

    错误.应用程序,通常位于…错误仍然存在你也可以在这里添加(子)框架的路径.

  2. ios – Veracode,调试符号和XCode

    我正在尝试通过Veracode扫描iOS应用程序:一个应用程序安全平台.为了使它们扫描.IPA,.IPA需要包含调试符号.对于正在使用的Archive构建配置和项目/目标,我已指定:>生成调试符号:是>复制期间剥离调试符号:NO>死代码剥离:没有>默认隐藏的符号:NO>剥离链接产品:没有然而,在提交.IPA时,Veracode仍然给我以下错误:没有调试符号编译的主文件–1个文件我需要采取另一个步骤来创建调试符号,或许是存档吗?

  3. 如何在xcode 6中构建32位和64位的单二进制文件

    我已经遵循this堆栈溢出解决方案,但即使我得到低于警告.我已经选择虽然我得到了警告.请帮帮我.谢谢.解决方法有同样的问题.看起来将’arm64’添加到ValidArchitectures解决了它.

  4. 从VS调试iOS时,程序’Mono’已从代码0(0x0)退出

    我有一个问题,当我尝试使用VS2013调试我的iOS应用程序时,我收到以下错误:Theprogram‘Mono’hasexitedwithcode0.我知道以下answer.清理解决方案确实解决了问题,但是下次调试时我需要再次执行…如果有人有长期解决方案,我们将非常感激.解决方法在发射期间它会崩溃吗?

  5. ios – 将二进制文件上传到Apple的iTunesConnect时会发生什么?

    我问,因为:它可能指向我们可以做的事情来加快这个过程.大约一年前,这个过程从“缓慢,因为苹果的服务器功率不足”变得“非常缓慢,因为苹果公司使用的带宽是发送二进制文件所需带宽的3倍”.例如我最近提交了一个90Mb应用程序,Xcode4将超过350Mb的数据上传到Apple.例如刚才我提交了一个8Mb的二进制文件,Xcode4将超过40Mb的数据上传到Apple.最近上传者有了很大的改进.但我想知道:苹果在上传期间实际上做了什么?

  6. 如何调试ios共享扩展?

    如何从扩展中打印Xcode的lldb调试器中的日志?解决方法简单回答:>不打印日志消息,但是您可以在断点处停止,然后使用lldb打印所有内容.>运行你的应用程序>在应用程序运行时,转到Debug–>通过PID或名称附加到进程>写下您的扩展名的名称或包ID,然后单击附加>然后以您在设备上执行此操作的任何方式运行扩展程序.>等待调试器在断点处停止扩展.

  7. ios – iTunes Connect警告:“您的二进制文件不支持iPad”

    我刚刚将一个新的二进制文件上传到iTunesConnect,并将其添加到iOS版本的新版本.添加二进制文件并保存更改后,iTunesConnect会显示警告消息:“您的二进制文件不支持iPad,iPad的屏幕截图或应用视频预览将不会显示在AppStore上.”Xcode项目是使用Cordova3CLI生成的;自从应用从Cordova2迁移以来,上传的二进制文件是第一个Cordova3版本.该应用程

  8. ios – 二进制无效,二进制文件缺少体系结构[arm64]

    没有足够的信誉发布图像,请搜索“TimT”作为他的回复.更新:已经修复“是的,最近有一个修复程序应用于服务器,每个人都应该能够再次提交32位应用程序.–由TimT在同一个线程.我再次尝试,一切都很好.

  9. ios – 不能检查CGPoint是否不等于CGPointZero

    我有一个CGPoint在UIView类中声明,在我的viewController中,我尝试检查CGPoint是否不等于CGPointZero,但是我收到这个错误:二进制表达式(‘CGPoint'(又名’structCGPoint’)’和’CGPoint“)这是if语句:错误指向!=和我不知道为什么它给我一个错误.操纵杆是UIView类,CGPoint速度是这样声明的:@property(非原子)C

  10. 通用定制iOS框架使用Xcode 6.4

    我已经使用Xcode6创建了一个自定义的iOS框架.我尝试了许多脚本来使框架通用(用于模拟器和设备).但是它们都不适用于我.请告诉我一种方法来为iOS创建一个自定义的通用框架Xcode6.解决方法要通过终端将两个二进制文件合并成通用二进制文件:首先编译器件二进制,然后分别编译模拟器二进制.找到这两个二进制文件.如果你想检查哪些架构被编译成每个,你可以在终端中运行这个命令:lipo-info/pat

随机推荐

  1. static – 在页面之间共享数据的最佳实践

    我想知道在UWP的页面之间发送像’selectedItem’等变量的最佳做法是什么?创建一个每个页面都知道的静态全局变量类是一个好主意吗?

  2. .net – 为Windows窗体控件提供百分比宽度/高度

    WindowsForm开发的新手,但在Web开发方面经验丰富.有没有办法为Windows窗体控件指定百分比宽度/高度,以便在用户调整窗口大小时扩展/缩小?当窗口调整大小时,可以编写代码来改变控件的宽度/高度,但我希望有更好的方法,比如在HTML/CSS中.在那儿?

  3. 使用Windows Azure查询表存储数据

    我需要使用特定帐户吗?>将应用程序部署到Azure服务后,如何查询数据?GoogleAppEngine有一个数据查看器/查询工具,Azure有类似的东西吗?>您可以看到的sqlExpressintance仅在开发结构中,并且一旦您表示没有等效,所以请小心使用它.>您可以尝试使用Linqpad查询表格.看看JamieThomson的thispost.

  4. windows – SetupDiGetClassDevs是否与文档中的设备实例ID一起使用?

    有没有更好的方法可以使用DBT_DEVICEARRIVAL事件中的数据获取设备的更多信息?您似乎必须指定DIGCF_ALLCLASSES标志以查找与给定设备实例ID匹配的所有类,或者指定ClassGuid并使用DIGCF_DEFAULT标志.这对我有用:带输出:

  5. Windows Live ID是OpenID提供商吗?

    不,WindowsLiveID不是OpenID提供商.他们使用专有协议.自从他们的“测试版”期结束以来,他们从未宣布计划继续它.

  6. 如果我在代码中进行了更改,是否需要重新安装Windows服务?

    我写了一个Windows服务并安装它.现在我对代码进行了一些更改并重新构建了解决方案.我还应该重新安装服务吗?不,只需停止它,替换文件,然后重新启动它.

  7. 带有双引号的字符串回显使用Windows批处理输出文件

    我正在尝试使用Windows批处理文件重写配置文件.我循环遍历文件的行并查找我想要用指定的新行替换的行.我有一个’函数’将行写入文件问题是%Text%是一个嵌入双引号的字符串.然后失败了.可能还有其他角色也会导致失败.如何才能使用配置文件中的所有文本?尝试将所有“在文本中替换为^”.^是转义字符,因此“将被视为常规字符你可以尝试以下方法:其他可能导致错误的字符是:

  8. .net – 将控制台应用程序转换为服务?

    我正在寻找不同的优势/劣势,将我们长期使用的控制台应用程序转换为Windows服务.我们为ActiveMQ使用了一个叫做java服务包装器的东西,我相信人们告诉我你可以用它包装任何东西.这并不是说你应该用它包装任何东西;我们遇到了这个问题.控制台应用程序是一个.NET控制台应用程序,默认情况下会将大量信息记录到控制台,尽管这是可配置的.任何推荐?我们应该在VisualStudio中将其重建为服务吗?我使用“-install”/“-uninstall”开关执行此操作.例如,seehere.

  9. windows – 捕获外部程序的STDOUT和STDERR *同时*它正在执行(Ruby)

    哦,我在Windows上:-(实际上,它比我想象的要简单,这看起来很完美:…是的,它适用于Windows!

  10. windows – 当我试图批量打印变量时,为什么我得到“Echo is on”

    我想要执行一个简单的批处理文件脚本:当我在XP中运行时,它给了我预期的输出,但是当我在Vista或Windows7中运行它时,我在尝试打印值时得到“EchoisOn”.以下是程序的输出:摆脱集合表达式中的空格.等号(=)的两侧可以并且应该没有空格BTW:我通常在@echo关闭的情况下启动所有批处理文件,并以@echo结束它们,所以我可以避免将代码与批处理文件的输出混合.它只是使您的批处理文件输出更好,更清洁.

返回
顶部