第23章 SEH结构化异常处理(3)_终止处理程序
23.3 终止处理程序
23.3.1 程序的结构
(1)框架
- __try{
- //被保护的代码块
- ……
- }
- __finally{
- //终止处理
- }
(2)__try/__finally的特点
①finally块总是保证,无论__try块中的代码有无异常,finally块总是被调用执行。
②try块后面只能跟一个finally块或except块,要跟多个时只能用嵌套,但__finally块不可以再嵌套SEH块,except块中可以嵌套SEH块。
③利用try/finally可以使代码的逻辑更清楚,在try块中完成正常的逻辑,finally块中完成清理工作,使代码可读性更强,更容易维护。
(3)__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。
23.3.2 __leave关键字
①该关键字只能用在try/finally框架中,它会导致代码执行控制流跳转到到try块的结尾,也可以认为是try后的闭花括号处。
②这种情况下,代码执行是正常从try块进入finally,所以不会进行局部展开。
③但一般需定义一个布尔变量,指令离开try块时,函数执行的结果是成功还是失败,然后在finally块中可根据这个(或这些)变量以决定资源是否需要释放。
23.3.3 局部展开实例分析
- //1、Funcenstein2:return引起的局部展开
- DWORD Funcenstein2(){
- DWORD dwTemp;
- //1.处理一些其他事情
- //...
- _try{
- //2.
- dwTemp = ;
- //返回一个新的值。因执行return会提前结束try块会导致
- //局部展开,此时会先保存dwTemp,然后局部展开,执行finally,
- //最后返回try块来执行return。即,将finally提前到return之前执行!
- return (dwTemp);
- }
- __finally{
- //3.清除工作,这里简单输出一行文本
- printf("Finally块被执行!\n");
- }
- //继续处理——以下这些代码将永远不会被执行
- dwTemp = ;
- printf("dwTemp = %d\n", dwTemp);
- }
- //2、Funcenstein3:goto引起的局部展开
- DWORD Funcenstein3(){
- DWORD dwTemp;
- //1.处理一些其他事情
- //...
- _try{
- //2.
- dwTemp = ;
- //试图利用goto跳过finally。这也会导致局部展开。
- //当遇到goto时,会先局部展开,先执行finally,
- //最后返回try块来执行goto语句。即,将finally提前到goto之前执行!
- goto ReturnValue;
- }
- __finally{
- //3.清除工作,这里简单输出一行文本
- printf("Finally块被执行!\n");
- }
- //继续处理——以下这些代码将永远不会被执行
- dwTemp = ; //该行会被跳过
- ReturnValue:
- printf("dwTemp = %d\n", dwTemp);
- return (dwTemp);
- }
- //3.FuncaDoodleDoo:Continue、Break引起的局部展开
- DWORD FuncaDoodleDoo(){
- DWORD dwTemp = ;
- while (dwTemp <){
- __try{
- //第2次循环时,dwTemp会等于2。由于continue会导致局部展开,
- //所以在执行continue前先执行finally块,dwTemp增到3
- //然后continue,进行第3次循环
- if (dwTemp == )
- continue;
- //第3次循环,这时dwTemp等于3。由于break会导致提前离开try块
- //所以进行局部展开,在break前先调用finally块,因此dwTemp增
- //到4,然后break跳出循环,所以finally块外面的dwTemp++没被执行
- if (dwTemp == )
- break;
- }
- __finally{
- dwTemp++;
- }
- dwTemp++;
- }
- dwTemp += ;
- return (dwTemp);
- }
- //4.Funcenstein4函数:finally块内执行return
- DWORD Funcenstein4(){
- DWORD dwTemp;
- //1.处理一些其他事情
- //...
- _try{
- //2.
- dwTemp = ;
- //返回一个新的值。因执行return会提前结束try块会导致
- //局部展开,此时会先保存dwTemp,然后局部展开,执行finally,
- //最后返回try块来执行return。即,将finally提前到return之前执行!
- return (dwTemp);
- }
- __finally{
- //3.清除工作,这里简单输出一行文本
- printf("Finally块被执行!\n");
- //return (103); //在finally块中提前return这种行为是未定义行为。
- //VC编译器直接报错。但有些编译器会让通过。
- //此时在局部展开时,因try块时将返回值复制到一个
- //临时变量,在等待这里的finally块返回。但因finally
- //里面又要return,所以会将103写入那个临时变量。然后
- //函数退出!注意,不再回到try块里了。
- }
- //继续处理——以下这些代码将永远不会被执行
- dwTemp = ;
- printf("dwTemp = %d\n", dwTemp);
- return (dwTemp);
- }
【SEHTerm程序】
//SEHTerm.cpp
- /************************************************************************
- Module: SEHTerm.cpp
- Notices:Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
- ************************************************************************/
- //#include "../../CommonFiles/CmnHdr.h"
- #include <windows.h>
- #include <tchar.h>
- //////////////////////////////////////////////////////////////////////////
- //判断操作系统是否是Vista及以上版本
- BOOL IsWindowsVistaAbove(){
- //第4章的代码
- //准备一个OSVERSIONINFOEX结构
- OSVERSIONINFOEX osver = { };
- osver.dwOSVersionInfoSize = sizeof(osver);
- osver.dwMajorVersion = ;
- osver.dwMinorVersion = ;
- osver.dwPlatformId = VER_PLATFORM_WIN32_NT;
- //准备条件掩码
- DWORDLONG dwlConditionMask = ; //必须初始化为0
- VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); //Vista及以上
- VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL);
- VER_SET_CONDITION(dwlConditionMask, VER_PLATFORMID, VER_EQUAL);
- //测试,返回TRUE时表示正好是Vista,否则不是Vista
- return VerifyVersionInfo(&osver, VER_MAJORVERSION | VER_MINORVERSION |
- VER_PLATFORMID, dwlConditionMask);
- }
- //////////////////////////////////////////////////////////////////////////
- void TriggerException(){
- __try{
- int n;
- int n = MessageBox(NULL, TEXT("执行非法内存访问吗?"),
- TEXT("SEHTerm:在try块内"), MB_YESNO);
- if (n == IDYES){
- *(PBYTE)NULL = ; //这将引发非法访问内存
- }
- }
- __finally{
- PCTSTR psz = AbnormalTermination() ?TEXT("异常结束") : TEXT("正常结束");
- MessageBox(NULL, psz, TEXT("SEHTerm:在finally块内"), MB_OK);
- }
- MessageBox(NULL, TEXT("函数正常结束!"),
- TEXT("SEHTerm:在finally块之后"), MB_OK);
- }
- //////////////////////////////////////////////////////////////////////////
- int WINAPI _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd){
- //在Vista里,如果过滤函数返回EXCEPTION_EXECUTE_HANDLER时,会引发
- //全局展开。如果有一个未处理异常发生,进程会简单地结束从而导致
- //finally块不会被执行。
- if (IsWindowsVistaAbove()){
- DWORD n = MessageBox(NULL, TEXT("要用try/except保护吗?"),
- TEXT("SEHTerm:工作流"), MB_YESNO);
- //DWORD n = IDYES;
- if (n == IDYES){ //代码要保护
- __try{
- TriggerException();
- }
- __except (EXCEPTION_EXECUTE_HANDLER){
- //因为代码被保护,系统对话框不会显示
- //所以弹出一个消息框
- MessageBox(NULL, TEXT("进程异常结束"),
- TEXT("main函数try/except处理器"), MB_OK);
- //进程退出码
- return (-);
- }
- } else{ //代码不保护
- TriggerException();
- }
- } else{
- TriggerException();//其中系统默认是保护的。
- }
- MessageBox(NULL, TEXT("进程正常结束"),
- TEXT("SEHTerm:即将退出主线程!"),MB_OK);
- return ;
- }
//resource.h
- //{{NO_DEPENDENCIES}}
- // Microsoft Visual C++ 生成的包含文件。
- // 供 23_SEHTerm.rc 使用
- //
- #define IDI_SEHTERM 101
- // Next default values for new objects
- //
- #ifdef APSTUDIO_INVOKED
- #ifndef APSTUDIO_READONLY_SYMBOLS
- #define _APS_NEXT_RESOURCE_VALUE 102
- #define _APS_NEXT_COMMAND_VALUE 40001
- #define _APS_NEXT_CONTROL_VALUE 1001
- #define _APS_NEXT_SYMED_VALUE 101
- #endif
- #endif
//SEHTerm.rc
- // Microsoft Visual C++ generated resource script.
- //
- #include "resource.h"
- #define APSTUDIO_READONLY_SYMBOLS
- /////////////////////////////////////////////////////////////////////////////
- //
- // Generated from the TEXTINCLUDE 2 resource.
- //
- #include "winres.h"
- /////////////////////////////////////////////////////////////////////////////
- #undef APSTUDIO_READONLY_SYMBOLS
- /////////////////////////////////////////////////////////////////////////////
- // 中文(简体,中国) resources
- #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
- LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
- #ifdef APSTUDIO_INVOKED
- /////////////////////////////////////////////////////////////////////////////
- //
- // TEXTINCLUDE
- //
- TEXTINCLUDE
- BEGIN
- "resource.h\0"
- END
- TEXTINCLUDE
- BEGIN
- "#include ""winres.h""\r\n"
- "\0"
- END
- TEXTINCLUDE
- BEGIN
- "\r\n"
- "\0"
- END
- #endif // APSTUDIO_INVOKED
- /////////////////////////////////////////////////////////////////////////////
- //
- // Icon
- //
- // Icon with lowest ID value placed first to ensure application icon
- // remains consistent on all systems.
- IDI_SEHTERM ICON "SEHTerm.ico"
- #endif // 中文(简体,中国) resources
- /////////////////////////////////////////////////////////////////////////////
- #ifndef APSTUDIO_INVOKED
- /////////////////////////////////////////////////////////////////////////////
- //
- // Generated from the TEXTINCLUDE 3 resource.
- //
- /////////////////////////////////////////////////////////////////////////////
- #endif // not APSTUDIO_INVOKED
第23章 SEH结构化异常处理(3)_终止处理程序的更多相关文章
- 第23章 SEH结构化异常处理(2)_编译器对系统SEH机制的封装
23.2 编译器层面对系统SEH机制的封装 23.2.1 扩展的EXCEPTION_REGISTRATION级相关结构:VC_EXCEPTION_REGISTRATION (1)VC_EXCEPTIO ...
- 第23章 SEH结构化异常处理(1)_系统SEH机制
23.1 基础知识 23.1.1 Windows下的软件异常 (1)中断和异常 ①中断是由外部硬件设备或异步事件产生的 ②异常是由内部事件产生的,可分为故障.陷阱和终止三类. (2)两种异常处理机制: ...
- 第25章 SEH结构化异常处理_未处理异常及向量化异常
25.1 UnhandledExceptionFilter函数详解 25.1.1 BaseProcessStart伪代码(Kernel32内部) void BaseProcessStart(PVOID ...
- 第24章 SEH结构化异常处理_异常处理及软件异常
24.1 程序的结构 (1)try/except框架 __try{ //被保护的代码块 …… } __except(except fileter/*异常过滤程序*/){ //异常处理程序 } (2) ...
- 异常处理第三讲,SEH(结构化异常处理),异常展开问题
异常处理第三讲,SEH(结构化异常处理),异常展开问题 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 不知道昨天有木有 ...
- Windows内核读书笔记——SEH结构化异常处理
SEH是对windows系统中的异常分发和处理机制的总称,其实现分布在很多不同的模块中. SEH提供了终结处理和异常处理两种功能. 终结处理保证终结处理块中的程序一定会被执行 __try { //要保 ...
- 深入研究 Win32 结构化异常处理(作者博客有许多SEH的研究文章)
摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的 ...
- [C++]深入解析结构化异常处理(SEH)
http://www.cppblog.com/weiym/archive/2015/02/27/209884.html 尽管以前写过一篇SEH相关的文章<关于SEH的简单总结>, 但那真的 ...
- SEH:结构化异常处理 学习
SEH:结构化异常处理 结构化异常处理机制提供了一个操作系统,用于优化结构的方案,为客户提供更强大的程序执行环境.试想一下,你写程序不用考虑内存访问错误,那里是空指针错误,一直在按照程序的逻辑结构来写 ...
随机推荐
- 如何选择RabbitMQ的消息保存方式?
RabbitMQ对于queue中的message的保存方式有两种方式:disc和ram.如果采用disc,则需要对exchange/queue/delivery mode都要设置成durable模式. ...
- [TypeScript] JSON对象转TypeScript对象范例
[TypeScript] JSON对象转TypeScript对象范例 Playground http://tinyurl.com/nv4x9ak Samples class DataTable { p ...
- JavaScript基础10——node对象
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- MySQL数据库的基本数据类型
整数类型 数值型数据类型主要用来存储数字,包含的类型有: TINYINT.SMALLINT.MEDIUMINT. INT(INTEGER). BIGINT. 下面通过一个例子来查看各种类型的所占有的数 ...
- TXT记事本转换PDF文件
使用的方式为,读取TXT记事本的内容,然后写入创建的PDF文件. static void Main(string[] args) { const string txtFile = "D:\\ ...
- SoapUI接口测试实例(webservice接口)
接口测试步骤 注:以测试queryHistoryAccepts接口作举例. 1. 用户登录获取SessionKey实体信息 注:由于大部分的接口都需要SessionKey实体的信息,因此测试那些接口都 ...
- Effective Java 06 Eliminate obsolete object references
NOTE Nulling out object references should be the exception rather than the norm. Another common sour ...
- Effective Java 14 In public classes, use accessor methods, not public fields
Principle To offer the benefits of encapsulation you should always expose private field with public ...
- "ORA-12154: TNS:could not resolve the connect identifier specified"的解决办法
添加环境变量解决: 变量名:TNS_ADMIN 变量值:D:\Ocl\product\11.2.0\dbhome_1\NETWORK\ADMIN tnsnames.ora所在的路径
- 微信开发(03)之新建按钮时报错 errcode 40054
在微信开发新建公众号的按钮时,报错如下: {errcode:40054,errmsg:"invalid sub button url domain"} 经过仔细排查,发现是url地 ...