Windows异常处理

来源:互联网 发布:开淘宝店卖什么最赚钱 编辑:程序博客网 时间:2024/06/03 19:46

1、Windows对于C++异常处理的扩展:

为了更便捷的在Windows系统中进行异常处理,微软在C++异常处理的机制上,又增加了扩展,称为SEH(Structured Exception Handling),即结构化异常处理;

语法如下:
__try

{

……

}
__except (<exception>)

{

……

}

Windows exception 最常见的问题 :
runtime_error 运行时错误:仅在运行时才能检测到的问题 
range_error 运行时错误:生产的问题超出了有意义的值域范围 
overflow_error 运行时错误:计算上溢 
underflow_error 运行时错误:计算下溢 
logic_error 逻辑错误: 可在运行前检测到的问题 
domain_error 逻辑错误: 参数的结果值不存在 
invalid_argument 逻辑错误: 不合适的参数 
length_error 逻辑错误: 试图生成一个超出该类型最大长度的对象 
out_of_range 逻辑错误:使用一个超出有效范围的值 


2、__try -__except:

__try-__except是Microsoft扩展出的C++关键字,__try块中出现错误或异常,一般不再用throw抛出,而是直接产生一个EXCEPTION_POINTERS类型的异常数据,然后开始查找SEH例程入口(调试的情况除外)。首先就会找到与__try块对应的__except块。__except的参数<exception>与catch的参数作用完全不同,也不类似于函数的参数,它主要是用于控制后面的程序执行,为这几个值之一:
EXCEPTION_EXECUTE_HANDLER(1),表示下面执行__except块内及其后面的代码

EXCEPTION_CONTINUE_EXECUTION(-1),表示回到抛出异常处继续向下执行

EXCEPTION_CONTINUE_SEARCH(0), 表示查找下一个异常处理例程入口
Microsoft提供两个函数GetExceptionCode(), GetExceptionInformation(),分别可以获取异常号和EXCEPTION_POINTERS类型的异常数据指针。而且这两个函数只能在__except参数<exception>的表达式中使用。为了保证这一点,在VC中,编译器做了特殊处理,如果这两个函数没有在正确的位置,将产生编译错误。


3、例:处理除零异常:

 #include <windows.h>

Intmain()
{
inti= 1;
intj = 0;
__try
{
i/= j;
}
__except(GetExceptionCode() ==
EXCEPTION_INT_DIVIDE_BY_ZERO ?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH)
{
cout<< “除零异常" << endl;
}
}

4、例:处理写内存异常:

Int main()
{
__try
{
int *p=NULL;
*p = 13;
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
cout << “内存异常" << endl;
}
}

5、Windows异常处理流程:

(1)中止当前程序的执行。
(2)如果程序处于被调试状态,向调试器发送EXCEPTION_DEBUG_EVENT消息。
(3)如果程序没有被调试或者调试器未能处理异常,查找线程相关的异常处理例程(如对应__except块)并处理。如果前面查找到的例程返回  EXCEPTION_CONTINUE_SEARCH,且线程有多个异常处理例程,则沿这些例程入口地址组成的链式结构逐一向后查找,请求下一个例程处理。
(4)如果线程没有对应的异常处理例程,或线程所有例程都返回EXCEPTION_CONTINUE_SEARCH,而且程序处于被调试状态,再次通知调试器。
(5)如果程序没有被调试或者调试器仍未处理异常,则进入主线程的“最终异常处理例程”链继续查找。
(6)“最终异常处理例程”链的最后是Windows默认的系统异常处理程序__CxxUnhandledExceptionFilter(),其处理通常是弹出一个异常对话框,上面显示一些异常信息,提   供“关闭”、“调试”等按钮。

6、 SEH 到 C++异常的转换

在同一个程序中,如果使用WIN32API它会抛出SHE,使用C++库函数,它们又会抛出C++异常,Win32API和C++函数混和使用时如果使用两种异常捕获机制时,使用起来会影响程序的可读性,因此C++运行库提供了_set_se_translator 函数,在SHE异常发生时通过回调方式来转换SEH异常为C++异常。在此提供一个转换的宏来实现转换。

转换宏的代码:

#define INSTALL_SEHCONVERT() ExceptionConvert ecExceptionConvert

class SEHException

{

private :

             unsigned int nSE;

public :

             SEHException() {}

    SEHException( unsigned int n ) : nSE( n ) {}

             ~SEHException() {}

    unsigned int getSeNumber() { return nSE; }

};

class ExceptionConvert

{

public :

         ExceptionConvert(){OldFanc = _set_se_translator(trans_func); }

         ~ExceptionConvert(){_set_se_translator(OldFanc); }

private :

                  static void trans_func( unsigned int u, EXCEPTION_POINTERS* pExp )

         {

                      throw SEHException(u);

         }

         _se_translator_function OldFanc;

};

使用上面 INSTALL_SEHCONVERT 宏后就可以使用如下代码来捕获SHE异常了

INSTALL_SEHCONVERT();

Try

{

         …

}

catch(SEHException &seh){

   …

}

7、 同步异常与异步异常

1) VC的C++ Exception 采用两种模式捕获异常:同步模式和异步模式。VC的工程的调试版本缺省使用异步模式,工程的发布版本缺省使用同步模式。在同步模式下,VC的编译器假定代 码中只有在显示使用throw和调用函数的时候才会引发异常,因此,在同步模式下,VC编译出的代码比较小,但在这种模式下,try-catch对不能捕 获内存访问异常与算术除零异常等。在异步模式下,VC的编译器为try块内的每一条语句生成异常捕获代码,在这种情况下,他能够捕获全部的异常,还能保证 栈上对象在解栈中正确释放。为了要在发行版本中也能够捕获全部异常就需要打开异步模式,但代价是程序编译出代码变大,运行速度变慢。

2)编译选项:

同步模式的编译选项为/EHs或者/GX(等同于/EHsc)

异步模式的编译选项为/EHa

8、 多线程下的异常捕获

在创建线程并运行线程的函数中把创建线程的代码放在try块中并不会捕获到线程函数中发生的异常,线程函数中发生的异常只能在线程函数中捕获。并且每一个线程都需要自己的SHE转换宏。转换宏可以放在线程函数的开始部分