本文将全面阐述__try,__except,__finally,__leave异常模型机制,它也即是Windows系列操作系统平台上提供的SEH模型。SEH实际包含两个主要功能:结束处理(termination handling)和异常处理(exception handling)。每当你建立一个try块,它必须跟随一个finally块或一个except块。一个try 块之后不能既有finally块又有except块。但可以在try - except块中嵌套try - finally块,反过来也可以。__try  __finally关键字用来标出结束处理程序两段代码的轮廓。不管保护体(try块)是如何退出的。不论你在保护体中使用return,还是goto,或者是longjump,结束处理程序(finally块)都将被调用。在try使用__leave关键字会引起跳转到try块的结尾。

一、try/except框架

SEH的异常处理模型主要由try-except语句来完成,它与标准C++所定义的异常处理模型非常类似,也都是可以定义出受监控的代码模块,以及定义异常处理模块等。代码框架如下:

__try{

   //被保护的代码块
……
} __except(except fileter/*异常过滤程序*/){
//异常处理程序
}

在一个函数中,可以有多个try-except语句。它们可以是一个平面的线性结构,也可以是分层的嵌套结构。为了与C++异常处理模型相区别,VC编译器对关键字做了少许变动。首先是在每个关键字加上两个下划线作为前缀,这样既保持了语义上的一致性,另外也尽最大可能来避免了关键字的有可能造成名字冲突而引起的麻烦等;其次,C++异常处理模型是使用catch关键字来定义异常处理模块,而SEH是采用__except关键字来定义。并且,catch关键字后面往往好像接受一个函数参数一样,可以是各种类型的异常数据对象;但是__except关键字则不同,它后面跟的却是一个表达式(可以是各种类型的表达式)。从这个框架我们可以看到由三块组成:__try块,__except过滤表达式和异常处理块。

①当__try块中的代码发生异常时,__except()中的过滤程序就被调用。

②过滤程序可以是一个简单的表达式或一个函数(返回值应为EXCEPTION_CONTINUE_SEARCH、EXCEPT_CONTINUE_EXECUTE或EXCEPT_EXECUTE_HANDLER之一)

当某个__try块中的代码触发了异常时(也可能是__try块中调用的函数中引发异常),操作系统会从最靠近引发异常代码的地方开始从下层往上层查找__except块(这里的层是指__try块的嵌套层),对于找到的每一个__except块,会先计算它的异常过滤器,如果过滤器返回EXCEPTION_CONTINUE_SEARCH,则说明此__except块不处理此类异常,需要继续往上层查找,如果某过滤器返回EXCEPTION_EXECUTE_HANDLER则说明此__except块可以处理此类异常,即找到了异常的处理代码,此时停止查找,但是在执行该__except块中的异常处理代码之前,要先进行全局展开。全局展开的过程与查找__except块的过程类似,只不过这次是查找从底层向上查找__finally块,查找过程中遇到的每一个__finally块中的代码都被执行,直到查找到前面说的处理异常的__except块那一层停止,这时全局展开完成,然后执行__except块中的异常处理代码。执行完异常处理代码之后,指令流从__except块后的第一条指令开始。从这里也可以看出全局展开也是为了保证__finally语义的正确性,因为指令流从引发异常代码转到到__except异常处理代码时也导致了指令流从__try块嵌套层中所有与__finally对应的__try块中流出,由前面的__finally语义说明可知,此时必须要执行全局展开过程以包成__finally语义的正确性。

③过滤表达式中可以调用GetExceptionCode和GetExceptionInformation函数取得正在处理的异常信息。但这两个函数不能在异常处理程序中使用。GetExceptionCode是个内联函数,其代码直接嵌入到被调用的地方(注意与函数调用的区别),它的返回值表明刚刚发生的异常的类型(定义在WinBase.h中,如EXCEPTION_ACCESS_VIOLATION)。GetExceptionInformation可获取异常发生时,系统向发生异常的线程栈中压入的EXCEPTION_RECORD、CONTEXT和EXCEPTION_POINTERS结构中的异常信息或CPU有关的信息

④与try/finally不同,try/except中可以使用return、goto、continue和break,它们并不会导致局部展开。

二、try/finally框架

SEH的异常结束处理模型主要由try-finally语句来完成。终止处理就是保证应用程序在一段被保护的代码发生中断后(无论是异常还是其他)还能够执行清理工作,清理工作包括关闭文件、清理内存等。代码框架如下:

__try{

   //被保护的代码块
……
}
__finally{ //终止处理 }

__try/__finally的特点

①finally块总是保证,无论__try块中的代码有无异常,finally块总是被调用执行。

②try块后面只能跟一个finally块或except块,要跟多个时只能用嵌套,但__finally块不可以再嵌套SEH块,except块中可以嵌套SEH块。

③利用try/finally可以使代码的逻辑更清楚,在try块中完成正常的逻辑,finally块中完成清理工作,使代码可读性更强,更容易维护。

④当指令从__try块底部自然流出时,会执行finally块

⑤局部展开时:从try块中提前退出(由goto、longjump、continue、break、return等语句引发)将程序控制流强制转入finally块,这时就会进行局部展开(但ExitProcess、ExitThread、TerminateProcess、TerminateThread等原因导致的提前离开除外,因为这会直接终止线/进程,而不能展开)。说白了,局部展开就是将__finally块的代码提前到上述那几种语句之前执行。

⑥全局展开时也会引发finally块的执行。从Vista开始,须显示保护try/finally框架,以确保抛出异常时finally会被执行。即try/finally块外面的某层要使用try/except块保护且except中的过滤函数要返回EXCEPTION_EXECUTE_HANDLER。Vista以前的Windows,会在线程的入口点处用try/except加以保护,但Vista为了提高Windows错误报告(WER)记录的可靠性,将这个入口点的异常过滤程序返回为EXCEPTION_CONTINUE_SEARCH,最后进程会被终止,从而导致finally块没有机会被执行。(关于全局展开见第24章的相关的部分)。

⑦如果异常发生在异常过滤程序里,终止处理程序也不会被执行。

⑧finally块被执行的原因总是由以上三种情况之一引起。可以调用AbnormalTermination函数来查看原因。该函数是个内联函数,当正常流出时会返回FALSE,局部或全局展开时返回TRUE。

三、__leave关键字

①该关键字只能用在try/finally框架中,它会导致代码执行控制流跳转到到try块的结尾,也可以认为是try后的闭花括号处。

②这种情况下,代码执行是正常从try块进入finally,所以不会进行局部展开。

③但一般需定义一个布尔变量,指令离开try块时,函数执行的结果是成功还是失败,然后在finally块中可根据这个(或这些)变量以决定资源是否需要释放。

参考:

https://www.cnblogs.com/5iedu/p/5228946.html

结构化异常SEH处理机制详细介绍(二)的更多相关文章

  1. 结构化异常SEH处理机制详细介绍(一)

    结构化异常处理(SEH)是Windows操作系统提供的强大异常处理功能.而Visual C++中的__try{}/__finally{}和__try{}/__except{}结构本质上是对Window ...

  2. [C++]深入解析结构化异常处理(SEH)

    http://www.cppblog.com/weiym/archive/2015/02/27/209884.html 尽管以前写过一篇SEH相关的文章<关于SEH的简单总结>, 但那真的 ...

  3. Windows 结构化异常

    结构化异常不能用于需要调用对象析构函数的函数中 __try{ } __except(){ } __try{ } __finally{ }

  4. Session机制详细介绍

    Session机制详细介绍  

  5. vb.net结构化异常处理和“邪用”

    vb.net中的错误处理包括两种:非结构化异常处理技术和结构化异常处理.非结构化异常处理技术在vb 6.0中使用的比较普遍,即通过Err对象和ON Error.Go To.Resume等语句来实现.这 ...

  6. SQLite中的WAL机制详细介绍-与回滚日志原理

    一.什么是WAL? WAL的全称是Write Ahead Logging,它是很多数据库中用于实现原子事务的一种机制,SQLite在3.7.0版本引入了该特性. 二.WAL如何工作? 在引入WAL机制 ...

  7. hibernate缓存机制详细介绍

    hibernate的缓存机制,包括一级缓存(session级别).二级缓存(sessionFactory级别). 一:hibernate的 N+1问题 list()获得对象: 如果通过list()方法 ...

  8. Windows结构化异常

    不错的总结: http://hi.baidu.com/wangxvfeng101/item/518f6efdab4e5616ff35820e http://www.vckbase.com/index. ...

  9. 深入解析结构化异常处理(SEH)

    jpg 改 rar

随机推荐

  1. AX导出excel设置格式

    今天在AX2009里面写一个导出EXCEL,没有模版,这是第一次碰到,之后写完之后发现导出的数据格式不对. 到处取经之后得到一下结果: 定义一个 Com   range; SysExcelCells  ...

  2. promethus监控mysql

    一.mysqld_exporter安装 下载页面 https://github.com/prometheus/mysqld_exporter/releases 下载最新版本 https://githu ...

  3. C盘清理、C盘瘦身、省出30G

    三招C盘瘦身30G,清理win10系统中虚占C盘空间的三大祸害 1.对C盘进行“磁盘清理” C盘右键->属性->磁盘清理->清理系统文件->勾选“windows更新清理”-&g ...

  4. 前端开发 Vue Vue.js和Nodejs的关系

    首先vue.js 是库,不是框架,不是框架,不是框架. Vue.js 使用了基于 HTML 的模版语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据. Vue.js 的核心是一个允许你 ...

  5. Oracle 创建与管理用户

    标识用户是 Oracle 数据库管理的基本要求之一,每一个能够连接到数据库的用户都必须是系统的合法用户.用户想要使用 Oracle 的系统资源,就必须拥有相应的权限. 一.身份验证 Oracle 为用 ...

  6. Python学习文档指引

    Python文档资源: 形式 角色 #注释 文件中的文档 dir函数 对象中可用属性的列表 文档字符串:__doc__ 附加在对象上的文件中的文档 PyDoc:help函数 对象的交互帮助 PyDoc ...

  7. shell 字符串截取表达式

    ${var#str} 从左向右匹配,非贪婪匹配,截取并保留右边的内容 txt='123456abc123456' echo ${txt#*34} # 56abc123456 ${var##str} 从 ...

  8. Tomcat启动时一闪而过,看不多错误信息解决方案

    转自:https://wangxh89.iteye.com/blog/1824806 有时Tomcat的启动窗口一闪而过,根本就看不出启动过程中发生了什么错误.这中间的原因有好多种,最常见的解决办法就 ...

  9. Discuz!数据库操作DB类和C::t类介绍

    类定义文件 DB类: 文件\source\class\class_core.php class DB extends discuz_database {} discuz_database类定义 文件\ ...

  10. Python学习日记(十七) os模块和sys模块

    os模块 1.os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 import os print(os.getcwd()) #C:\Users\Administrator\ ...