利用SetUnhandledExceptionFilter对Release程序进行异常处理。

闪电杀毒手2.5发布出去有一段时间了,最近收到几个Bug反馈。
通常发布出去的Release产品,如果遇到没有预料到的异常,通常都会弹出Windows默认的错误窗口Windows Error Reporter.
这时用户就傻了:交互不友好;RD也傻了:不知道异常的具体信息。

SetUnhandledExceptionFilter可以设置在WER弹出之前的最后一次异常处理的机会。所以只要设置好我们的异常处理函数就可以捕获到Unhandled Exception。SetUnhandledExceptionFilter(CleanToolExceptionFun);
LONG WINAPI CleanToolExceptionFun(struct _EXCEPTION_POINTERS* ExceptionInfo)
{

}

在这个函数里你可以做以下几件事:
1·获得异常的ExceptionRecord和Context
2·获得异常编号
3·获得异常的模块
4·获得堆栈的信息
5·程序继续执行还是退出

1和2都好办,直接通过传递进来的参数就可以获得:
ExceptionInfo->ExceptionRecord->ExceptionAddress;

3通常的做法是,通过ExceptionRecord获得异常的内存地址,调用VirtualQuery获得Handle,再通过GetModuleFileName获得出现异常的文件路径。
MEMORY_BASIC_INFORMATION mbi = {0};
if (FALSE == ::VirtualQuery( addr, &mbi, sizeof(mbi) ) ) return;
UINT_PTR h_module = (UINT_PTR)mbi.AllocationBase;
::GetModuleFileNameW((HMODULE)h_module, sz_module, len);
return;

4的做法是通过StackWalk64函数便利堆栈。
StackWalk64(IMAGE_FILE_MACHINE_I386,hCurrentProcess,hCurrentThread,&sStackFrame,pContext,0,0,0,0)

5是通过函数的返回值来确定的。

在实现的过程遇到几个好玩的问题:
1·如果exe调用dll,dll中要使用AFX_MANAGE_STATE(AfxGetStaticModuleState())之后,dll的资源才能被访问到;但是如果dll调用exe,则必须使用AFX_MANAGE_STATE(AfxGetAppModuleState())。

2·StackWalk64的问题:在Release版本下,Stackwalk64获得的堆栈信息不完整,具体表现为:如果异常是由MFC的模块抛出的,那么获得的堆栈将缺少栈最上方我们自己的模块的信息。比如A->B->MFC,则获得的栈为(至顶向下):MFC->A。但是在Debug的版本下不存在这个问题。为此我还特意用OD调试了Release版本,发现堆栈是完整的,那么只能认为是StackWalk的问题了。

3·在便利异常的模块的节表的时候,Release版本下程序偶尔会莫名其妙地退出。

4·Release编译的优化问题:如果我直接写int i = 5/0,编译时会直接报错。如果我写成:
int i = 0;
AfxMessageBox(L”%d”, 5 /i),编译就可以通过。汗……

《利用SetUnhandledExceptionFilter对Release程序进行异常处理。》上有4条评论

  1. 这个是你的项目还是你自己做着玩的?
    [reply=colordancer,2009-07-03 04:14 PM]公司的项目哈,不过目前50%以上都是我做的[/reply]

  2. SetUnhandledExceptionFilter还是会漏掉某些异常的,应该挂接UnhandledExceptionFilter.

  3. 亲,有些代码里面的参数不知道是怎么来的,能给我份稍微详细点的么,我比较菜……

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注