Visual C++异常处理机制原理与应用(一)—— C/C++结构化异常处理之try-finally终止处理的使用与原理(上)

异常处理是我们日常编程会不时用到但是却很少深入了解的部分,也是硬件、操作系统、编译器与用户程序需要密切配合才能完成的一个复杂过程。我之前学习Win32汇编时了解过Win32系统用户层的SEH(结构化异常处理),但对于Visual C++中的异常处理机制在实际编程中应该如何应用,以及它是如何利用Windows系统的SEH构建自己的异常处理机制等方面依然存在疑问。所以在这一系列文章中,我会对Visual C++中的异常处理机制的应用进行介绍、归纳和总结,并试图揭示其实现原理。

终止型异常处理

Visual C++提供的终止型异常处理机制想要实现如下功能:无论被__try保护的代码块是否能正常执行完(即不管其中是否发生异常、是否有执行流程向__try代码块外转移),__finally块中的代码都能被执行。

这在编程中是非常实用的:可以将可能发生异常或者产生错误的代码用__try块保护起来,并将解锁或者清理的代码放在__finally块中,增强程序的健壮性。下面给出两种经典应用场景:

  1. 在__finally块中执行解锁操作

    try
    {
        // 1.执行加锁、申请资源等操作,获取对某资源的使用权
        // 2.对该资源进行操作,操作过程可能发生异常
    }
    __finally
    {
        // 执行解锁、释放资源等操作,释放对该资源的使用权
    }

    这种情况下,可以避免在执行P操作获取对该资源的使用权后,在操纵该资源时发生异常或错误,使得V操作不能被执行,最终导致其他需要访问该资源的线程全部在P操作上死等,而永远无法获取到该资源的使用权。

  2. 在__finally块中执行清理操作

     try
     {
        bool bRet1 = false;
        bool bRet2 = false;
        bool bRet3 = false;
    
        // 1. 执行第1步操作
        if (/* 第1步执行不成功 */)
        {
            __leave;
        }
        bRet1 = true;   // 表明第1步执行成功
    
        // 2. 执行第2步操作
        if (/* 第2步执行不成功 */)
        {
            __leave;
        }
        bRet2 = true;   // 表明第2步执行成功
    
        // 3. 执行第3步操作
        if (/* 第3步执行不成功 */)
        {
            __leave;
        }
        bRet3 = true;   // 表明第3步执行成功
    
        // 4. 执行后续操作
    }
    __finally
    {
        if (bRet3)
        {
            // 清理、释放第3步中打开的资源
        }
        if (bRet2)
        {
            // 清理、释放第2步中打开的资源
        }
        if (bRet1)
        {
            // 清理、释放第1步中打开的资源
        }
    }

    个人认为这种情况可能是终止型异常处理最典型的应用了。Windows编程中有很多步骤都有如下特点:

    • 后一步依赖前一步的执行成功
    • 如果后一步执行失败,需要把前一步占用的资源释放掉

    如果不适用终止型异常处理来完成这些步骤,那么这部分代码将变的冗长而繁琐,在每一步操作后的判断语句中,都需要在判定执行失败后清理之前所有步骤占用的资源。

正常情况下的执行流程

首先分析执行流程正常转移的情况,此时__try块中被保护的代码不会提前退出,所以无需进行局部展开,是效率最高、开销最小的情况。

DWORD funcTest01()
{
    DWORD dwTemp = 3;
    __try
    {
        dwTemp = 4;
    }
    __finally
    {
        dwTemp = 6;
        if (AbnormalTermination())
        {
            cout << "__try块中执行时提前退出了" << endl;
        }
        else
        {
            cout << "执行流程自然转到了__finally块中" << endl;
        }
    }

    cout << dwTemp << endl;
    return 0;
}

从执行流程上分析,__try块中代码执行完后,执行流程转到__finally块中,因此对于__try块中的代码来说,属于正常终止的情况。

原理分析

反汇编代码

对应的反汇编如下:

     5: DWORD funcTest01()
     6: {
001523F0 55                   push        ebp  
001523F1 8B EC                mov         ebp,esp  
001523F3 6A FE                push        0FFFFFFFEh  
001523F5 68 E8 9E 15 00       push        159EE8h  
001523FA 68 50 29 15 00       push        offset _except_handler4 (0152950h)  
001523FF 64 A1 00 00 00 00    mov         eax,dword ptr fs:[00000000h]  
00152
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值