>

__leave相当模型机制,Windows结构化分外管理浅析

- 编辑:金沙国际平台登录 -

__leave相当模型机制,Windows结构化分外管理浅析

近来一向被多个难题所压抑,正是写出来的次序老是出新无故崩溃,有的地点和睦了解大概有标题,可是有的位置又历来不能知道有怎么着难点。更加苦逼的事情是,大家的前后相继是亟需7x24劳动客商,纵然没有必要实时精准零差错,不过总不可能现身断线错失数据状态。故刚巧通过管理该难题,找到了部分施工方案,怎么捕获访谈不合法内部存储器地址或者0除以二个数。进而就碰见了这么些结构化卓殊管理,今就简短做个介绍认知下,方便我们蒙受相关主题材料后,首先知道难题由来,再正是怎么样肃清。废话不多说,上边踏入正题。

转自:

什么是结构化极度管理

结构化格外管理(structured exception handling,下文简单的称呼:SEH卡塔 尔(阿拉伯语:قطر‎,是用作大器晚成种系统编制引进到操作系统中的,自己与语言无关。在大家温馨的前后相继中运用SEH能够让大家三月不知肉味开拓首要成效,而把程序中所可能现身的极其实行统大器晚成的拍卖,使程序显得愈加精短且扩大可读性。

使用SHE,并不表示能够完全忽视代码中可能出现的荒唐,可是大家能够将软件专业流程和软件分外情形管理进展分离,先三月不知肉味干首要且热切的活,再来管理那几个大概会遇到种种的失实的要害不迫切的标题(不火急,但相对首要卡塔 尔(英语:State of Qatar)

当在程序中接纳SEH时,就改成编写翻译器相关的。其所变成的担当首要由编写翻译程序来肩负,比方编写翻译程序会发出部分表(table)来支撑SEH的数据结构,还有或许会提供回调函数。

注:
无须混淆SHE和C++ 极度处理。C++ 极度管理再情势上表现为运用首要字catchthrow,这些SHE的方式不近似,再windows Visual C++中,是由此编写翻译器和操作系统的SHE进行落到实处的。

在所有 Win32 操作系统提供的机制中,使用最习以为常的未公开的体制可能就要数SHE了。一提到SHE,或许就能够令人想起 *__try__finally* 和 *__except* 之类的词儿。SHE骨子里包含两上边包车型客车机能:终止管理(termination handing)十一分处理(exception handing)

导读: 
从本篇文章开端,将通盘阐释__try,__except,__finally,__leave分外模型机制,它也正是Windows体系操作系统平台上提供的SEH模型。主人公阿愚将要这里地与大家大饱眼福SEH( 结构化卓殊管理)的就学进程和经验总计。 深远掌握请参阅<<windows 焦点编制程序>>第23, 24章.

停下管理

终止处理程序确定保障不管多个代码块(被爱戴代码)是怎样退出的,别的三个代码块(终止管理程序)总是能被调用和实施,其语法如下:

__try
{
    //Guarded body
    //...
}
__finally
{
    //Terimnation handler
    //...
}

**__try __finally** 关键字标识了结束管理程序的三个部分。操作系统和编写翻译器的协同工承保证了随意爱惜代码部分是哪些退出的(无论是寻常退出、如故不行退出)终止程序都会被调用,即**__finally**代码块都能试行。

SEH实际包涵八个主要意义:截止管理(termination handling卡塔 尔(英语:State of Qatar)和至极管理(exception handling) 

try块的例行退出与十分退出

try块大概会因为returngoto,至极等非当然退出,也大概会因为成功试行而自然退出。但无论是try块是怎么着退出的,finally块的剧情都会被实行。

int Func1()
{
    cout << __FUNCTION__ << endl;
    int nTemp = 0;
    __try{
        //正常执行
        nTemp = 22;
        cout << "nTemp = " << nTemp << endl;
    }
    __finally{
        //结束处理
        cout << "finally nTemp = " << nTemp << endl;
    }
    return nTemp;
}

int Func2()
{
    cout << __FUNCTION__ << endl;
    int nTemp = 0;
    __try{
        //非正常执行
        return 0;
        nTemp = 22;
        cout << "nTemp = " << nTemp << endl;
    }
    __finally{
        //结束处理
        cout << "finally nTemp = " << nTemp << endl;
    }
    return nTemp;
}

结果如下:

Func1
nTemp = 22  //正常执行赋值
finally nTemp = 22  //结束处理块执行

Func2
finally nTemp = 0   //结束处理块执行

以上实例能够看出,通过选取终止管理程序能够免御太早实行return语句,当return言辞视图退出try块的时候,编写翻译器会让finally代码块再它前边推行。对于在三十一线程编制程序中经过信号量访问变量时,出现至极情形,能顺遂是或不是能量信号量,那样线程就不会平昔据有叁个时限信号量。当finally代码块施行完后,函数就回去了。

为了让漫天机制运作起来,编写翻译器必需生成一些格外轮代理公司码,而系统也必得试行一些相当专业,所以应当在写代码的时候制止再try代码块中接受return语句,因为对应用程序品质有影响,对于简易demo难题非常小,对于要长日子不间断运维的次第照旧悠着点好,下文仲提到三个重要字**__leave**关键字,它能够帮忙大家开掘成后生可畏对进展花费的代码。

一条好的资历法则:毫无再结束管理程序中含有让try块提前退出的言语,那意味从try块和finally块中移除return,continue,break,goto等语句,把那么些言辞放在终止管理程序以外。那样做的裨益就是不用去捕获哪些try块中的提前退出,进而时编写翻译器生成的代码量最小,进步程序的运作功效和代码可读性。

每当你创设多个try块,它必需跟随二个finally块或多少个except块。

####finally块的清理功效及对程序结构的熏陶

在编码的进度中须求参加须求检验,检查测量试验功效是还是不是中标施行,若成功的话推行那一个,不成事的话须要作一些优异的清理职业,举例释放内部存款和储蓄器,关闭句柄等。假设检查评定不是成千上万以来,倒没什么影响;但若又相当多检查实验,且软件中的逻辑关系比较复杂时,往往供给化十分的大精力来落到实处繁缛的检查测量试验剖断。结果就能够使程序看起来结构比较复杂,大大收缩程序的可读性,并且程序的体量也不断增大。

对应以此标题本身是深有心得,曾在写通过COM调用WordVBA的时候,必要层层获取对象、判定指标是或不是拿走成功、实践有关操作、再自由对象,三个流水生产线下来,本来意气风发两行的VBA代码,C++ 写出来将在好几十行(那还得看操作的是多少个什么指标)。

上面就来三个方式让大家看看,为何某一个人心爱脚本语言而不赏识C++的原故吧。

为了更有逻辑,更有档期的顺序地操作 OfficeMicrosoft 把应用(Application)按逻辑成效划分为如下的树形结构

Application(WORD 为例,只列出一部分)
  Documents(所有的文档)
        Document(一个文档)
            ......
  Templates(所有模板)
        Template(一个模板)
            ......
  Windows(所有窗口)
        Window
        Selection
        View
        .....
  Selection(编辑对象)
        Font
        Style
        Range
        ......
  ......

独有了然了逻辑档期的顺序,我们能力科学的操纵 Office。譬释迦牟尼说,纵然给出大器晚成个VBA语句是:

Application.ActiveDocument.SaveAs "c:abc.doc"

那么,我们就知晓了,那个操作的历程是:

  1. 第一步,取得Application
  2. 第二步,从Application中取得ActiveDocument
  3. 第三步,调用 Document 的函数 SaveAs,参数是二个字符串型的文件名。

那只是二个最简便的的VBA代码了。来个稍稍复杂点的如下,在选中处,插入叁个书签:

 ActiveDocument.Bookmarks.Add Range:=Selection.Range, Name:="iceman"

此地流程如下:

  1. 获取Application
  2. 获取ActiveDocument
  3. 获取Selection
  4. 获取Range
  5. 获取Bookmarks
  6. 调用方法Add

获取各个对象的时候都亟待判断,还须要提交错误管理,对象释放等。在那就交给伪码吧,全写出来篇幅有一点长

#define RELEASE_OBJ(obj) if(obj != NULL) 
                        obj->Realse();

BOOL InsertBookmarInWord(const string& bookname)
{
    BOOL ret = FALSE;
    IDispatch* pDispApplication = NULL;
    IDispatch* pDispDocument = NULL;
    IDispatch* pDispSelection = NULL;
    IDispatch* pDispRange = NULL;
    IDispatch* pDispBookmarks = NULL;
    HRESULT hr = S_FALSE;

    hr = GetApplcaiton(..., &pDispApplication);
    if (!(SUCCEEDED(hr) || pDispApplication == NULL))
        return FALSE;

    hr = GetActiveDocument(..., &pDispDocument);
    if (!(SUCCEEDED(hr) || pDispDocument == NULL)){
        RELEASE_OBJ(pDispApplication);
        return FALSE;
    }

    hr = GetActiveDocument(..., &pDispDocument);
    if (!(SUCCEEDED(hr) || pDispDocument == NULL)){
        RELEASE_OBJ(pDispApplication);
        return FALSE;
    }

    hr = GetSelection(..., &pDispSelection);
    if (!(SUCCEEDED(hr) || pDispSelection == NULL)){
        RELEASE_OBJ(pDispApplication);
        RELEASE_OBJ(pDispDocument);
        return FALSE;
    }

    hr = GetRange(..., &pDispRange);
    if (!(SUCCEEDED(hr) || pDispRange == NULL)){
        RELEASE_OBJ(pDispApplication);
        RELEASE_OBJ(pDispDocument);
        RELEASE_OBJ(pDispSelection);
        return FALSE;
    }

    hr = GetBookmarks(..., &pDispBookmarks);
    if (!(SUCCEEDED(hr) || pDispBookmarks == NULL)){
        RELEASE_OBJ(pDispApplication);
        RELEASE_OBJ(pDispDocument);
        RELEASE_OBJ(pDispSelection);
        RELEASE_OBJ(pDispRange);
        return FALSE;
    }

    hr = AddBookmark(...., bookname);
    if (!SUCCEEDED(hr)){
        RELEASE_OBJ(pDispApplication);
        RELEASE_OBJ(pDispDocument);
        RELEASE_OBJ(pDispSelection);
        RELEASE_OBJ(pDispRange);
        RELEASE_OBJ(pDispBookmarks);
        return FALSE;
    }
    ret = TRUE;
    return ret;

那只是伪码,纵然也足以通过goto裁减代码行,不过goto用得倒霉就出错了,上边程序中稍不留心就goto到不应当得到地点了。

BOOL InsertBookmarInWord2(const string& bookname)
{
    BOOL ret = FALSE;
    IDispatch* pDispApplication = NULL;
    IDispatch* pDispDocument = NULL;
    IDispatch* pDispSelection = NULL;
    IDispatch* pDispRange = NULL;
    IDispatch* pDispBookmarks = NULL;
    HRESULT hr = S_FALSE;

    hr = GetApplcaiton(..., &pDispApplication);
    if (!(SUCCEEDED(hr) || pDispApplication == NULL))
        goto exit6;

    hr = GetActiveDocument(..., &pDispDocument);
    if (!(SUCCEEDED(hr) || pDispDocument == NULL)){
        goto exit5;
    }

    hr = GetActiveDocument(..., &pDispDocument);
    if (!(SUCCEEDED(hr) || pDispDocument == NULL)){
        goto exit4;
    }

    hr = GetSelection(..., &pDispSelection);
    if (!(SUCCEEDED(hr) || pDispSelection == NULL)){
        goto exit4;
    }

    hr = GetRange(..., &pDispRange);
    if (!(SUCCEEDED(hr) || pDispRange == NULL)){
        goto exit3;
    }

    hr = GetBookmarks(..., &pDispBookmarks);
    if (!(SUCCEEDED(hr) || pDispBookmarks == NULL)){
        got exit2;
    }

    hr = AddBookmark(...., bookname);
    if (!SUCCEEDED(hr)){
        goto exit1;
    }

    ret = TRUE;
exit1:
    RELEASE_OBJ(pDispApplication);
exit2:
    RELEASE_OBJ(pDispDocument);
exit3:
    RELEASE_OBJ(pDispSelection);
exit4:
    RELEASE_OBJ(pDispRange);
exit5:
    RELEASE_OBJ(pDispBookmarks);
exit6:
    return ret;

此处照旧经过SEH的告风流洒脱段落管理程序来重新该方法,那样是或不是更清晰明了。

BOOL InsertBookmarInWord3(const string& bookname)
{
    BOOL ret = FALSE;
    IDispatch* pDispApplication = NULL;
    IDispatch* pDispDocument = NULL;
    IDispatch* pDispSelection = NULL;
    IDispatch* pDispRange = NULL;
    IDispatch* pDispBookmarks = NULL;
    HRESULT hr = S_FALSE;

    __try{
        hr = GetApplcaiton(..., &pDispApplication);
        if (!(SUCCEEDED(hr) || pDispApplication == NULL))
            return FALSE;

        hr = GetActiveDocument(..., &pDispDocument);
        if (!(SUCCEEDED(hr) || pDispDocument == NULL)){
            return FALSE;
        }

        hr = GetActiveDocument(..., &pDispDocument);
        if (!(SUCCEEDED(hr) || pDispDocument == NULL)){
            return FALSE;
        }

        hr = GetSelection(..., &pDispSelection);
        if (!(SUCCEEDED(hr) || pDispSelection == NULL)){
            return FALSE;
        }

        hr = GetRange(..., &pDispRange);
        if (!(SUCCEEDED(hr) || pDispRange == NULL)){
            return FALSE;
        }

        hr = GetBookmarks(..., &pDispBookmarks);
        if (!(SUCCEEDED(hr) || pDispBookmarks == NULL)){
            return FALSE;
        }

        hr = AddBookmark(...., bookname);
        if (!SUCCEEDED(hr)){
            return FALSE;
        }

        ret = TRUE;
    }
    __finally{
        RELEASE_OBJ(pDispApplication);
        RELEASE_OBJ(pDispDocument);
        RELEASE_OBJ(pDispSelection);
        RELEASE_OBJ(pDispRange);
        RELEASE_OBJ(pDispBookmarks);
    }
    return ret;

那多少个函数的成效是如出后生可畏辙的。能够见见在InsertBookmarInWord中的清理函数(RELEASE_OBJ卡塔 尔(英语:State of Qatar)随地都以,而InsertBookmarInWord3中的清理函数则整个聚齐在finally块,尽管在读书代码时只需看try块的剧情就能够精通程序流程。那四个函数本身都超级小,能够细稳重得下这两个函数的差异。

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

关键字 __leave

try块中央银行使**__leave至关重大字会使程序跳转到try块的终极,进而自然的进去finally块。
对于上例中的InsertBookmarInWord3try块中的return完全能够用
__leave** 来替换。两者的界别是用return会引起try太早退出系统会举香港行政局地进展而充实系统开辟,若使用**__leave**就能自然退出try块,开销就小的多。

BOOL InsertBookmarInWord4(const string& bookname)
{
    BOOL ret = FALSE;
    IDispatch* pDispApplication = NULL;
    IDispatch* pDispDocument = NULL;
    IDispatch* pDispSelection = NULL;
    IDispatch* pDispRange = NULL;
    IDispatch* pDispBookmarks = NULL;
    HRESULT hr = S_FALSE;

    __try{
        hr = GetApplcaiton(..., &pDispApplication);
        if (!(SUCCEEDED(hr) || pDispApplication == NULL))
            __leave;

        hr = GetActiveDocument(..., &pDispDocument);
        if (!(SUCCEEDED(hr) || pDispDocument == NULL))
            __leave;

        hr = GetActiveDocument(..., &pDispDocument);
        if (!(SUCCEEDED(hr) || pDispDocument == NULL))
            __leave;

        hr = GetSelection(..., &pDispSelection);
        if (!(SUCCEEDED(hr) || pDispSelection == NULL))
            __leave;

        hr = GetRange(..., &pDispRange);
        if (!(SUCCEEDED(hr) || pDispRange == NULL))
            __leave;

        hr = GetBookmarks(..., &pDispBookmarks);
        if (!(SUCCEEDED(hr) || pDispBookmarks == NULL))
            __leave;

        hr = AddBookmark(...., bookname);
        if (!SUCCEEDED(hr))
            __leave;

        ret = TRUE;
    }
    __finally{
        RELEASE_OBJ(pDispApplication);
        RELEASE_OBJ(pDispDocument);
        RELEASE_OBJ(pDispSelection);
        RELEASE_OBJ(pDispRange);
        RELEASE_OBJ(pDispBookmarks);
    }
    return ret;
}

__try  __finally关键字用来标记甘休管理程序两段代码的轮廓

那一个管理程序

软件至极是大家都不乐意见到的,可是错误照旧不经常有,举例CPU捕获近似违规内部存款和储蓄器采访和除0那样的标题,风姿罗曼蒂克旦侦察到这种张冠李戴,就抛出有关非常,操作系统会给大家应用程序叁个查看异常类型的空子,况且运路程序自身管理这几个极其。非凡管理程序结构代码如下

  __try {
      // Guarded body
    }
    __except ( exception filter ) {
      // exception handler
    }

注意关键字**__except**,任何try块,前面总得更贰个finally代码块恐怕except代码块,但是try后又不可能同不时候有finallyexcept块,也不可能並且有多少个finnalyexcept块,不过能够相互嵌套使用

不论是爱戴体(try块卡塔尔
是何许退出的。无论你在爱惜体中动用return,依然goto,恐怕是longjump,截止管理程序
(finally块卡塔 尔(阿拉伯语:قطر‎都将被调用。

特别管理核心流程

int Func3()
{
    cout << __FUNCTION__ << endl;
    int nTemp = 0;
    __try{
        nTemp = 22;
        cout << "nTemp = " << nTemp << endl;
    }
    __except (EXCEPTION_EXECUTE_HANDLER){
        cout << "except nTemp = " << nTemp << endl;
    }
    return nTemp;
}

int Func4()
{
    cout << __FUNCTION__ << endl;
    int nTemp = 0;
    __try{
        nTemp = 22/nTemp;
        cout << "nTemp = " << nTemp << endl;
    }
    __except (EXCEPTION_EXECUTE_HANDLER){
        cout << "except nTemp = " << nTemp << endl;
    }
    return nTemp;
}

结果如下:

Func3
nTemp = 22  //正常执行

Func4
except nTemp = 0 //捕获异常,

Func3try块只是多少个轻易操作,故不会导致万分,所以except块中代码不会被施行,Func4try块视图用22除0,导致CPU捕获这几个事件,并抛出,系统定点到except块,对该非常进行拍卖,该处有个十一分过滤表明式,系统中有三该定义(定义在Windows的Excpt.h中):

1. EXCEPTION_EXECUTE_HANDLER:
    我知道这个异常了,我已经写了代码来处理它,让这些代码执行吧,程序跳转到except块中执行并退出
2. EXCEPTION_CONTINUE_SERCH
    继续上层搜索处理except代码块,并调用对应的异常过滤程序
3. EXCEPTION_CONTINUE_EXECUTION
    返回到出现异常的地方重新执行那条CPU指令本身

面是三种为主的应用方法:

  • 办法意气风发:直接采取过滤器的四个重回值之风度翩翩
__try {
   ……
}
__except ( EXCEPTION_EXECUTE_HANDLER ) {
   ……
}
  • 办法二:自定义过滤器
__try {
   ……
}
__except ( MyFilter( GetExceptionCode() ) )
{
   ……
}

LONG MyFilter ( DWORD dwExceptionCode )
{
  if ( dwExceptionCode == EXCEPTION_ACCESS_VIOLATION )
    return EXCEPTION_EXECUTE_HANDLER ;
  else
    return EXCEPTION_CONTINUE_SEARCH ;
}

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

.NET4.0中捕获SEH异常

在.NET 4.0自此,CLLacrosse将会有别出部分百般(都以SEH相当卡塔 尔(阿拉伯语:قطر‎,将这么些极其标记为破坏性卓殊(Corrupted State Exception卡塔 尔(阿拉伯语:قطر‎。针对那个极度,CL福睿斯的catch块不会捕捉这一个特别,一下代码也还未有主意捕捉到这个极度。

try{
    //....
}
catch(Exception ex)
{
    Console.WriteLine(ex.ToString());
}

因为并不是所有人都亟待捕获那一个可怜,如若你的次第是在4.0底下编写翻译并运营,而你又想在.NET程序里捕捉到SEH相当的话,有多少个方案得以品味:

  • 在托管程序的.config文件里,启用legacyCorruptedStateExceptionsPolicy这些天性,即简化的.config文件相仿上边包车型客车文本:
App.Config

<?xml version="1.0"?>
<configuration>
 <startup>
   <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
 </startup>
    <runtime>
      <legacyCorruptedStateExceptionsPolicy enabled="true" />
    </runtime>
</configuration>

本条设置告诉CLLacrosse 4.0,整个.NET程序都要选取老的不得了捕捉机制。

  • 在急需捕捉破坏性十分的函数外面加叁个HandleProcessCorruptedStateExceptions属性,那一个特性只调节一个函数,对托管程序的其它函数未有影响,比方:
[HandleProcessCorruptedStateExceptions]
try{
    //....
}
catch(Exception ex)
{
    Console.WriteLine(ex.ToString());
}

 SEH有两项十三分有力的效能。当然,首先是那几个管理模型了,因而,那篇散文首先深切阐明SEH提供的那八个管理模型。此外,SEH还应该有三个特意有力的机能,那将要下后生可畏篇文章中进行详尽介绍。

try-except入门
  SEH的那二个管理模型首要由try-except语句来完毕,它与标准C++所定义的不行管理模型特别相仿,也都以足以定义出受监察和控制的代码模块,以致定义非凡管理模块等。照旧老方法,看贰个事例先,代码如下: 
//seh-test.c

图片 1

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

图片 2

 呵呵!是否一点也不细略,并且与C++格外管理模型很相同。当然,为了与C++至极管理模型相差别,VC编写翻译器对重大字做了少于改造。首先是在种种首要字加上四个下划线作为前缀,那样既维持了语义上的大器晚成致性,此外也尽最大大概来制止了至关心拥戴要字的有超大概率以致名字矛盾而孳生的麻烦等;其次,C++十分管理模型是应用catch关键字来定义极度管理模块,而SEH是采取__except关键字来定义。而且,catch关键字背后往往好像采纳一个函数参数同样,能够是各体系型的特别数据对象;不过__except关键字则不一样,它背后跟的却是三个表明式(能够是各体系型的表明式,后边会进一步深入分析卡塔尔。

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

// 例程1
// 平面包车型大巴线性结构

图片 3

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


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

图片 4

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

图片 5

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

图片 6

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

图片 7

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

        puts("in except");
    }
}

图片 8

 1. 受监督的代码模块被实践(也即__try定义的模块代码卡塔尔;
  2. 万大器晚成地点的代码施行进程中,未有现身至极的话,那么调节流将转入到__except子句之后的代码模块中;
  3. 再不,就算现身分外的话,那么调节流将步向到__except前面包车型地铁表达式中,也即首先总计那几个表明式的值,之后再依赖那个值,来调整做出相应的拍卖。那个值有三种境况,如下:
  EXCEPTION_CONTINUE_EXECUTION (–1) 至极被忽略,调整流将要万分现身的点今后,继续复苏运转。
  EXCEPTION_CONTINUE_SEARCH (0) 万分不被识别,也即当前的这么些__except模块不是其风流倜傥可怜错误所对应的不利的十一分管理模块。系统将一连到上豆蔻梢头层的try-except域中世袭查找二个适中的__except模块。
  EXCEPTION_EXECUTE_HANDLEPRADO (1) 格外已经被辨认,也即眼下的那些那多少个错误,系统现已找到了并能够承认,那个__except模块正是理所必然的老大管理模块。调整流将步入到__except模块中。
 
try-except深入
  上边的原委中早就对try-except实行了应有尽有的刺探,然而有几许还从未阐述到。这正是什么样在__except模块中得到特别错误的相关新闻,那极度首要,它实际是开展非常错误管理的前提,也是对足够举办分层分等级处理的前提。总来说之,若无这一个最少的音讯,至极管理怎么着开展?因而赢得格外新闻非常的要害。Windows提供了四个API函数,如下:  

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

  此中GetExceptionCode()重临错误代码,而GetExceptionInformation()再次回到更全面包车型地铁音讯,看它函数的扬言,重返了一个LPEXCEPTION_POINTESportageS类型的指针变量。那么EXCEPTION_POINTE凯雷德S结构如何呢?如下,  

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

 

  呵呵!留意瞅瞅,那是否和上豆蔻梢头篇随笔中,客商程序所注册的不得了管理的回调函数的八个参数类型相像。是的,的确对的!当中EXCEPTION_RECO凯雷德D类型,它记录了生机勃勃部分与那二个相关的音讯;而CONTEXT数据结构体中著录了老大发生时,线程当时的上下文情形,首要满含贮存器的值。由此有了那一个音信,__except模块便得以对极其错误进行很好的分类和东山复起管理。可是特别必要注意的是,那八个函数只可以是在__except前面包车型客车括号中的表明式成效域内卓有成效,不然结果可能没有管教(至于为啥,在后头深远剖判相当模型的兑现时候,再做详细演说卡塔 尔(阿拉伯语:قطر‎。看一个例程吧!代码如下:

图片 9

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块中");
    }
}

图片 10

下边包车型地铁程序运转结果如下:

存款和储蓄爱戴万分
内层的except块中
Press any key to continue

 

  呵呵!认为没有错,我们能够在上边的次序根基之上退换一下,让它抛出八个被0除特别,看程序的周转结果是或不是如预期那样。
  末了还恐怕有一点索要解说,在C++的要命管理模型中,有三个throw关键字,也即在受监督的代码中抛出一个十三分,那么在SEH相当管理模型中,是否也理应宛如此一个近乎的要害字或函数呢?是的,没有错!SEH分外管理模型中,对极度划分为两大类,第豆蔻梢头种正是上边一些例程中所见到的,那类卓殊是系统丰裕,也被喻为硬件特别;还应该有生龙活虎类,正是程序中本身抛出极其,被称为软件万分。怎么抛出呢?照旧Windows提供了的API函数,它的表明如下:  

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
);

 

  很简短吗!实际上,在C++的卓殊管理模型中的throw关键字,最后也是对RaiseException()函数的调用,也便是说,throw是RaiseException的上层封装的越来越尖端大器晚成类的函数,这之后再详细解析它的代码实现。这里依然看几个简短例子吗!代码如下:

图片 11

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

    return 1;
}


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

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

}

图片 12

地方的程序运维结果如下:
hello
try块中
客商自定义的软件非常,错误代码:e0000001
except块中
world
Press any key to continue

 

地点的主次非常的粗略,这里不做越来越解析。大家供给珍视钻探的是,在__except模块中怎么着识别差别的百般,以便对那么些举办很好的归类管理。无可辩驳,它自然是通过GetExceptionCode()或GetExceptionInformation ()函数来获得当前的那多少个错误代码,实际也正是DwExceptionCode字段。非凡错误代码在winError.h文件中定义,它信守Windows系统下统风姿罗曼蒂克的错误代码的平整。每一种DWOEvoqueD被细分多少个字段,如下表所示:
举例大家能够在winbase.h文件中找到EXCEPTION_ACCESS_VIOLATION的值为0 xC0000005,将那一个充足代码值拆开,来深入分析看看它的次第bit位字段的涵义。
C 0 0 0 0 0 0 5 (十四进制卡塔尔国
1100 0000 0000 0000 0000 0000 0000 0101 (二进制)
第3 0位和第33个人都以1,表示该极度是五个严重的乖谬,线程也许不可以见到世袭往下运营,必要求及时管理恢复那一个可怜。第贰二十一个人是0,表示系统中已经定义了那一个代码。第2 8位是0,留待后用。第1 6 位至贰拾陆位是0,表示是FACILITY_NULL设备项目,它象征存取万分可产生在系统中任哪儿方,不是使用一定设备才发生的非常。第0位到第十五位的值为5,表示特别错误的代码。
  借使程序猿在程序代码中,布署抛出生龙活虎部分自定义类型的老大,必需求规划设计好温馨的那个类型的撤销合并,依照上边包车型地铁准绳来填充相当代码的次第字段值,如上边示例程序中抛出一个那些代码为0xE0000001软件分外。

总结
  (1卡塔 尔(阿拉伯语:قطر‎C++十分模型用try-catch语法定义,而SEH卓殊模型则用try-except语法;
  (2卡塔 尔(阿拉伯语:قطر‎ 与C++十分模型相仿,try-except也支撑多层的try-except嵌套。
  (3卡塔 尔(英语:State of Qatar)与C++十分模型差异的是,try-except模型中,一个try块只可以是有一个except块;而C++相当模型中,多个try块能够有多少个catch块。
  (4)与C++格外模型相同,try-except模型中,查找寻觅非凡模块的平整也是逐级向上扩充的。不过稍有分其余是,C++极度模型是遵照十分对象的连串来开展相称查找的;而try-except模型则分化,它经过三个表明式的值来打开推断。假若表明式的值为1(EXCEPTION_EXECUTE_HANDLE汉兰达卡塔 尔(英语:State of Qatar),表示找到了特别管理模块;若是值为0(EXCEPTION_CONTINUE_SEARCH卡塔 尔(阿拉伯语:قطر‎,表示继续向上大器晚成层的try-except域中继承寻觅其它大概格外的老大管理模块;假使值为-1(EXCEPTION_CONTINUE_EXECUTION卡塔尔,表示忽视那几个可怜,注意这一个值常常非常少用,因为它超轻松招致程序难以预测的结果,举例,死循环,以至形成程序的垮台等。
   (5) __except关键字背后跟的表明式,它能够是各种类型的表明式,举个例子,它能够是一个函数调用,或是贰个标准化表达式,或是八个逗号表达式,或索性正是一个整型常量等等。最常用的是一个函数表明式,并且通过利用GetExceptionCode()或GetExceptionInformation ()函数来收获当前的不得了错误新闻,便于程序员有效调控非常错误的归类管理。
   (6卡塔尔国SEH非凡管理模型中,分外被划分为两大类:系统非常和软件十分。当中国应用软件与技能服务总集团件万分通过RaiseException()函数抛出。RaiseException()函数的功效近似于C++格外模型中的throw语句。

C++有时用关键字(__leave)

**总结__finally块被实行的流水生产线时,无外乎两种情况。第风流罗曼蒂克种就是各类推行到__finally块区域内的代码,这种意况很简短,轻巧明白;第三种正是goto语句或return语句引发的主次调节流离开当前__try块成效域时,系统自动完毕对__finally块代码的调用;第二种便是由于在__try块中现身分外时,诱致程序调节流离开当前__try块成效域,这种意况下也是由系统活动完毕对__finally块的调用。无论是第 2种,仍然第3种状态,无可置疑,它们都会挑起相当大的体系开荒,编写翻译器在编写翻译此类程序代码时,它会为这两种意况计划超多的附加代码。平时第2种情状,被叫作“局地進展(LocalUnwinding卡塔尔”;第3种情状,被称作“全局张开(GlobalUnwinding卡塔尔国”。在后头演讲SEH完成的时候会详细分析到那一点。
第3种情状,也即由于出现至极而导致的“全局张开”,对于工程师而言,那可能是望眼欲穿制止的,因为你在运用非凡管理机制进步程序可信强壮性的还要,不可制止的会引起质量上此外的后生可畏对支付。呵呵!那世界实质上也算瞒公平的,有得必有失。

  但是,对于第2种意况,技术员完全能够使得地幸免它,幸免“局地进展”引起的不必要的额外花费。实际那也是与结构化程序设计观念相平等的,也即三个顺序模块应该独有二个输入和叁个张嘴,程序模块内尽量幸免使用goto语句等。不过,话虽如此,临时为了增长度序的可读性,程序猿在编辑代码时,偶然恐怕只可以动用部分与结构化程序设计理念相悖的做法,比方,在多少个函数中,也是有多处的return语句。针对这种状态,SEH提供了意气风发种拾贰分有效的折衷方案,那便是__leave关键字所起的效应,它既有着像goto语句和return语句那样近似的效果与利益(由于检查实验到有些程序运营中的错误,须求登时离开当前的 __try块作用域卡塔尔国,不过又幸免了“局地进展” 的额外花费。照旧看个例子吗!代码如下:** 

图片 13

#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块中");
}
}

图片 14

地方的程序运营结果如下:
hello
__try块中
__finally块中
world
Press any key to continue

本文由办公软件发布,转载请注明来源:__leave相当模型机制,Windows结构化分外管理浅析