Windows结构性异常处理封装类

     学习软件工程时有句老话,“不存在没有错误的程序”,十余年历练,各种错误如影随行一如鬼魅,由此看真理是不需要检验的,你只需要信仰就可以了。

Windows的程序员对于上图应用程序崩溃的对话框应该再熟悉不过,这是所谓的结构性异常的默认处理方式。空指针读写、数组越界、除零错误、溢出等严重错误,Windows都将产生结构性异常。由于MFC的框架并不提供结构性异常的封装,所以无论使用SDK或者MFC的程序员都必须面对结构性异常。针对错误,当然你要做的大抵三个层次:

(1)、捕捉错误,定位错误,并事后纠正错误。

(2)运行中如非致命性错误,忽略错误,维持程序带病运行。

(3)、当然你足够水平,最好是补救错误,维持程序正确运行。

我日常工作写的最多的是24小时运行的后台值守程序,所以持续运行很关键,但也很困难。目前我只做前两点,有时尝试做第三点,所以写了段代码在捕捉结构性错误的同时,利用dbghelpimagehlp.dll的调试函数产生内存Dump文件,并产生文本文件捕捉一些相关信息用于定位错误。并将结构性异常转换为C++异常,以期维持程序继续运行。对于可预见的关键代码段做一些保护性工作,以期能够补救错误。

以往的做法将结构性异常处理代码在各个项目拷来拷去再适当修改,时间久了、项目多了也觉得不好。去年打包了一下,有改动,所有的项目可以一起升级,规范一些。今天略作整理、精简,希望和有需要的朋友分享。技术上是简单的,用起来也挺简单,可以解决大家一些敲键盘的时间。专业度高的、熟悉结构性异常的朋友可以跳过,不用浪费时间,没接触过的朋友可以看看代码,代码是最能说明问题的,应该有些益处。至于结构性异常的知识俺就不介绍了,网络上多如牛毛。

之所以贴出来,就因为使用简单,举例说明如下,一般应用(seh.h 下载链接 SEH头文件):

#include "seh.h"

void Call1(void *p1, void *p);
void Call2(void *p1, void *p);
void Call3(void *p1, void *p);

void Call1(void *p1,  void *p)
{
    Call2(p, (
void*)0x11223344);
}

void Call2(void *p1, void *p )
{
    Call3(p, (
void*)0x55667788);
}

void Call3(void *p1, void *p  )
{
    
*((char*)p1) = 'a';//产生结构性异常
}
int main(int argc, char *argv[])
{
    SEH
<>::DoCatch();//顶层捕捉结构性异常,捕捉到后产生报告文件并退出,报告文件存于.\seh目录下
    Call1((void*)0xaabbccdd, (void*)0xeeff0011);
    printf(
"\n seh Call exit\n");
    
return 0;
}
将结构性异常转换为C++标准异常:

int main(int argc, char *argv[])
{
    SEH
<>::DoCatch();//顶层捕捉 捕捉漏网之鱼
    SEH<>::DoCatchCpp();//将当前线程的结构性异常转换为C++异常
    try
    {
        Call1((
void*)0xaabbccdd, (void*)0xeeff0011);
    }
    
catch (exception& e)
    {
        printf(
"exception:%s\n", e.what());
    }
    
return 0;
}
有启用捕捉功能当然也要有停用功能:

SEH<>::DoCatch(false);
SEH
<>::DoCatchCpp(false);
//当然这个功能一般用不上,DoCatchCpp将占用一个线程局部存储空间(TLS)
用户自行定制部分。封装一定要注意将变化部分暴露出来。结构性异常处理两个关键事项,一个是生产什么样的报告文件,二是转换为哪个标准的C++异常,所以我在这里用两个模板参数提供变化策略:
template<class ReportType = SehReport, class ThrowType = SehThrowStd>
class SEH ;
简单定制,替换模板参数即可,复杂的就需要扩展编写新的类。

//一下策略,将不产生报告文件,捕捉到就行异常将抛出MFC异常
SEH<SehNvlReport, SehThrowMfc>::DoCatchCpp();
//自定义报告类,必须实现void Report(_EXCEPTION_POINTERS* pException)    
class MySehReport : public SehReport
{
public:
    
void Report(_EXCEPTION_POINTERS* pException)    
    {
        system(
"ipconfig -a > ip.txt");//保存出错程序当前运行机器的IP配置
    }
};
//自定义异常抛出 必须实现static void Throw(LPCTSTR pMsg)
class MyThrowSeh
{
public:
    
static void Throw(LPCTSTR pMsg)
    {
        
throw pMsg;
    }

};
//使用
SEH<MySehReport, MyThrowSeh>::DoCatchCpp();
try
{
    Call1((
void*)0xaabbccdd, (void*)0xeeff0011);
}
catch(LPCTSTR pMsg)
{
    printf(
"LPCTSTR:%s\n", pMsg);
}

其他注意事项:
如果需将结构性异常转换为C++异常,应在编译参数中添加/EHa,这样做是为了避免VC优化器当检测不到抛出异常语句,会将捕捉语句优化去除,比如

try
{
   
//如果不包含throw new CException()

}
catch(CException* E)
{
   
//本语句将被优化忽略
}
多线程程序将结构性异常转换为C++异常,必须在每一个线程入口点加入SEH<>::DoCatchCpp(); 而SEH<>::DoCatch();整个程序只需一个 。

posted on 2009-05-14 11:23 llbird 阅读(1046) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

导航

统计

常用链接

留言簿(8)

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜