博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Windows下C++异常处理
阅读量:4331 次
发布时间:2019-06-06

本文共 3952 字,大约阅读时间需要 13 分钟。

SEH(structured exception handling)结构化异常处理

Windows提供的异常处理机制,与语言无关。SEH使用关键字: __try, __except, __finally, __leave。

  • __try语句,定义受监控的代码模块。
  • __except语句,定义的异常处理模块。
  • 执行过程:
  1. 受监控的代码模块被执行。
  2. 如果没有出现异常,控制流转入__except子句之后的代码模块中。
  3. 否则出现异常,控制流转入__except后面的表达式中,计算表达式值,再根据这个值来做相应的处理。
  4. EXCEPTION_CONTINUE_EXECUTION (–1) 异常被忽略,控制流将在异常出现的点之后,继续恢复运行。
  5. EXCEPTION_CONTINUE_SEARCH (0) 异常不被识别,也即当前的这个__except模块不是这个异常错误所对应的正确的异常处理模块。系统将继续到上一层的try-except域中继续查找一个恰当的__except模块。
  6. EXCEPTION_EXECUTE_HANDLER (1) 异常已经被识别,也即当前的这个异常错误,系统已经找到了并能够确认,这个__except模块就是正确的异常处理模块。控制流将进入到__except模块中。  
  • __finally 语法的含义就是无论如何,此句总是会执行,常用于资源释放。_finally与__try必须配对,不能同时有__except和__finally。因为两者都必须同时和__try配对使用,否则会报编译错误。
  • Windows提供了两个相关的API:
  1. LPEXCEPTION_POINTERS GetExceptionInformation(VOID);
    1 try  2 {  3     // try block  4 }  5 except ( FilterFunction(GetExceptionInformation() )  6 {  7     // exception handler block  8 }  9 10 typedef struct _EXCEPTION_POINTERS {11     PEXCEPTION_RECORD ExceptionRecord; 12     PCONTEXT ContextRecord; 13 } EXCEPTION_POINTERS;

    ExceptionRecord记录了异常的相关信息,如错误码、标志位等。 ContextRecord记录了异常发生时,线程当时的上下文环境,主要包括寄存器的值,因此有了这些信息,__except模块便可以对异常错误进行很好的分类和恢复处理。不过特别需要注意的是,这两个函数只能是在__except后面的括号中的表达式作用域内有效,否则结果可能没有保证。

  2.  DWORD GetExceptionCode(VOID); 

  3. 1 try 2 { 3     // try block  4 } 5 except ( FilterFunction(GetExceptionCode() ) 6 { 7     // exception handler block 8 }  
  • SEH使用 RaiseException函数来引发异常
    1 VOID RaiseException(2     DWORD dwExceptionCode,3     DWORD dwExceptionFlags,4     DWORD nNumberOfArguments,5     CONST ULONG_PTR* pArguments);

C++异常处理

C++也实现了异常处理机制,即我们常见的try-catch语句

1 try  2 {   3     包含可能抛出异常的语句;   4 }   5 catch(类型名[形参名])    // 捕获特定类型的异常   6 {   7   8 }   9 catch(类型名[形参名])    // 捕获特定类型的异常  10 {  11  12 }  13 catch(...)    // 三个点则表示捕获所有类型的异常  14 { 15  16 }

在有些情况下,我们捕获异常并不是为了对这个异常进行处理,而是修改异常的信息,然后将之抛出。C++专门提供了这样的操作方式,即我们可以直接使用throw关键字,后面无需跟随具体错误对象,这样可以将捕捉到的异常抛出。

1 int func(int x, int y)  //定义函数   2 {   3     if(y == 0)   4     {   5         throw y;  //除数为0,抛出异常   6     }   7     return x/y;  //否则返回两个数的商   8 }   9  10 void main()  11 {  12     int res;  13     try  //定义异常  14     {  15         res = func(2, 3);  16         cout << "The result of x/y is : " << res << endl;  17         res = func(6, 0);  //出现异常,函数内部会抛出异常     18     }  19     catch(int)  //捕获并处理异常  20     {  21          cerr << "error of dividing zero.\n";  22          exit(1);  //异常退出程序  23     }  24 }

   使用筛选器处理异常。当发生异常时,比如内存访问违规时,CPU硬件会发现此问题,并产生一个异常(你可以把它理解为中断),然后CPU会把代码流程切换到异常处理服务例程。操作系统异常处理服务例程会查看当前进程是否处于调试状态,如果是则通知调试器发生了异常,如果不是则操作系统会查看当前线程是否安装了异常帧链(FS[0]),如果安装了SEH(try.... catch....),则调用SEH,并根据返回结果决定是否全局展开或局部展开。如果异常链中所有的SEH都没有处理此异常,而且此进程还处于调试状态,则操作系统会再次通知调试器发生异常(二次异常)。如果还没人处理,则调用操作系统的默认异常处理代码UnhandledExceptionHandler,不过操作系统允许你Hook这个函数,就是通过SetUnhandledExceptionFilter函数来设置。大部分异常通过此种方法都能捕获,不过栈溢出、覆盖的有可能捕获不到。

1 LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(2   LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter3 );

  参数是一个函数指针,定义如下:

typedef LONG (*PTOP_LEVEL_EXCEPTION_FILTER)(STRUCT _EXCEPTION_POINTERS *ExceptionInfo );typedef PTOP_LEVEL_EXCEPTION_FILTER LPTOP_LEVEL_EXCEPTION_FILTER;

  返回值有三种:

EXCEPTION_EXECUTE_HANDLER:表明异常处理完毕,程序可以退出。

EXCEPTION_CONTINUE_EXECUTION:忽略此异常,从异常点继续运行。如果此时再发生异常,还会调用异常处理函数。

EXCEPTION_CONTINUE_SEARCH:异常没被识别,交由上一级处理函数处理。

 MiniDump文件

有了强大的SetUnhandledExceptionFilter 这个Windows API,就使得记录异常快照变得可行。只需要在通过这个API注册的异常处理函数中记录下当前上下文信息,就可以通过这些信息来分析并弥补程序上的缺陷。通常,在异常处理函数中生成MiniDump文件来供我们对异常进行分析和处理。

Microsoft提供了一个API函数,用以生成MiniDump:MiniDumpWriteDump,该API需要加载dbghelp.dll模块,并在dbghelp.h头文件里声明。

1 BOOL WINAPI MiniDumpWriteDump(2   _In_  HANDLE hProcess,3   _In_  DWORD ProcessId,4   _In_  HANDLE hFile,5   _In_  MINIDUMP_TYPE DumpType,6   _In_  PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,7   _In_  PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,8   _In_  PMINIDUMP_CALLBACK_INFORMATION CallbackParam9 );

有了MiniDump文件以及Release编译时生成的pdb文件,我们就可以通过VS或WinDbg等工具来分析和调试异常代码了。

转载于:https://www.cnblogs.com/biyaxiong/archive/2013/03/28/2986777.html

你可能感兴趣的文章
POJ 1141 Brackets Sequence
查看>>
Ubuntu 18.04 root 使用ssh密钥远程登陆
查看>>
Servlet和JSP的异同。
查看>>
虚拟机centOs Linux与Windows之间的文件传输
查看>>
ethereum(以太坊)(二)--合约中属性和行为的访问权限
查看>>
IOS内存管理
查看>>
middle
查看>>
[Bzoj1009][HNOI2008]GT考试(动态规划)
查看>>
Blob(二进制)、byte[]、long、date之间的类型转换
查看>>
OO第一次总结博客
查看>>
day7
查看>>
iphone移动端踩坑
查看>>
vs无法加载项目
查看>>
Beanutils基本用法
查看>>
玉伯的一道课后题题解(关于 IEEE 754 双精度浮点型精度损失)
查看>>
《BI那点儿事》数据流转换——百分比抽样、行抽样
查看>>
哈希(1) hash的基本知识回顾
查看>>
Leetcode 6——ZigZag Conversion
查看>>
dockerfile_nginx+PHP+mongo数据库_完美搭建
查看>>
Http协议的学习
查看>>