windows终止处理程序( __try __finally) 简单解析

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介:

本文大部分来自《windows核心编程》。

例1

复制代码
//二话不说,直接上代码
int
Funcenstein2() { __try { return 3; } __finally { //在函数返回之前会处理__finally里的内容 cout<<"finally executed"<<endl; } return 4;//此函数返回3而不是4 }
复制代码

 

通过使用终止处理程序可以防止过早的执行return语句。当return语句试图退出try块的时候,编译器会让finally代码在它。即编译器保证finally代码块在出try块的时候return之前执行。

者可以想知道,编译器是如何保证此功能的呢?原来当编译器检查程序代码时,会发现try代码里有一个return语句。于是,编译器就会生成一些代码先将返回值(例子中的 3)保存在一个由它创建的一个临时变量里,然后再执行finally语句块。这个过程被称之前为局部展开(LOCAL UNWIND)。更确切的说,当系统因try代码提前退出finally时就会发生局部展开。一旦finally代码块执行完毕,编译器所创建的临时变量值就会返回给函数调用者。

 

由此可见,为了让整个机制运行起来,编译器必须生成一些额外的代码,而系统也要很执行一些额外的工作。在不同的cpu结构上,让终止处理工作起来的步骤也不同。需要注意的是,应该避免在try代码中使用return语句,因为这是对程序性能有害的。__leave关键字,它可以帮助我们发现那些有局部展开开销的代码

 

 例2

复制代码
int Funcenstein3()
{
    //在try中使用goto语句时,就会产生局部展开以执行finally代码块。
    //这一次当finally执行完之后。因为try和finally中都没有函数返回语句,
    //所以ReturnValue标签后面的代码也会执行。因此这函数返回4。
    //但是由于代码破坏了try块到finally的正常执行流程,可能有比较大的性能损失,其程序取决于cpu体系结构。

    int ret=0;
    __try
    {
        ret=5;
        goto ReturnValue;
    }
    __finally
    {
        cout<<"finally executed"<<endl;
    }
ReturnValue:
    return 4;
}
复制代码

 

 例3

在这个例子中,终止处理将真正证明它的价值。首先看一下代码

复制代码
DWORD Funcfurter1()
{
    DWORD dwTemp;

    //1. do any processing here

    __try
    {
        WaitForSingleObject(g_hSem,INFINITE);
        dwTemp=Funcinator(g_dwProtectedData);
    }
    __finally
    {
        ReleaseSemaphore(g_hSem,1,NULL);
    }
    return dwTemp;
}
复制代码

假设try代码块中Funcinator函数存在一个缺陷会导致程序访问非法的内存。如果没有SEH这种情况下最络导致Windows错误报告(WER)弹出一个对话框:“Application has stopped working”。这个对话框在Windows上经常可以见到。一旦用户取消这个对话框,进程就会终止。但信号量依然被占用并再也得不到释放。其他进程中的纯种就会因为无休止的等待这个信号量而得不到CPU时间片。如果把信号量放在finally之中,即使用try中调用的函数发生了内存访问违规这样的异常,这个信号量也可以被释放。但是,请注意,从Windows Vista开始,须显式地保护try/finally框架,以确保在异常抛出时,finally代码会执行。

 

 例4

现在不防做一个实验,判断一下这个函数的返回值

复制代码
DWORD FuncalDoodLeDoo()
{
    DWORD dwTemp=0;

    while (dwTemp<10)
    {
        __try
        {
            if(dwTemp==2)
                continue;
            if(dwTemp==3)
                break;
        }
        __finally
        {
            dwTemp++;
        }
        dwTemp++;
    }
    dwTemp+=10;
    return dwTemp;
}
复制代码

让我们逐步分析这个函数执行的过程:一开始将dwTemp赋值为0,然后try块中的代码开始执行,但是两个if语句都为false。于是程序正常进入到finally代码块,在这里给dwTemp的值上加1,而finally块后面的代码又将dwTemp加1。

 

下一次循环开时,dwTemp=2,第一个if为true,程序试如果没有__finally程序会跳到while条件判断处执行,但dwTemp值班不会改变,这将会是一个死循环。但是现在我们有终止处理程序,系统注意到continue语句将会导致提前跳出try块,于是强制执行finally语句块。 在finally语句块中dwTemp被增加到3.这次finally之后的代码块没有机会执行。因此finally执行完之后程序跳到循环顶部执行。

 

现在我们分析第三次迭代,这次第一个if判断表达式的值为false,第二个表达式的值为true,系统再次侦测到程序流想要提前跳出try块,于是调用finally代码块,这里的dwTemp增加到4.因为break语句的执行程序控制流从whhile循环后开始继续。因而finally块之后循环以内的代码就不会被执行了。而循环之后的代码将dwTemp的值设置为14。这是程序最终返回的结果。不用我指明,请教也不会写出FuncalDoodleDoo这样的代码。此处只是为了演示终止处理程序是如何工作的。‘

 

绝大多数部情况下,try块中的提前退出都会被终止处理程序所捕获,但是在进程或线程提前终止的情况下,系统没法保证finally代码块的执行。调用ExitThread或者ExitProcess可以马上终止纯种或进程,而不会引发finally执行。同样如果当前纯种或者进程因为另一个程序调用TerminateThread或TerminateProcess而不得不结束,finally代码块也不会被执行,有一些c运行期函数比如(abort),因为在其内部最络调用的是ExitProcess,也会导致finally块不能执行。我们没法阻止别的线程杀死我们的线程或进程,但是可以在自己的代码中尽量避免对ExitThread或ExitProces的草率调用。

 

例5

复制代码
DWORD Funcenstein4()
{
    DWORD dwTemp;
    
    //1. do any processing here    
    __try
    {
        WaitForSingleObject(g_hSem,INFINITE);
        
        g_dwProcectedData=5;
        dwTemp=g_dwProcectedData;
        //return the new value
        return dwTemp; 
    }
    __finally
    {
        ReleaseSemaphore(g_hSem,1,NULL);
        return 103;
    }
    dwTemp = 9;
    return dwTemp;
}
复制代码

在上面的函数中,try中的代码试图用return 返回给调用者。正如我们前面提到的那样,试图在try块中提前退出函数导致编程器生成一些额外代码,将函数返回结果保存在一个临时变量中,然后执行finally中又多了一个return,那么导致103被写入到编译器生成的临时变量时。从而覆盖了原先的值5。而返回103

 

相关文章
|
2月前
|
存储 算法 C++
【Qt应用开发】复刻经典:基于Qt实现Windows风格计算器(加减乘除、删除、归零功能全解析)
在Qt中,"栈"的概念主要体现在两个层面:一是程序设计中的数据结构——栈(Stack),二是用户界面管理中的QStackedWidget控件。下面我将分别简要介绍这两个方面:
114 4
|
18天前
|
开发者 C# Windows
WPF与游戏开发:当桌面应用遇见游戏梦想——利用Windows Presentation Foundation打造属于你的2D游戏世界,从环境搭建到代码实践全面解析新兴开发路径
【8月更文挑战第31天】随着游戏开发技术的进步,WPF作为.NET Framework的一部分,凭借其图形渲染能力和灵活的UI设计,成为桌面游戏开发的新选择。本文通过技术综述和示例代码,介绍如何利用WPF进行游戏开发。首先确保安装最新版Visual Studio并创建WPF项目。接着,通过XAML设计游戏界面,并在C#中实现游戏逻辑,如玩家控制和障碍物碰撞检测。示例展示了创建基本2D游戏的过程,包括角色移动和碰撞处理。通过本文,WPF开发者可更好地理解并应用游戏开发技术,创造吸引人的桌面游戏。
49 0
|
18天前
|
存储 开发者 C#
WPF与邮件发送:教你如何在Windows Presentation Foundation应用中无缝集成电子邮件功能——从界面设计到代码实现,全面解析邮件发送的每一个细节密武器!
【8月更文挑战第31天】本文探讨了如何在Windows Presentation Foundation(WPF)应用中集成电子邮件发送功能,详细介绍了从创建WPF项目到设计用户界面的全过程,并通过具体示例代码展示了如何使用`System.Net.Mail`命名空间中的`SmtpClient`和`MailMessage`类来实现邮件发送逻辑。文章还强调了安全性和错误处理的重要性,提供了实用的异常捕获代码片段,旨在帮助WPF开发者更好地掌握邮件发送技术,提升应用程序的功能性与用户体验。
22 0
|
18天前
|
API C# Shell
WPF与Windows Shell完美融合:深入解析文件系统操作技巧——从基本文件管理到高级Shell功能调用,全面掌握WPF中的文件处理艺术
【8月更文挑战第31天】Windows Presentation Foundation (WPF) 是 .NET Framework 的关键组件,用于构建 Windows 桌面应用程序。WPF 提供了丰富的功能来创建美观且功能强大的用户界面。本文通过问题解答的形式,探讨了如何在 WPF 应用中集成 Windows Shell 功能,并通过具体示例代码展示了文件系统的操作方法,包括列出目录下的所有文件、创建和删除文件、移动和复制文件以及打开文件夹或文件等。
34 0
|
18天前
|
数据库 Windows
超详细步骤解析:从零开始,手把手教你使用 Visual Studio 打造你的第一个 Windows Forms 应用程序,菜鸟也能轻松上手的编程入门指南来了!
【8月更文挑战第31天】创建你的第一个Windows Forms (WinForms) 应用程序是一个激动人心的过程,尤其适合编程新手。本指南将带你逐步完成一个简单WinForms 应用的开发。首先,在Visual Studio 中创建一个“Windows Forms App (.NET)”项目,命名为“我的第一个WinForms 应用”。接着,在空白窗体中添加一个按钮和一个标签控件,并设置按钮文本为“点击我”。然后,为按钮添加点击事件处理程序`button1_Click`,实现点击按钮后更新标签文本为“你好,你刚刚点击了按钮!”。
50 0
|
26天前
【Azure 应用服务】App Service 配置 Application Settings 访问Storage Account得到 could not be resolved: '*.file.core.windows.net'的报错。没有解析成对应中国区 Storage Account地址 *.file.core.chinacloudapi.cn
【Azure 应用服务】App Service 配置 Application Settings 访问Storage Account得到 could not be resolved: '*.file.core.windows.net'的报错。没有解析成对应中国区 Storage Account地址 *.file.core.chinacloudapi.cn
|
2月前
|
监控 安全 网络安全
Windows系统安全深度解析:挑战、策略与全面防护
对敏感数据进行加密是保护数据机密性的重要手段。使用强加密算法对敏感数据进行加密存储和传输,即使数据被窃取也无法被轻易解密。此外,还可以考虑使用全磁盘加密技术来保护整个系统的数据安全性。
|
20天前
|
监控 网络协议 Java
Tomcat源码解析】整体架构组成及核心组件
Tomcat,原名Catalina,是一款优雅轻盈的Web服务器,自4.x版本起扩展了JSP、EL等功能,超越了单纯的Servlet容器范畴。Servlet是Sun公司为Java编程Web应用制定的规范,Tomcat作为Servlet容器,负责构建Request与Response对象,并执行业务逻辑。
Tomcat源码解析】整体架构组成及核心组件
|
1月前
|
存储 NoSQL Redis
redis 6源码解析之 object
redis 6源码解析之 object
53 6
|
5天前
|
存储 缓存 Java
什么是线程池?从底层源码入手,深度解析线程池的工作原理
本文从底层源码入手,深度解析ThreadPoolExecutor底层源码,包括其核心字段、内部类和重要方法,另外对Executors工具类下的四种自带线程池源码进行解释。 阅读本文后,可以对线程池的工作原理、七大参数、生命周期、拒绝策略等内容拥有更深入的认识。
什么是线程池?从底层源码入手,深度解析线程池的工作原理

推荐镜像

更多