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

本文将全面阐述__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

原文地址:https://www.cnblogs.com/yilang/p/11238201.html

时间: 2024-11-19 01:40:45

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

结构化异常(SEH)简述

/********************************************************************* * Author  : Samson * Date    : 08/04/2014 * Test platform: *              Linux ubuntu 3.2.0-58-generic-pae *              GNU bash, version 4.2.39 * *****************************

红黑树详细介绍二

删除 RB-TRANSPLANT(T,u,v)函数是将u子树用v来代替,在替换的时候分为了三种情况,如果u就是root结点则直接替换u,如果树里面还包含有其它结点,则将u的左右子树转移到v的左右子树上面. RB-TRANSPLANT(T,u,v) if u.p == T.nil T.root = v else if u == u.p.left u.p.left = v else u.p.right = v v.p = u.p 删除代码 RB-DELETE(T,z) y = z y-origina

解析结构化异常处理(SEH)(第二部分)

书接上一篇:http://www.cnblogs.com/ONDragon/p/6855174.html 虽然这个异常回调机制很好,但它并不是一个完美的解决方案.对于稍微复杂一些的应用程序来说,仅用一个 函数就能处理程序中任何地方都可能发生的异常是相当困难的.一个更实用的方案应该是有多个异常处理例程,每个例程针对程序中的一部分.实际上,操作系统提 供的正是这个功能. 还 记得系统用来查找异常回调函数的EXCEPTION_REGISTRATION结构吗?这个结构的第一个成员,称为prev,前面我们

SEH 结构化异常

Structured Exception Handling是windows特有的异常处理,SEH包含了两个方面的功能: ? 终止处理 ? 异常处理 终止处理: 终止处理确保无论被保护代码是如何退出的,另一个代码块总能被调用和执行: __try { //Guarded Body - } __finally { //Termination handler - } 无论在被保护代码中使用了return还是goto,亦或者longjump(除非调用了ExitProcess, EixtThread, Te

Varnish缓存机制详细介绍及简单配置

Varnish是一款高性能的开源HTTP加速器,其主要用来做为反向代理中的缓存服务器使用,但其实Varnish本身也是具有反向代理功能的,但在创建连接和维持连接上,与Nginx相比差距很大,现在有一个很流行的架构就是前端用Nginx作为反向代理,后面加Varnish缓存服务器为Web服务加速 在将Varnish前先谈谈我们的浏览器缓存机制,现在的浏览器基本都具有缓存功能,它能将我们以前访问过的静态内容和可进行缓存的动态内容缓存再本地,而后在下次访问相同资源时,如果可以确认Server端的资源未发

hibernate缓存机制详细介绍

hibernate的缓存机制,包括一级缓存(session级别).二级缓存(sessionFactory级别). 一:hibernate的 N+1问题 list()获得对象: 如果通过list()方法来获得对象,毫无疑问,hibernate会发出一条sql语句,将所有的对象查询出来,这点相信大家都能理解 Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.rid as rid2_, student

shell 脚本基础知识详细介绍(二)

SimonSu 博客 评论墙 读者墙 链接 关于 linux中的sh脚本语法 玩linux都知道sh脚本的方便,但如何编写sh脚本却是像我这样的新手和菜鸟的难题.能不能编写得出来是一回事,了不了解又是另一回事.抱着好好学习,天天向上的精神,大家有空就看看吧,没有坏处的.下面来抄来一大段sh脚本的语法说明.慢慢看: 一.开头程序必须以下面的行开始(必须方在文件的第一行):#!/bin/sh符号#!用来告诉系统它后面的参数是用来执行该文件的程序.在这个例子中我们使用/bin/sh来执行程序.当编写脚

深入研究 Win32 结构化异常处理(作者博客有许多SEH的研究文章)

摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的最基本概念讲起. Matt Pietrek 著董岩 译Victor 转载自 Xfocus 并整理 在所有 Win32 操作系统提供的机制中,使用最广泛的未公开的机制恐怕就要数结构化异常处理(structured exception handling,SEH)了.一提到结构化异常处理,可能就会令人想起

第23章 SEH结构化异常处理(2)

23.2 编译器层面对系统SEH机制的封装 23.2.1 扩展的EXCEPTION_REGISTRATION级相关结构:VC_EXCEPTION_REGISTRATION (1)VC_EXCEPTION_REGISTRATION结构 struct VC_EXCEPTION_REGISTRATION { VC_EXCEPTION_REGISTRATION* prev; //前一个结构体的指针 FARPROC handler; //永远指向_exception_handler4回调函数 scopet