Windows结构化卓殊管理浅析,__leave万分模型机制

作者: 计算机工程  发布:2019-10-21

近年一贯被叁个主题素材所烦扰,就是写出来的次第老是出现无故崩溃,有的地点和煦理解恐怕有标题,可是有的地方又历来不能知道有何样难点。更加苦逼的政工是,大家的次第是索要7x24劳动客商,就算无需实时精准零差错,可是总不可能冒出断线错过数据状态。故刚好通过拍卖该难题,找到了有的减轻方案,怎么捕获探访不合法内部存款和储蓄器地址或者0除以三个数。进而就遇到了那些结构化万分管理,今就简短做个介绍认知下,方便大家遭逢相关主题素材后,首先知道难题由来,再正是怎样缓和。废话非常少说,上面进入正题。

转自:

怎么着是结构化万分管理

结构化万分管理(structured exception handling,下文简单称谓:SEH),是当做精力充沛种系统编写制定引进到操作系统中的,本人与语言非亲非故。在我们相依为命的顺序中央银行使SEH能够让大家集中精力开采重视意义,而把程序中所恐怕出现的老大进行合併的管理,使程序显得愈发从简且扩大可读性。

使用SHE,并不表示能够完全忽略代码中只怕出现的错误,不过我们能够将软件职业流程和软件非凡情形管理进展抽离,先聚集精力干主要且火急的活,再来管理这么些或许会遇上各个的乖谬的尤为重要不急切的标题(不火急,但相对重要)

当在程序中应用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)和足够管理(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)到处都以,而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事后,CL卡宴将会分别出有个别特出(都以SEH格外),将这个格外标记为破坏性十分(Corrupted State Exception)。针对这一个非常,CLLX570的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>

以此装置告诉CLWrangler 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_HANDLE景逸SUV (1) 至极已经被识别,也即日前的这么些非常错误,系统现已找到了并能够认同,那些__except模块就是不错的不得了管理模块。调节流将步向到__except模块中。
 
try-except深入
  上面的始末中后生可畏度对try-except进行了周密的了然,可是有一点还未曾演聊到。这正是什么样在__except模块中获得非常错误的相关音讯,那特别关键,它事实上是进展丰盛错误管理的前提,也是对极度实行分层分品级管理的前提。总之,若无那一个起码的信息,分外处理怎么着开展?因而收获至极音讯特别的显要。Windows提供了三个API函数,如下:  

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

  当中GetExceptionCode()重临错误代码,而GetExceptionInformation()再次回到更完善的音讯,看它函数的扬言,再次回到了一个LPEXCEPTION_POINTE兰德奥德赛S类型的指针变量。那么EXCEPTION_POINTE帕杰罗S结构如何呢?如下,  

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

 

  呵呵!留心瞅瞅,这是或不是和上风流倜傥篇小说中,客户程序所注册的老大管理的回调函数的七个参数类型一样。是的,的确没有错!个中EXCEPTION_RECOQashqaiD类型,它记录了活龙活现部分与极度相关的消息;而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系统下统后生可畏的错误代码的平整。每一个DWOEscortD被划分多少个字段,如下表所示:
举个例子大家能够在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位和第35位都以1,表示该特别是二个严重的谬误,线程恐怕否够继续往下运作,必要求及时管理复苏这些极其。第29人是0,表示系统中早就定义了那些代码。第2 8位是0,留待后用。第1 6 位至二十多少人是0,表示是FACILITY_NULL设备等级次序,它象征存取极度可发出在系统中其余地点,不是使用一定设备才发生的卓殊。第0位到第15位的值为5,表示拾贰分错误的代码。
  假使程序猿在程序代码中,安顿抛出风华正茂部分自定义类型的老大,必得求规划设计好温馨的那些类型的细分,根据地点的平整来填充相当代码的依次字段值,如上面示例程序中抛出三个那二个代码为0xE0000001软件非凡。

总结
  (1) C++至极模型用try-catch语法定义,而SEH分外模型则用try-except语法;
  (2) 与C++万分模型相似,try-except也扶助多层的try-except嵌套。
  (3) 与C++万分模型不一样的是,try-except模型中,二个try块只好是有一个except块;而C++万分模型中,八个try块可以有三个catch块。
  (4) 与C++非常模型相似,try-except模型中,查找寻找非常模块的平整也是逐级向上拓宽的。然而稍有分其他是,C++分外模型是依照非常对象的类型来进展相称查找的;而try-except模型则区别,它经过三个表明式的值来展开判别。若是表明式的值为1(EXCEPTION_EXECUTE_HANDLEENCORE),表示找到了非常管理模块;假使值为0(EXCEPTION_CONTINUE_SEARCH),表示继续向上风流倜傥层的try-except域中三翻五次搜索此外也许非常的百般管理模块;假若值为-1(EXCEPTION_CONTINUE_EXECUTION),表示忽视这些这些,注意这一个值日常少之甚少用,因为它相当轻易变成程序难以预测的结果,譬如,死循环,以至造成程序的崩溃等。
   (5) __except关键字背后跟的表明式,它能够是各连串型的表明式,举例,它能够是一个函数调用,或是一个口径表达式,或是二个逗号表明式,或差非常少正是贰个整型常量等等。最常用的是三个函数表明式,何况通过使用GetExceptionCode()或GetExceptionInformation ()函数来获取当前的可怜错误音讯,便于技术员有效调控非常错误的归类管理。
   (6) SEH十分管理模型中,卓殊被分开为两大类:系统十二分和软件万分。在那之中国Computer软件与技艺服务总公司件分外通过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

本文由贝博体育app发布于计算机工程,转载请注明出处:Windows结构化卓殊管理浅析,__leave万分模型机制

关键词:

上一篇:Win7系统开放C盘下文件夹Everyone权限
下一篇:没有了