• 第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 = 5;
    
            //返回一个新的值。因执行return会提前结束try块会导致
            //局部展开,此时会先保存dwTemp,然后局部展开,执行finally,
            //最后返回try块来执行return。即,将finally提前到return之前执行!
            return (dwTemp);
        }
            __finally{
            //3.清除工作,这里简单输出一行文本
            printf("Finally块被执行!
    ");
        }
    
        //继续处理——以下这些代码将永远不会被执行
        dwTemp = 9;
        printf("dwTemp = %d
    ", dwTemp);
    }
    
    //2、Funcenstein3:goto引起的局部展开
    DWORD Funcenstein3(){
        DWORD dwTemp;
    
        //1.处理一些其他事情
        //...
    
        _try{
            //2.
            dwTemp = 5;
    
            //试图利用goto跳过finally。这也会导致局部展开。
            //当遇到goto时,会先局部展开,先执行finally,
            //最后返回try块来执行goto语句。即,将finally提前到goto之前执行!
            goto ReturnValue;
        }
            __finally{
            //3.清除工作,这里简单输出一行文本
            printf("Finally块被执行!
    ");
        }
    
        //继续处理——以下这些代码将永远不会被执行
        dwTemp = 9; //该行会被跳过
    
    ReturnValue:
        printf("dwTemp = %d
    ", dwTemp);
        return (dwTemp);
    }
    
    //3.FuncaDoodleDoo:Continue、Break引起的局部展开
    DWORD FuncaDoodleDoo(){
        DWORD dwTemp = 0;
        while (dwTemp <10){
            __try{
                //第2次循环时,dwTemp会等于2。由于continue会导致局部展开,
                //所以在执行continue前先执行finally块,dwTemp增到3
                //然后continue,进行第3次循环
                if (dwTemp == 2)
                    continue;
    
                //第3次循环,这时dwTemp等于3。由于break会导致提前离开try块
                //所以进行局部展开,在break前先调用finally块,因此dwTemp增
                //到4,然后break跳出循环,所以finally块外面的dwTemp++没被执行
                if (dwTemp == 3)
                    break;
            }
            __finally{
                dwTemp++;
            }
    
            dwTemp++;
        }
        dwTemp += 0;
        return (dwTemp);
    }
    
    //4.Funcenstein4函数:finally块内执行return
    DWORD Funcenstein4(){
        DWORD dwTemp;
    
        //1.处理一些其他事情
        //...
    
        _try{
            //2.
            dwTemp = 5;
    
            //返回一个新的值。因执行return会提前结束try块会导致
            //局部展开,此时会先保存dwTemp,然后局部展开,执行finally,
            //最后返回try块来执行return。即,将finally提前到return之前执行!
            return (dwTemp);
        }
            __finally{
            //3.清除工作,这里简单输出一行文本
            printf("Finally块被执行!
    ");
            //return (103); //在finally块中提前return这种行为是未定义行为。
                            //VC编译器直接报错。但有些编译器会让通过。
                            //此时在局部展开时,因try块时将返回值复制到一个
                            //临时变量,在等待这里的finally块返回。但因finally
                            //里面又要return,所以会将103写入那个临时变量。然后
                            //函数退出!注意,不再回到try块里了。                        
        }
    
        //继续处理——以下这些代码将永远不会被执行
        dwTemp = 9;
        printf("dwTemp = %d
    ", 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 = { 0 };
        osver.dwOSVersionInfoSize = sizeof(osver);
        osver.dwMajorVersion = 6;
        osver.dwMinorVersion = 0;
        osver.dwPlatformId = VER_PLATFORM_WIN32_NT;
    
        //准备条件掩码
        DWORDLONG dwlConditionMask = 0; //必须初始化为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 = 5; //这将引发非法访问内存        
            }
        }
        __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 (-1); 
                }
            } else{  //代码不保护
                TriggerException();
            }
        } else{
            TriggerException();//其中系统默认是保护的。
        }
    
        MessageBox(NULL, TEXT("进程正常结束"), 
                   TEXT("SEHTerm:即将退出主线程!"),MB_OK);
        return 0;
    }

    //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
    //
    
    1 TEXTINCLUDE 
    BEGIN
        "resource.h"
    END
    
    2 TEXTINCLUDE 
    BEGIN
        "#include ""winres.h""
    "
        ""
    END
    
    3 TEXTINCLUDE 
    BEGIN
        "
    "
        ""
    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
  • 相关阅读:
    C++经典书籍:游戏编程
    云计算学习笔记Hadoop简介,hadoop实现原理,NoSQL介绍...与传统关系型数据库对应关系,云计算面临的挑战
    A win for the Nokia N8 is a win for Qt
    Qt 为中国移动音乐客户端提供多平台支持
    诺基亚力邀App开发员加入Ovi以对抗苹果
    MeeGo手机或将跳票至2011年
    TinyXML:一个优秀的C++ XML解析器
    企业开发中Qt和.Net小谈
    Qt 的昨天,今天,明天
    学机械的看看吧,一般看不见的机械原理——全动画图解
  • 原文地址:https://www.cnblogs.com/5iedu/p/5228946.html
Copyright © 2020-2023  润新知