结束处理程序——Windows核心编程学习手札之二十三
结束处理程序
——Windows核心编程学习手札之二十三
使用SEH可以只关注程序要完成任务,而运行中发生的错误,系统将会发现并通知。Windows引入SHE是为了便于操作系统的开发,使用SHE所造成的负担主要由编译程序来承担,而不是由操作系统承担。当异常块(exception block)出现时,编译程序要生成特殊的代码,产生一些表(table)来支持处理SHE的数据结构,提供回调(callback)函数,操作系统可以调用这些函数,保证异常块被处理。编译程序还负责准备栈结构和其他内部信息,供操作系统使用和参考。在编译程序中增加SHE支持,不同的编译商会以不同的方式实现SEH。SHE实际包含两个主要功能:结束处理(termination handling)和异常处理(exception handling)。
一个结束处理程序能够确保去调用和执行一个代码块(结束处理程序,termination handling),而不管另外一段代码(保护体,guarded body)是如何退出,结束处理程序的文法结构如下:
__try{
//guarded body
。。。
}
__finally{
//termination handler
。。。
}
__try和__finally关键字用来标出处理程序两段代码的轮廓,操作系统和编译程序共同确保结束处理程序中的__finally代码块能够被执行,不管保护体(try块)是如何退出的,不论保护体中使用return,还是goto,或者是longjump,结束处理程序(finally块)都将被调用。
尽管结束处理程序可以捕捉try块过早退出的大多数情况,但当线程或进程被结束时,却不能引起finally块中的代码执行。当调用ExitThread或ExitProcess时,将立即结束线程或进程,而不会执行finally块中的任何代码,另外,由于某个程序调用terminateThread或terminateProcess,线程或进程将死掉,finally块中的代码也不执行。某些C运行期函数(如abort)要调用ExitProcess,也使finally块中的代码不能执行,虽然没有办法阻止其他程序结束线程或进程,但可以避免过早调用ExitThread和ExitProcess,以执行finally块中代码。最好将return、continue、break、goto等语句从结束处理程序的try块和finally块中移出去,放在结束处理程序的外面,这样使编译程序产生较小的代码,因为不需要再捕捉try块中的过早退出,也使编译程序产生更快的代码(因为执行局部展开的指令也不少),另外代码更容易阅读和维护。
为避免在try块中使用return语句,microsoft在C/C++编译程序中增加了__leave关键字。在try块中使用__leave关键字会引起跳转到try块的结尾,即跳转到try块的右大括号,由于控制流自然地从try块中退出并进入finally块,不产生系统开销,不过需要增加BOOL型变量来指示函数的成功或失败,当然这个代价是很小的。按照这个方式利用结束处理程序来设计函数时,需要在进入try块之前,将所有资源句柄初始化为无效的值,然后在finally块中查看那些资源被成功的分配,就可以知道那些要释放。另外一种确定需要释放资源的办法是对成功分配的资源设置一个标志,然后在finally块中的代码检查标志的状态,来确定资源是否被释放。
区分强制执行finally块的情况:
1)从try块中进入finally块的正常控制流;
2)局部展开:从try块中过早退出(goto、longjump、contiune、break、return等)强制控制转移到finally块,由于系统开销比较大,尽量避免在try块中过早退出,可使用__leave代替return;
3)全局展开(global unwind),发生时没有明显标识,如引起一个内存访问违规(memory access violation),一个全局展就会在finally块执行。
为了确定是那种情况引起finally块执行,可调用内部函数(或内蕴函数,intrinsic function)Abnormal Termination:
BOOL AbnormalTermination();
这个内部函数只在finally块中调用,返回一个Boolean值,指出与finally块相结合的try块是否过早退出,如果控制流离开try块并自然进入finally块,则返回FALSE;如果控制流非正常退出try块(由于goto/return/break/contiune语句引起的局部展开),或由于内部访问违规或其他异常引起的全局展开,将返回TRUE,这里没发区别finally块的执行是由于局部展开还是全局展开。
内部函数是编译程序识别的一种特殊函数,编译程序为内部函数产生内联(inline)代码而不是生成调用函数的代码,例如,memcpy是一个内部函数(如果指定/Oi编译程序开关)当编译程序看到一个对memcpy的调用,直接将memcpy的代码插入调用memcpy的 函数中,而不是生成一个对memcpy函数的调用,其作用是代码的长度增加了,但执行速度加快了。
利用结束处理程序的理由:
1)简单错误处理,所有清理工作在一个位置并且保证被执行;
2)提高程序的可读性;
3)使代码更加容易维护;
4)如果使用得当,具有最小的系统开销。
结束处理程序——Windows核心编程学习手札之二十三相关推荐
- 异常处理程序和软件异常——Windows核心编程学习手札之二十四
异常处理程序和软件异常 --Windows核心编程学习手札之二十四 CPU负责捕捉无效内存访问和用0除一个数值这种错误,并相应引发一个异常作为对错误的反应,CPU引发的异常称为硬件异常(hardwar ...
- 未处理异常和C++异常——Windows核心编程学习手札之二十五
未处理异常和C++异常 --Windows核心编程学习手札之二十五 当一个异常过滤器返回EXCEPTION_CONTINUE_SEARCH标识符时是告诉系统继续上溯调用树,寻找另外的异常过滤器,但当每 ...
- 窗口消息——Windows核心编程学习手札之二十六
窗口消息 --Windows核心编程学习手札之二十六 Windows允许一个进程至多建立10000个不同类型的用户对象(user object):图符.光标.窗口类.菜单.加速键表等,当一个线程调用一 ...
- 插入DLL和挂接API——Windows核心编程学习手札之二十二
插入DLL和挂接API --Windows核心编程学习手札之二十二 如下情况,可能要打破进程的界限,访问另一个进程的地址空间: 1)为另一个进程创建的窗口建立子类时: 2)需要调试帮助时,如需要确定另 ...
- 线程本地存储器——Windows核心编程学习手札之二十一
线程本地存储器 --Windows核心编程学习手札之二十一 C/C++运行期库使用线程本地存储器,运行期库是在多线程应用程序出现前设计的,因此运行期库里的大多数函数是用于单线程应用程序的.函数strt ...
- DLL的高级操作技术——Windows核心编程学习手札之二十
DLL的高级操作技术 --Windows核心编程学习手札之二十 显示加载DLL模块: HINSTANCE LoadLibrary(PCTSTR pszDLLPathName); HINSTANCE L ...
- Unicode——Windows核心编程学习手札之二
Unicode --Windows核心编程学习手札之二 处理软件本地化的核心在于处理不同的字符集.文本串一直作为一系列单字节字符进行编码,并在结尾处放上一个零,当调用strlen函数时,获取以/0结尾 ...
- 线程与内核对象的同步——Windows核心编程学习手札之九
线程与内核对象的同步 --Windows核心编程学习手札之九 用户方式下的线程同步机制具有速度快的特点,但有其局限性,对于许多应用程序来说,并不合适.例如,互锁函数家族只能在单值上运行,根本无法使线程 ...
- 内核对象——Windows核心编程学习手札系列之三
内核对象 --Windows核心编程学习手札系列之三 内核对象可供系统和应用程序使用来管理各种各样的资源,如进程.线程.文件等,是内核分配的一个内存块,只能又内核访问,该内存块是一种数据结构,它的成员 ...
最新文章
- 2019牛客暑期多校训练营(第三场)
- 宁做程序员,不做 CTO!估值 50 亿美元公司的创始人只想专注编程
- TWaver版3D化学元素周期表
- 苹果应用ipa图片提取
- c语言 集中上机题目,C语言集中上机题目.doc
- Hashtable Dictionary的使用
- 数据结构排序系列详解之七 归并排序
- 在继续之前,如何暂停我的Shell脚本一秒钟?
- PyTorch学习—11.权值初始化
- 网络工程师 名词解释
- dos下\与/有什么区别
- java for冒号_浅谈对Java双冒号::的理解
- port isolate enable命令
- 解决windows下蓝牙设备将休眠中的pc唤醒的困扰
- 建立蜜蜂需求模型过程中参考的网站:
- 【分块】【Violet】蒲公英
- django3.2连接虚拟机里的openGauss
- 每天学一点英文:Espresso 20210813
- 机车出入库相关、调车转线、、后期杂谈
- [Java版]Selenium系列:TestNG框架实现数据驱动DataProvider