24.1  程序的结构

(1)try/except框架

__try{

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

(2)说明

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

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

  ③过滤表达式中可以调用GetExceptionCode和GetExceptionInformation函数取得正在处理的异常信息。但这两个函数不能在异常处理程序中使用。

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

24.2 异常过滤程序

(1)返回值

标识

说明

EXCEPTION_EXECUTE_HANDLER

1

执行except花括号内代码,同时执行全局展开。最后程序从except花括号后面的第一句代码继续运行。

EXCEPTION_CONTINUE_SEARCH

0

向外层查找带except的try块,并调用对应的异常过滤程序。

EXCEPTION_CONTINUE_EXECUTE

-1

重新执行导致异常的那条CPU指令本身。

(2)全局展开——异常过滤程序返回EXCEPTION_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语义的正确性。

(3)停止全局展开——将return置于finally块中可阻止全局展开。【未定义行为,VC2013直接报错了!】

(4)慎用EXCEPTION_CONTINUE_EXECUTION

  ①尝试修复错误,出现失败的实例分析

  *pchBuffer = TEXT("J");//C/C++语句

  //编译后的产生的机器指令
MOV EAX,DWORD PTR[pchBuffer]
MOV WORD PTR[EAX], 'J' //导致异常的指令。当异常过滤程序捕获该异常后,修正
//pchBuffer,让其指向一个正确的地址。并让系统重新
//执行第二要CPU指令。问题在于寄存器不可能自动更新
//以反映变量pchBuffer的更新,于是该异常又致另一个导
//异常,程序陷入了死循环

  ②虚拟内存结合SEH可实现按需调拨存储器,有时能写出运行速度快和高效的应用程序(见第15章的《如何预订大块地址空间和为地址空间稀疏调拨存储器》

  ③系统为线程栈建了一个SEH框。当线程试图访问栈中尚未调拨存储器的区域时,会引发一个异常。系统内部的异常过滤程序会捕获到该异常并在内部调用VirtualAlloc为线程栈调拨更多的存储,并且返回EXCEPTION_CONTINUE_EXECUTION让原先抛出异常的指令重新执行下去。

【SEHAndMemory】演示虚拟内存的按需调拨

#include <windows.h>
#include <tchar.h>
#include <locale.h> #define PAGELIMIT 80
LPBYTE lpNxtPage;
DWORD dwPages = ;
DWORD dwPageSize;//页面大小,一般为4KB INT PageFualtExceptionFilter(DWORD dwCode){
LPVOID lpvResult; //不是非法访问内存
if (dwCode !=EXCEPTION_ACCESS_VIOLATION){
return EXCEPTION_EXECUTE_HANDLER;//执行except块的异常处理程序代码
} //当超过指定的页面数时
if (dwPages >=PAGELIMIT){
return EXCEPTION_EXECUTE_HANDLER;//执行except块的异常处理程序代码
} //非法访问内存,则为预订的空间提交下一页物理存储器
lpvResult = VirtualAlloc((LPVOID)lpNxtPage, dwPageSize, MEM_COMMIT, PAGE_READWRITE);
if (lpvResult == NULL){
return EXCEPTION_EXECUTE_HANDLER;//执行except块的异常处理程序代码
} //提交成功
dwPages++;
lpNxtPage += dwPageSize; _tprintf(_T("第%d页提交成功!\n"), dwPages);
return EXCEPTION_CONTINUE_EXECUTION; //重新执行触发异常的那条CPU指令
} int main(){
_tsetlocale(LC_ALL, _T("chs")); LPVOID lpvBase;LPTSTR lpPtr;BOOL bSuccess;
SYSTEM_INFO sSysInfo;
GetSystemInfo(&sSysInfo);
dwPageSize = sSysInfo.dwPageSize; _tprintf(_T("CPU页面大小为%dKB.\n"), sSysInfo.dwPageSize / ); //预订存储器
lpvBase = VirtualAlloc(NULL, PAGELIMIT*dwPageSize, MEM_RESERVE, PAGE_NOACCESS); lpPtr = (LPTSTR)(lpNxtPage = (LPBYTE)lpvBase);
for (DWORD i = ; i < PAGELIMIT*dwPageSize/sizeof(TCHAR);i++){
__try{
lpPtr[i] = _T('a');//写入一个字节的数据
}
__except (PageFualtExceptionFilter(GetExceptionCode())){
_tprintf(_T("异常被处理\n"));
//ExitProcess(GetLastError());
}
} bSuccess = VirtualFree(lpvBase, , MEM_RELEASE);
_tprintf(_T("释放操作%s.\n"), bSuccess ? _T("成功") : _T("失败"));
_tsystem(_T("PAUSE"));
return ;
}

24.3 GetExceptionCode

(1)GetExceptionCode是个内联函数,其代码直接嵌入到被调用的地方(注意与函数调用的区别),它的返回值表明刚刚发生的异常的类型(定义在WinBase.h中,如EXCEPTION_ACCESS_VIOLATION)

(2)该函数只能在异常过滤程序里(即__except之后的小括号内)或者异常处理程序的代码里调用(__except块后面的花括号内),但不能在异常过滤函数中使用。

//合法代码

__try{

y = 0;

x = / y;

}

__except ((GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO) ?

EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH){ //在__except块的小括内使用,合法

switch (GetExceptionCode()){ //__except块的花括中使用,合法!

......

}

}

//非法代码

LONG MyFilter(void){}

{

//在异常过滤函数中使用GetExceptionCode,不合法!

return ((GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO) ?

EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH);

}

__try{

y = 0;

x = 4 / y;

}

__except(MyFilter()){ //可改成将GetExceptionCode作为参数传给MyFilter的形式。

//处理异常

}

(3)异常错误代码的规则

31-30

29

28

27-16

15-0

内容

严重性

Microsoft/

Customer

保留位

设备代码

异常代码

含义

0=Success

1=Informational

2=Warning

3=error

0=Mircosoft所定义的代码

1=Customer所定义的代码

一直为0

前256个值为Micorsoft所保留。(如FACILITY_NULL(0)表示该异常可以在系统任何设备出现,并不只发生在一些特定的设备上)

由Microsoft/

Customer所定义的代码

24.4 GetExceptionInformation

(1)GetExceptionInformation可获取异常发生时,系统向发生异常的线程栈中压入的EXCEPTION_RECORD、CONTEXT和EXCEPTION_POINTERS结构中的异常信息或CPU有关的信息

(2)这个函数只能在异常过滤程序中调用(即__except块的小括号),因为EXCEPT_RECORD、CONTEXT和EXCEPTION_POINTER数据结构只有在系统计算异常过滤程序时才有效。一旦控制流被转移到其他地方,这些栈上的数据结构会被销毁。但我们可以自己保存他们,以备后用。

//保存栈中异常信息的方法
void FuncSkunk(){
//声明一些可以保存异常信息的结构体,须在try块外面声明
EXCEPTION_RECORD SavedExceptRec;
CONTEXT SavedContext; __try{ }
__except ( //注意逗号表达式,取最后一个表达式为整个表达式的值。
SavedExceptRec = *(GetExceptionInformation())->ExceptionRecord,
SavedContext = *(GetExceptionInformation())->ContextRecord,
EXCEPTION_EXECUTE_HANDLER){
//异常处理
}
}

(3)EXCEPTION_RECORD结构体——刚发生的异常的详细信息

字段

说明

DWORD ExceptionCode

异常代码,就是GetExceptionCode函数的返回值

DWORD ExceptionFlags

异常标志

0—表示继续的异常;EXCEPTION_NONCONTINUABLE—不可继续的异常,如果程序试图在一个不可继续的异常之后继续执行,会引发EXCEPTION_NONCONTINUABLE_EXCEPTION异常。

PEXCEPTION_RECORD pExceptionRecord

指向另一个未处理异常的EXCEPTION_RECORD结构。(即嵌套异常发生时,异常会形成异常链)

PVOID ExceptionAddress

导致异常的CPU指令的地址

DWORD NumberParameters

ExceptionInformation数组里元素的个数。对绝大部分的异常来说,这个值为0。

ULONG_PTR ExceptionInformation

[EXCEPTION_MAXIMUM_PARAMETERS]

描述异常的附加参数数组,对绝大部分的异常来说,这个数组元素都未定义。

24.5 软件异常——RaiseException函数

参数

说明

DWORD dwExceptionCode

要抛出异常的标识符,可参考《异常错误代码规则》来编写

DWORD dwExceptionFlags

必须下列两者之一

0:

EXCEPTION_NONCONTINUABLE:异常不可继续,即不能再异常过滤程序中返回EXCEPTION_CONTINUE_EXECUTE,否则重新执行那条导致错误的CPU指令会继续抛出一个新的EXCEPTION_NONCONTINUABLE_EXCEPTION异常。

DWORD nNumberOfArguments

用来传递有关抛出异常的附加信息。一般不需要。可将nNumberOfArgument设为0。pArguments设为NULL。

Const ULONG_PTR* pArguments

返回值

void

【RaiseException程序】——演示自己抛出的软件异常

#include <tchar.h>
#include <windows.h> DWORD FilterFunction(){
_tprintf(_T("")); //第1句被输出的语句
return EXCEPTION_EXECUTE_HANDLER;
} int main(){
__try{
__try{
RaiseException(, , , NULL);
}
__finally{
_tprintf(_T("")); //第2句被输出的语句
}
}
__except (FilterFunction()){
_tprintf(_T("3\n")); //第3句被输出的语句
}
_tsystem(_T("PAUSE"));
return 0;
}

第24章 SEH结构化异常处理_异常处理及软件异常的更多相关文章

  1. 第25章 SEH结构化异常处理_未处理异常及向量化异常

    25.1 UnhandledExceptionFilter函数详解 25.1.1 BaseProcessStart伪代码(Kernel32内部) void BaseProcessStart(PVOID ...

  2. 第23章 SEH结构化异常处理(3)_终止处理程序

    23.3 终止处理程序 23.3.1 程序的结构 (1)框架 __try{ //被保护的代码块 …… } __finally{ //终止处理 } (2)__try/__finally的特点 ①fina ...

  3. 第23章 SEH结构化异常处理(2)_编译器对系统SEH机制的封装

    23.2 编译器层面对系统SEH机制的封装 23.2.1 扩展的EXCEPTION_REGISTRATION级相关结构:VC_EXCEPTION_REGISTRATION (1)VC_EXCEPTIO ...

  4. 第23章 SEH结构化异常处理(1)_系统SEH机制

    23.1 基础知识 23.1.1 Windows下的软件异常 (1)中断和异常 ①中断是由外部硬件设备或异步事件产生的 ②异常是由内部事件产生的,可分为故障.陷阱和终止三类. (2)两种异常处理机制: ...

  5. 异常处理第三讲,SEH(结构化异常处理),异常展开问题

    异常处理第三讲,SEH(结构化异常处理),异常展开问题 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 不知道昨天有木有 ...

  6. 《Linux命令行与shell脚本编程大全》第十二章 使用结构化命令

    许多程序要就对shell脚本中的命令施加一些逻辑控制流程. 结构化命令允许你改变程序执行的顺序.不一定是依次进行的 12.1 使用if-then语句 如下格式: if command then     ...

  7. Linux编程 24 shell编程(结构化 if [ condition ] 数值比较,字符串比较)

    一.概述 接着上篇讲的结构化命令,最后讲到了test命令的另一种写法 if [ condition ],它的语法格式如下: --格式如下: if [ condition ] then commands ...

  8. Windows内核读书笔记——SEH结构化异常处理

    SEH是对windows系统中的异常分发和处理机制的总称,其实现分布在很多不同的模块中. SEH提供了终结处理和异常处理两种功能. 终结处理保证终结处理块中的程序一定会被执行 __try { //要保 ...

  9. Elasticsearch结构化搜索_在案例中实战使用term filter来搜索数据

    1.根据用户ID.是否隐藏.帖子ID.发帖日期来搜索帖子 (1)插入一些测试帖子数据 POST /forum/article/_bulk { "index": { "_i ...

随机推荐

  1. Javascript中void操作符

    Javascript中void是一个操作符,该操作符指定要计算一个表达式但是不返回值. void操作符用法格式如下:1.javascript:void (expression)2.javascript ...

  2. SQL索引学习-聚集索引

    这篇接着我们的索引学习系列,这次主要来分享一些有关聚集索引的问题.上一篇SQL索引学习-索引结构主要是从一些基础概念上给大家分享了我的理解,没有实例,有朋友就提到了聚集索引的问题,这里列出来一下: 其 ...

  3. SAP 金额在表中的存储及货币转换因子

    场景:一个接口出问题了,SAP通过RFC将SO数据传输到Java系统,错误的将100日元传为1.00日元. 其实查看SAP透明表发现,表中存储的的确是1.00,是前台真实数据的1/100,开发接口时没 ...

  4. Interoperability between Java and SharePoint 2013 on Premises

    http://ctp-ms.blogspot.com/2012/12/interoperability-between-java-and.html   Introduction One of the ...

  5. 桥牌笔记:Skill Level 4 D8

    西拿黑桃K.A后,转攻小方块. 看来只有一个小红桃失张了.飞方块没有必要冒险.但将牌分布4-0时会有点麻烦.

  6. 一个基于Myeclipse开发的Java打地鼠小游戏(Appletcation)

    package javaes.zixue.wangshang.daima; 2 3 import java.awt.Cursor; import java.awt.Image; import java ...

  7. iOS9 HTTP 不能正常使用的解决办法

    Google后查证,iOS9引入了新特性App Transport Security (ATS).详情:App Transport Security (ATS) 新特性要求App内访问的网络必须使用H ...

  8. 提高Objective-C代码质量心机一:简化写法

    提高OC代码质量的小心机 一.OC特性 OC 为 C 语言添加了面向对象特性,是其超集; OC 使用动态绑定的消息结构,也就是,在运行时才会检查对象类型; 接收一条消息后,究竟应执行何种代码,由运行期 ...

  9. SVN Can't open file 'xxx':Premission denied

    换了一台电脑,重新搭建本地svn服务器的时候,服务器搭起来了,但是用Cornerstone往服务器上传工程的时候报错 报错有以上两种,都是因为文件权限的限制 解决方法 第一种报错 1.在Finder里 ...

  10. iOS开发之网络数据解析--GDataXML解析框架的使用

      前言:GDataXML是google基于C语言libxml2写的第三方框架,该源码文件就一个类,看其源码,基本使用了C语言的底层的很多lib编译库代码,所以刚导入使用,会报错提示需要设置导入需要的 ...