ACCESS【 转】__try,__except,__finally,__leave卓殊模型机制

存储爱护相当
内层的except块中
Press any key to continue

ACCESS 1

ACCESS 2

ACCESS 3

SEH实际包罗四个基本点功效:停止处理(termination
handling)和相当处理(exception handling) 

#include <stdio.h>

void test()
{
puts("hello");
__try
{
int* p;
puts("__try块中");

// 直接跳出当前的__try作用域
__leave;
p = 0;
*p = 25;
}
__finally
{
// 这里会被执行吗?当然
puts("__finally块中");
}

puts("world");
}

void main()
{
__try
{
test();
}
__except(1)
{
puts("__except块中");
}
}

 1. 受监察和控制的代码模块被执行(也即__try定义的模块代码);
  2.
比方下面的代码执行进程中,没有出现非常的话,那么控制流将转入到__except子句之后的代码模块中;
  3.
再不,假设出现卓殊的话,那么控制流将进入到__except后边的表达式中,也即首先计算这一个表明式的值,之后再依据这些值,来决定做出相应的处理。那一个值有三种状态,如下:
  EXCEPTION_CONTINUE_EXECUTION (–1)
极度被忽视,控制流将在老大出现的点之后,继续回升运营。
  EXCEPTION_CONTINUE_SEA奥迪Q3CH (0)
格外不被识别,也即当前的那个__except模块不是其一丰盛错误所对应的不错的老大处理模块。系统将继承到上一层的try-except域中持续寻找三个方便的__except模块。
  EXCEPTION_EXECUTE_HANDLEPRADO (1)
至极已经被识别,也即近来的这些相当错误,系统现已找到了并能够承认,那些__except模块就是不错的不胜处理模块。控制流将进入到__except模块中。
 
try-except深入
  上面包车型地铁始末中早就对try-except进行了健全的摸底,不过有几许还不曾演讲到。那就是哪些在__except模块中获得非凡错误的相干音信,这格外首要,它实际是开展丰裕错误处理的前提,也是对那些举办分层分级别处理的前提。综上可得,假使没有这么些起码的音讯,相当处理怎么着开始展览?由此获得至极音信卓殊的首要。Windows提供了八个API函数,如下:
 

try-except进阶
  与C++非凡处理模型很相像,在2个函数中,能够有多少个try-except语句。它们能够是1个平面包车型大巴线性结构,也能够是分段的嵌套结构。例程代码如下:

typedef struct _EXCEPTION_POINTERS { // exp 
PEXCEPTION_RECORD ExceptionRecord; 
PCONTEXT ContextRecord; 
} EXCEPTION_POINTERS;

C++不常用关键字(__leave)

3个try 块之后不能够既有finally块又有except块。但能够在try –
except块中嵌套try – finally块,反过来
也可以。

__try  __finally关键字用来标注甘休处理程序两段代码的概略

// 例程2
// 分层的嵌套结构

ACCESS 4

无论是珍重体(try块)
是如何退出的。不论你在保养体中运用return,依然goto,可能是longjump,甘休处理程序
(finally块)都将被调用。

// 例程1
// 平面的线性结构

地方的顺序很简单,那里不做越来越的剖析。大家必要重视谈论的是,在__except模块中如何分辨不一样的非凡,以便对充裕进行很好的归类处理。毫无疑问,它自然是通过GetExceptionCode()或GetExceptionInformation
()函数来赢妥贴前的12分错误代码,实际相当于DwExceptionCode字段。格外错误代码在winError.h文件中定义,它遵从Windows系统下统一的错误代码的条条框框。每一个DWO奥迪Q5D被剪切多少个字段,如下表所示:
比如说大家得以在winbase.h文件中找到EXCEPTION_ACCESS_VIOLATION的值为0
xC0000005,将以此可怜代码值拆开,来分析看看它的各类bit位字段的涵义。
C 0 0 0 0 0 0 5 (十六进制)
1100 0000 0000 0000 0000 0000 0000 0101 (二进制)
第③ 0位和第一十位都以1,表示该尤其是三个严重的荒谬,线程大概不可见一连往下运作,必必要及时处理恢复生机那个充足。第③12个人是0,表示系统中早就定义了12分代码。第1 7个人是0,留待后用。第① 6 位至2
七人是0,表示是FACILITY_NULL设备项目,它意味着存取至极可产生在系统中任哪个地点方,不是选拔一定设备才发生的不行。第0位到第二五个人的值为5,表示丰盛错误的代码。
  如果程序员在程序代码中,计划抛出有个别自定义类型的不胜,必须求规划设计好本人的百般类型的划分,遵照地点的平整来填充很是代码的依次字段值,如上面示例程序中抛出二个非常代码为0xE0000001软件卓殊。

 

ACCESS 5

void main()
{
    __try
    {
        puts("in try");
    }
    __except(1)
    {
        // 又一个try-except语句
        __try
        {
            puts("in try1");
        }
        __except(1)
        {
            puts("in except1");
        }

        puts("in except");
    }
}

ACCESS 6

 呵呵!是或不是很简短,而且与C++万分处理模型很一般。当然,为了与C++万分处理模型相差别,VC编写翻译器对根本字做了不难改观。首先是在每一个重点字加上八个下划线作为前缀,那样既保持了语义上的一致性,别的也尽最大恐怕来制止了重在字的有只怕导致名字冲突而滋生的麻烦等;其次,C++万分处理模型是行使catch关键字来定义分外处理模块,而SEH是使用__except关键字来定义。并且,catch关键字背后往往好像接受一个函数参数一样,能够是各类别型的丰裕数据对象;可是__except关键字则分裂,它背后跟的却是贰个表达式(能够是各体系型的表明式,前面会越加分析)。

上边的程序运维结果如下:
hello
__try块中
__finally块中
world
Press any key to continue

  可是,对于第壹种状态,程序员完全能够使得地防止它,幸免“局地进展”引起的不供给的额外开销。实际那也是与结构化程序设计思想相平等的,也即一个先后模块应该唯有三个进口和1个说道,程序模块内尽量制止使用goto语句等。不过,话虽如此,有时为了增强程序的可读性,程序员在编写代码时,有时只怕只好动用部分与结构化程序设计思想相悖的做法,例如,在3个函数中,或许有多处的return语句。针对那种意况,SEH提供了一种非凡实用的折衷方案,那正是__leave关键字所起的服从,它既具有像goto语句和return语句那样类似的作用(由于检查和测试到某个程序运营中的错误,供给立时离开当前的
__try块功用域),不过又制止了“局地进展”
的额外费用。如故看个例子吗!代码如下:** 

ACCESS 7

上边的程序运维结果如下:

 SEH有两项非凡强大的意义。当然,首先是不行处理模型了,由此,那篇小说首先深远阐发SEH提供的那么些处理模型。此外,SEH还有一个特意强劲的成效,那将在下一篇作品中开始展览详细介绍。

ACCESS 8

ACCESS 9

  呵呵!仔细瞅瞅,那是否和上一篇小说中,用户程序所注册的分外处理的回调函数的五个参数类型一样。是的,的确没错!其中EXCEPTION_RECO中华VD类型,它记录了部分与尤其相关的新闻;而CONTEXT数据结构体中记录了卓殊产生时,线程当时的上下文环境,紧要包蕴寄存器的值。由此有了那么些音讯,__except模块便足以对那多少个错误实行很好的分类和复苏处理。然则尤其供给留意的是,那八个函数只好是在__except前边的括号中的说明式功用域内立见功能,不然结果也许没有保险(至于何以,在后头深切剖析相当模型的兑现时候,再做详细演讲)。看三个例程吧!代码如下:

在try使用__leave关键字会引起跳转到try块的终极

导读: 
从本篇作品开头,将通盘阐释__try,__except,__finally,__leave至极模型机制,它也正是Windows类别操作系统平台上提供的SEH模型。主人公阿愚将在那边与我们大饱眼福SEH(
结构化十分处理)的上学进程和经验总计。 深切驾驭请参阅<<windows
宗旨编制程序>>第一3, 24章.

int seh_filer(int code)
{
    switch(code)
    {
    case EXCEPTION_ACCESS_VIOLATION :
        printf("存储保护异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_DATATYPE_MISALIGNMENT :
        printf("数据类型未对齐异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_BREAKPOINT :
        printf("中断异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_SINGLE_STEP :
        printf("单步中断异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_ARRAY_BOUNDS_EXCEEDED :
        printf("数组越界异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_FLT_DENORMAL_OPERAND :
    case EXCEPTION_FLT_DIVIDE_BY_ZERO :
    case EXCEPTION_FLT_INEXACT_RESULT :
    case EXCEPTION_FLT_INVALID_OPERATION :
    case EXCEPTION_FLT_OVERFLOW :
    case EXCEPTION_FLT_STACK_CHECK :
    case EXCEPTION_FLT_UNDERFLOW :
        printf("浮点数计算异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_INT_DIVIDE_BY_ZERO :
        printf("被0除异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_INT_OVERFLOW :
        printf("数据溢出异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_IN_PAGE_ERROR :
        printf("页错误异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_ILLEGAL_INSTRUCTION :
        printf("非法指令异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_STACK_OVERFLOW :
        printf("堆栈溢出异常,错误代码:%x\n", code);
        break;
    case EXCEPTION_INVALID_HANDLE :
        printf("无效句病异常,错误代码:%x\n", code);
        break;
    default :
        if(code & (1<<29))
            printf("用户自定义的软件异常,错误代码:%x\n", code);
        else
            printf("其它异常,错误代码:%x\n", code);
        break;
    }

    return 1;
}


void main()
{
    __try
    {
        puts("try块中");

        // 注意,主动抛出一个软异常
        RaiseException(0xE0000001, 0, 0, 0);
    }
    __except(seh_filer(GetExceptionCode()))
    {
        puts("except块中");
    }

}
int exception_access_violation_filter(LPEXCEPTION_POINTERS p_exinfo)
{
    if(p_exinfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
    {
        printf("存储保护异常\n");
        return 1;
    }
    else 
        return 0;
}

int exception_int_divide_by_zero_filter(LPEXCEPTION_POINTERS p_exinfo)
{
    if(p_exinfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
    {
        printf("被0除异常\n");
        return 1;
    }
    else 
        return 0;
}

void main()
{

    __try
    {
        __try
        {
            int* p;

            // 下面将导致一个异常
            p = 0;
            *p = 45;
        }
        // 注意,__except模块捕获一个存储保护异常
        __except(exception_access_violation_filter(GetExceptionInformation()))
        {
            puts("内层的except块中");
        }
  //可以在此写除0异常的语句
     int b = 0;
      int a = 1 / b;
    }
    // 注意,__except模块捕获一个被0除异常
    __except(exception_int_divide_by_zero_filter(GetExceptionInformation())) 
    {
        puts("外层的except块中");
    }
}

  呵呵!感觉不错,大家能够在上头的次第基础之上改动一下,让它抛出二个被0除格外,看程序的运转结果是或不是如预期那样。
  最终还有少数须求阐述,在C++的足够处理模型中,有1个throw关键字,也即在受监督的代码中抛出2个丰裕,那么在SEH万分处理模型中,是否也应该有那样一个好像的机要字或函数呢?是的,没错!SEH万分处理模型中,对尤其划分为两大类,第贰种正是地点一些例程中所见到的,那类极度是系统十三分,也被称作硬件非凡;还有一类,正是程序中协调抛出卓殊,被称呼软件极度。怎么抛出呢?仍旧Windows提供了的API函数,它的扬言如下:
 

 

  很简单吗!实际上,在C++的不得了处理模型中的throw关键字,最后也是对RaiseException()函数的调用,相当于说,throw是RaiseException的上层封装的更高级一类的函数,那之后再详细分析它的代码实现。那里依然看三个简短例子吗!代码如下:

void main()
{
    __try
    {
        puts("in try");
        // 又一个try-except语句
        __try
        {
            puts("in try1");
        }
        __except(1)
        {
            puts("in except1");
        }
    }
    __except(1)
    {
        puts("in except");
    }
}

ACCESS 10

void main()
{
    // 定义受监控的代码模块
    __try
    {
        puts("in try");
    }
    //定义异常处理模块
    __except(1)
    {
        puts("in except");
    }
}

ACCESS 11

// 例程3
// 分层的嵌套在__except模块中

LPEXCEPTION_POINTERS GetExceptionInformation(VOID);
DWORD GetExceptionCode(VOID);

 

转自:http://blog.csdn.net/wwl33695/article/details/8686458

每当你建立一个try块,它必须跟随二个finally块或三个except块。

VOID RaiseException(
DWORD dwExceptionCode, // exception code
DWORD dwExceptionFlags, // continuable exception flag
DWORD nNumberOfArguments, // number of arguments in array
CONST DWORD *lpArguments // address of array of arguments
);

**总结__finally块被实践的流水生产线时,无外乎三种情景。第叁种就是各种执行到__finally块区域内的代码,那种景况很简单,不难明白;第三种正是goto语句或return语句引发的次第控制流离开当前__try块成效域时,系统活动实现对__finally块代码的调用;第两种就是由于在__try块中出现非常时,导致程控流离开当前__try块功效域,那种情状下也是由系统活动实现对__finally块的调用。无论是第③种,还是第③种状态,毫无疑问,它们都会挑起一点都不小的体系开发,编写翻译器在编写翻译此类程序代码时,它会为这三种情状准备很多的附加代码。一般第②种景况,被喻为“局地进展(LocalUnwinding)”;第叁种状态,被叫作“全局展开(GlobalUnwinding)”。在末端演讲SEH完毕的时候会详细分析到那或多或少。
第1种境况,也即出于出现卓殊而招致的“全局展开”,对于程序员而言,那或然是无力回天幸免的,因为您在接纳至极处理体制升高程序可相信健壮性的同时,不可幸免的会挑起品质上别样的某个支付。呵呵!这世界实质上也算瞒公平的,有得必有失。

地点的程序运行结果如下:
hello
try块中
用户自定义的软件万分,错误代码:e0000001
except块中
world
Press any key to continue

void main()
{
    __try
    {
        puts("in try");
    }
    __except(1)
    {
        puts("in except");
    }


    // 又一个try-except语句
    __try
    {
        puts("in try1");
    }
    __except(1)
    {
        puts("in except1");
    }
}

ACCESS 12

 

总结
  (1)
C++至极模型用try-catch语法定义,而SEH万分模型则用try-except语法;
  (2) 与C++格外模型相似,try-except也辅助多层的try-except嵌套。
  (3)
与C++十分模型不一样的是,try-except模型中,2个try块只好是有三个except块;而C++万分模型中,三个try块能够有多少个catch块。
  (4)
与C++极度模型相似,try-except模型中,查找搜索相当模块的平整也是逐级升高拓展的。不过稍有分其余是,C++十分模型是根据十分对象的类型来开始展览匹配查找的;而try-except模型则不一致,它经过贰个表明式的值来拓展判断。固然表明式的值为1(EXCEPTION_EXECUTE_HANDLE奇骏),表示找到了12分处理模块;假如值为0(EXCEPTION_CONTINUE_SEA卡宴CH),表示继续向上一层的try-except域中持续查找别的只怕卓殊的不得了处理模块;假设值为-1(EXCEPTION_CONTINUE_EXECUTION),表示忽略这一个特别,注意那些值一般很少用,因为它很简单导致程序难以预测的结果,例如,死循环,甚至招致程序的倒台等。
   (5)
__except关键字背后跟的表明式,它能够是各种类型的表明式,例如,它能够是三个函数调用,或是叁个口径表明式,或是1个逗号表明式,或几乎正是多少个整型常量等等。最常用的是叁个函数表明式,并且经过行使GetExceptionCode()或GetExceptionInformation
()函数来收获当前的百般错误音讯,便于程序员有效控制尤其错误的归类处理。
   (6)
SEH非凡处理模型中,非常被剪切为两大类:系统丰裕和软件万分。当中国总计机软件与技术服务总集团件相当通过RaiseException()函数抛出。RaiseException()函数的效率类似于C++很是模型中的throw语句。

ACCESS 13

ACCESS 14

try-except入门
  SEH的这几个处理模型首要由try-except语句来形成,它与规范C++所定义的要命处理模型万分相近,也都是足以定义出受监督的代码模块,以及定义万分处理模块等。依然老艺术,看3个例证先,代码如下: 
//seh-test.c

  在那之中GetExceptionCode()重返错误代码,而GetExceptionInformation()重返更宏观的新闻,看它函数的扬言,再次回到了叁个LPEXCEPTION_POINTE大切诺基S类型的指针变量。那么EXCEPTION_POINTERAV4S结构怎样呢?如下,
 

相关文章