About Exceptions and Exception Handling
About Exception
当程序遇到一个异常或一个严重的错误时,通常意味着它不能继续正常运行并且需要停止执行。
例如,当遇到下列情况时,程序会出现异常:
程序访问一个不可用的内存地址(例如,NULL指针);
l 无限递归导致的栈溢出;
l 向一个较小的缓冲区写入较大块的数据;
l 类的纯虚函数被调用;
l 申请内存失败(内存空间不足);
l 一个非法的参数被传递给C++函数;
l C运行时库检测到一个错误并且需要程序终止执行。
有两种不同性质的异常:结构化异常(Structured Exception Handling, SEH)和类型化的C++异常。
SEH是为C语言设计的,但是他们也能够被用于C++。SEH异常由__try{}__except(){}结构来处理。SEH是VC++编译器特有的,因此如果你想要编写可移植的代码,就不应当使用SEH。
C++中类型化的异常是由try{}catch(){}结构处理的。例如(例子来自这里http://www.cplusplus.com/doc/tutorial/exceptions/):
2 using namespace std;
3
4 int main(){
5 try{
6 throw 20;
7 }
8 catch ( int e){
9 cout << " An exception occrred. Exception Nr. " << e << endl;
10 }
11
12 return 0;
13 }
结构化异常处理
当发生一个SEH异常时,你通常会看到一个意图向微软发送错误报告的弹出窗口。
你可以使用RaiseException()函数自己产生一个SEH异常。
你可以在你的代码中使用__try{}__except(Expression){}结构来捕获SEH异常。程序中的main()函数被这样的结构保护,因此默认地,所有未被处理的SEH异常都会被捕获。
例如:
2
3 int main(){
4 int *p = NULL; // pointer to NULL
5 __try{
6 // Guarded code
7 *p = 13; // causes an access violation exception;
8 }
9 __except(EXCEPTION_EXECUTE_HANDLER){ // Here is exception filter expression
10 // Here is exception handler
11 // Terminate program
12 ExitProcess( 1);
13 }
14
15 return 0;
16 }
每一个SEH异常都有一个与其相关联的异常码(exception code)。你可以使用GetExceptionCode()函数来获取异常码。你可以通过GetExceptionInformation()来获取异常信息。为了使用这些函数,你通常会像下面示例中一样定制自己的exception filter。
下面的例子说明了如何使用SEH exception filter。
2 // Generate error report
3 // Execute exception handler
4 return EXCEPTION_EXECUTE_HANDLER;
5 }
6
7 int main(){
8 __try{
9 // .. some buggy code here
10 }
11 __except(seh_filter(GetExceptionCode(), GetExceptionInformation())){
12 // Terminate program
13 ExitProcess( 1);
14 }
15
16 return 0;
17 }
__try{}__exception(){}结构是面向C语言的,但是,你可以将一个SEH异常重定向到C++异常,并且你可以像处理C++异常一样处理它。我们可以使用C++运行时库中的_set_se_translator()函数来实现。
看一个MSDN中的例子(译者注:运行此例子需打开/EHa编译选项):
2 #include <windows.h>
3 #include <eh.h>
4
5 void SEFunc();
6 void trans_func(unsigned int, EXCEPTION_POINTERS *);
7
8 class SE_Exception{
9 private:
10 unsigned int nSE;
11 public:
12 SE_Exception(){}
13 SE_Exception(unsigned int n) : nSE(n){}
14 ~SE_Exception() {}
15 unsigned int getSeNumber(){ return nSE; }
16 };
17
18 int main( void){
19 try{
20 _set_se_translator(trans_func);
21 SEFunc();
22 }
23 catch(SE_Exception e){
24 printf( " Caught a __try exception with SE_Exception.\n ");
25 }
26 }
27
28 void SEFunc(){
29 __try{
30 int x, y= 0;
31 x = 5 / y;
32 }
33 __finally{
34 printf( " In finally\n ");
35 }
36 }
37
38 void trans_func(unsigned int u, EXCEPTION_POINTERS* pExp){
39 printf( " In trans_func.\n ");
40 throw SE_Exception();
41 }
你可能忘记对一些潜在的错误代码使用__try{}__catch(Expression){}结构进行保护,而这些代码可能会产生异常,但是这个异常却没有被你的程序所处理。不用担心,这个未被处理的SEH异常能够被unhandled Exception filter所捕获,我们可以使用SetUnhandledExceptionFilter()函数设置top-levelunhandled exception filter。
异常信息(异常发生时的CPU状态)通过EXCEPTION_POINTERS被传递给exception handler。
例如:
1 // crt_settrans.cpp2 // compile with: /EHa
3 LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionPtrs){
4 // Do something, for example generate error report
5 // ..%%%%%pc@ezCP]@jj^o^kMNCT\lDbcezpfZvW_qYGZqX[NhHdOtxORYzNKg\FQ?pmnuGXU]G>E>CMqGXut<J@<<R\m=fCsBXYz>gCoGL\X^e`Fuc`=K{XYCpGKp]fIUk_UPlHyWpDGNCY]_rfk^]fc\?HYTBi`XvGjMcF{RV`seiBUJ{d=k>MJYqquTFgmN_EHELKa`okprmMIFcbXraIxktSy<VmO>=H]dN`DgDrGJ@lAQZmWhNMpcgs<k_Cln=rE]BgNj^I]UKszsOXteyJGdJiqa_\`USA=GC^cez_e@YlnL^[rrjVuA[@KLpVeJ[aoeOEtJieEOUIYHbSpLtXiLuEexAiX]NhIklw`qixptEQcM{SG_L?BMYTxHZp`dqwfkN]iGijHxfP`XJzjePTYe@dnlfXXqRC[\tVKJEjxO^Z\mjx=taBIbACi`FNIuDRTlTOT_Tbl{{RTS{@lQjIuYATBEygeZOrrqwwiHs[CjOWHFdJt`RNQC_AVZbkjRj?HgDt@ljG=vSgDF_EfTmf@m_{qhuO_[NXnfUVq?ioq{ueWktpwP<pSHwlTQBPQpRS{{KCMaYqOJscwtNDuB<{gtKyZvv\elMo>KeL^hgKJfnsE[CueeW\bbBmvfRegMDWRBgLj{`MzB]pSo\wSpcAUHkuTPF_JDkipS[\?zj?km_xrnijBU<XQdhBYnn<hlaEoNbPuU>Ks[_j?ngWJfgPH{?[VVaDTHKOAuUrVDPu^paRpKX>ywgiRMLWyAFrPqHyvssBFhIHJv=h=KBPa\wTJCdz[oZAN`ih<G<wiLqt^VHCuMWGEINkaKov_sDibC^SvDXGdrlN[RTbdjNr?SSr?au\<<aqSOiq<[ltzGU_gczfXRTMdEtQfBimE@XI?^XjHOhPXD{STv{LPXWmrWKOVl=HU<aWDn??YBEjHiG`BmcEAWGBa`gfZzWlZ^rpf<^vijZAbUbPR=rQFlv^]XG@TkQnbcdMwIOlc{^bgc<{df?JZ?iytwfyx\gZuNEgaNI=d^DZlVthIB\IDXJJmoMCvXlQx{vTTMcS<dMqSGJptsx=`gKGcog[ZPOeM>TwJIdYXyOyP^Dmnzx=HvvK=WEIOPgaHNHKAr<UBGsj\SmP>gXANooPPcpsNuUmlIePmq^YVtvnurDE=>LlgxLR\^l^aYE?s?@nBlbA^[syvQA>oUx[{TQdWGiXeIeOOLKZ<Xt`Be>hA^k?<{iPAJDWx={dTJQH{Y?hCsFQdbT^dJeDePlLEYamPTOEUTbGiNRBzhhYSdhSkwcJI=yrJju>UAd@Ue=j<^rr>jQNNhEyz\XMI]zt%%%%%
6 // Execute default exception handler next
7 return EXCEPTION_EXECUTE_HANDLER;
8 }
9
10 void main(){
11 SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
12 // .. some unsafe code here
13 }
向量化异常处理(Vectored Exception Handling)
向量化异常处理(VEH)是结构化异常处理的一个扩展,它在Windows XP中被引入。
你可以使用AddVectoredExceptionHandler()函数添加一个向量化异常处理器,VEH的缺点是它只能用在WinXP及其以后的版本,因此需要在运行时检查AddVectoredExceptionHandler()函数是否存在。
要移除先前安装的异常处理器,可以使用RemoveVectoredExceptionHandler()函数。
VEH允许查看或处理应用程序中所有的异常。为了保持后向兼容,当程序中的某些部分发生SEH异常时,系统依次调用已安装的VEH处理器,直到它找到有用的SEH处理器。
VEH的一个优点是能够链接异常处理器(chain exception handlers),因此如果有人在你之前安装了向量化异常处理器,你仍然能截获这些异常。
当你需要像调试器一样监事所有的异常时,使用VEH是很合适的。问题是你需要决定哪个异常需要处理,哪个异常需要跳过。 In program's code, some exceptions may be intentionally guarded by __try{}__except(){} construction, and handling such exceptions in VEH and not passing it to frame-based SEH handler, you may introduce bugs into application logics.
VEH目前没有被CrashRpt所使用。SetUnhandledExceptionFilter()更加适用,因为它是top-level SEH处理器。如果没有人处理异常,top-level SEH处理器就会被调用,并且你不用决定是否要处理这个异常。
CRT 错误处理
除了SEH异常和C++类型化异常,C运行库(C runtime libraries, CRT)也提供它自己的错误处理机制,在你的程序中也应该考虑使用它。
当CRT遇到一个未被处理的C++类型化异常时,它会调用terminate()函数。如果你想拦截这个调用并提供合适的行为,你应该使用set_terminate()函数设置错误处理器(error hanlder)。