Windows异常处理机制介绍

摘要:
throw引发的数据类型和catch项的&lt。当抛出在函数(或堆栈的框架)中找不到合适的catch块时。或者找出如何在不匹配catch块的情况下执行调用顶层(如主函数)的throw语句,但直接生成EXCEPTION_POINTERS类型的异常数据具有与catch参数完全不同的函数;这意味着返回到抛出异常的位置,并继续向下执行exception _ continue _ SEARCH(0)

转自:http://hi.baidu.com/zwegpcwvtybivxq/item/a8b7e6c15e8b15155150581f

最近做了一个Windows下的异常处理模块,查阅了一些新的资料,结合我自己的理解,将一些点滴记录如下,希望对兄弟们有所帮助。

一、C++标准异常

也就是try、throw、catch这三个关键字。

try
{
    ……
    throw <exception-data>
    ……
}

catch (<exception-declaration 1>)
{
    ……
}
catch (<exception-declaration 2>)
{
    ……
}
……

try 块中的throw会抛出一个数据<exception-data>,比如一个整数,一个字串,或是其他自定义类型的数据。这时,当前程序中止 执行,开始查找catch入口。throw抛出的数据类型与catch入口的<exception-declaration>数据类型必须匹 配,这一点类似函数调用的形参、实参匹配。一个try块可以对应多个catch块,这一点类似于函数的重载。当然,你也可以用catch (…)来接收所有可能抛出的数据。MFC提供了一些标准的抛出异常类型,如CFileException类、CDaoException类等,它们都是 CException类的派生类,使用MFC时可以了解一下,这里就不多说。

执行完catch块,程序会继续向下执行。

当 throw在本函数(或说栈的本帧)没有找到合适的catch块时,会向上一层调用函数(或说栈的上一帧)回溯,直到匹配到合适的try-catch块为 止。也就是说,try-catch块可以捕获到try块中调用(可以是多层调用)的函数中的,没被处理的异常。同时,try-catch块是可以嵌套的。

那么,有一个问题:没有try-catch块的,或查找到调用顶层(如main函数)都没有匹配上catch块的throw语句会如何执行呢?这在不同操作系统会有不同的处理,在Windows中则是由一个叫SEH的机制来处理的。

二、Windows SEH

SEH(Structured Exception Handling),即结构化异常处理,是Microsoft提供的异常处理机制。要了解这个机制,咱先来了解一下__try-__except关键字。

1. __try-__except关键字

__try
{
    ……
}

__except (<exception>)
{
    ……
}

__try- __except是Microsoft扩展出的C++关键字,__try块中出现错误或异常,一般不再用throw抛出,而是直接产生一个 EXCEPTION_POINTERS类型的异常数据,然后开始查找SEH例程入口(调试的情况除外)。首先就会找到与__try块对应的 __except块。__except的参数<exception>与catch的参数作用完全不同,也不类似于函数的参数,它主要是用于控 制后面的程序执行,为这几个值之一:

EXCEPTION_EXECUTE_HANDLER(1),表示下面执行__except块内及其后面的代码
EXCEPTION_CONTINUE_EXECUTION(-1),表示回到抛出异常处继续向下执行
EXCEPTION_CONTINUE_SEARCH(0), 表示查找下一个异常处理例程入口

Microsoft 提供两个函数GetExceptionCode(), GetExceptionInformation(),分别可以获取异常号和EXCEPTION_POINTERS类型的异常数据指针。而且这两个函数只 能在__except参数<exception>的表达式中使用。为了保证这一点,在Microsoft Visual C++(以下简称VC)中,编译器做了特殊处理,如果这两个函数没有在正确的位置,将产生编译错误。(这个感觉有点搞。)

所 以,__except一行一般会这样写:__except (ExceptFilterFunc(GetExceptionInformation())),其中ExceptFilterFunc是一个自定义的异 常处理例程,它输入一个EXCEPTION_POINTERS *类型的参数,返回EXCEPTION_EXECUTE_HANDLER、EXCEPTION_CONTINUE_EXECUTION或 EXCEPTION_CONTINUE_SEARCH。

(注:下面所提到的“异常处理例程”,不管是自定义的还是系统提供的,都是这种类型的函数,这种函数指针类型在winbase.h中被定义为LPTOP_LEVEL_EXCEPTION_FILTER。)

EXCEPTION_POINTERS结构中包含丰富的异常相关数据,主要有异常号、异常发生时寄存器的值等。

与try-catch一样,__try-__except也支持调用栈回溯,也可以嵌套,但没法重载。

另外,在VC中,还提供__try-__finally块和__leave关键字,这里不细说了,感兴趣的可以查查MSDN。

2. Windows异常处理步骤

回到上文的问题,没有匹配上catch、__except块的错误或异常将会如何处理呢?原来,包括__except块在内,SEH异常处理例程可以有多个,它们的入口地址形成一个链式结构,这个链式结构由Windows操作系统管理。

发生错误或异常后,Windows的处理顺序一般如下:

(1)中止当前程序的执行。

(2)如果程序处于被调试状态,向调试器发送EXCEPTION_DEBUG_EVENT消息。

(3) 如果程序没有被调试或者调试器未能处理异常,查找线程相关的异常处理例程(如对应__except块)并处理。如果前面查找到的例程返回 EXCEPTION_CONTINUE_SEARCH,且线程有多个异常处理例程,则沿这些例程入口地址组成的链式结构逐一向后查找,请求下一个例程处 理。

(4)如果线程没有对应的异常处理例程,或线程所有例程都返回EXCEPTION_CONTINUE_SEARCH,而且程序处于被调试状态,再次通知调试器。

(5)如果程序没有被调试或者调试器仍未处理异常,则进入主线程的“最终异常处理例程”链继续查找。

(6)“最终异常处理例程”链的最后是Windows默认的系统异常处理程序__CxxUnhandledExceptionFilter(),其处理通常是弹出一个异常对话框,上面显示一些异常信息,提供“关闭”、“调试”等按钮。

著 名的SetUnhandledExceptionFilter()函数就是在所谓“最终异常处理例程”链的 __CxxUnhandledExceptionFilter()之前插入一个自定义的异常处理例程,当这个例程返回 EXCEPTION_EXECUTE_HANDLER时,一般会直接结束进程。

三、两种异常处理机制的比较

我能想到的一些特征的比较:

C++标准异常

SEH

局部对象析构函数

执行

局部对象有析构函数,且用__try-__exception时,编译错误

可重载

有参数类型匹配

无条件处理

可移植

C++都有,不依赖操作系统平台

只有Windows提供

程序流程控制

catch块后只能继续向下执行

EXCEPTION_EXECUTE_HANDLER、EXCEPTION_CONTINUE_EXECUTION、

EXCEPTION_CONTINUE_SEARCH三种流程控制,多个处理例程的依次处理

数据通用

各种不同的异常数据类型

统一结构的异常数据

四、VC编译参数EH

在 VC中,你可能会发现一个怪异的现象,就是try-catch块无法捕获像“除0”、“空指针访问”之类的异常。原来,在VC中一般的错误和异常都是用 SEH来处理的,不等同于throw抛出的异常。而try-catch对结构化异常的处理,是由编译参数EH来控制的。

无EH参数

EHs(EHsc)

EHa(同Ehac)

try-catch

不处理异常

只处理C++标准异常,代码优化较好

处理C++标准异常和结构化异常,代码优化较差

__try-__except(VC2005及以后)

处理C++标准异常和结构化异常

处理C++标准异常和结构化异常

处理C++标准异常和结构化异常

__try-__except(VC2003及以前)

只处理结构化异常

只处理结构化异常

只处理结构化异常

从表中可以看出,EH参数对__try-__except块的处理并无影响。

从VC2005开始,SEH也可以统一捕获和处理C++标准异常。而在VC2003及之前,C++标准异常只能由catch块来捕获。

VC2005中,EH参数默认为EHsc。

附记:关于自定义SEH异常处例程的编写,如保存内存dump,保存调用栈,使用调试相关的.pdb .map文件等,网上相关的资料很多,需要可以查询。

免责声明:文章转载自《Windows异常处理机制介绍》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇ssh分发秘钥时出现错误“Permission denied (publickey,gssapi-keyex,gssapi-with-mic)”c# 定时关闭 MessageBox 或弹出的模态窗口下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

MFC入门(一)

Windows消息机制 基本概念解释 1) SDK和API SDK:软件开发工具包(Software Development Kit),一般都是一些被软件工程师用于为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件的开发工具的集合。 API函数: Windows操作系统提供给应用程序编程的接口(Application Programming Int...

RabbitMQ-C 客户端接口使用说明

  rabbitmq-c是一个用于C语言的,与AMQP server进行交互的client库。AMQP协议为版本0-9-1。rabbitmq-c与server进行交互前需要首先进行login操作,在操作后,可以根据AMQP协议规范,执行一系列操作。   这里,根据项目需求,只进行部分接口说明,文后附demo的github地址。 接口描述 接口说明:声明一...

虚表和虚表指针

  编译器:VS2015 0x01 基础概念   首先还是简单重复一下基础概念。   C++的多态性可以简单地概括为“一个接口,多种方法”,程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念。    1、多态性     指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:编译时多态性,运行时多态性。     a...

表变量与临时表

临时表: create table #tmpTbName(     colName1 int identity(1,1),     colName2 nvarchar(20))insert into #tmpTbName(colName1 ,colName2) select c1,c2 from c;drop table #tmpTbName;--删除临时...

apk反编译与破解

前段时间项目上线,单机游戏,想着肯定是会被破解的,但没想到,分分钟就被破了,游戏数据也是相当的差,于是闲暇时,也自己研究下安卓的破解。个人观点(装一下X):所谓反编译和破解付费等都只应用于学习交流,发布出去,就有点太不尊重他人劳动成果了。 使用工具:(当然首先得配好java环境,这是废话)   apktool : 资源文件获取,可以提取出图片文件和布局文件...

VIM使用系列之一——配置VIM下C/C++编程环境

作者:gnuhpc from http://blog.csdn.net/gnuhpc本文环境:ubuntu 9.10 1.基本配置建立三个目录:mkdir ~/.vim/{plugin,doc,syntax} –p建立配置文件:touch ~/.vimrc vimrc作为vim的主要配置文件,我们在配置VIM时首先对它进行基本的设置。打开你home目录下的...