异常处理与MiniDump详解(3) SEH(Structured Exception Handling)

在此我非常感谢博客版主 九天雁翎(JTianLing) 先生/女士 做出的辛苦劳动,写了这么好的文章,本人受益匪浅,特此感谢!

 

原文地址:
http://blog.csdn.net/vagrxie/archive/2009/07/27/4382591.aspx

 

常处理与MiniDump详解(3) SEH(Structured Exception Handling)


write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie

讨论新闻组及文件

一、   综述
SEH--Structured Exception Handling,是Windows操作系统使用的异常处理方式。

对于SEH,有点需要说明的是,SEH是属于操作系统的特性,不为特定语言设计,但是实际上,作为操作系统的特性,几乎就等同与面向C语言设计,这点很好理解,就像Win32 API,Linux下的系统调用,都是操作系统的特性吧,实际还是为C做的。但是,作为为C语言设计的东西,实际上可调用的方式又多了,汇编,C++对于调用C语言的接口都是比较方便的。

 

二、   基础篇
还是简单介绍一下SEH的使用,但是不准备太详细的介绍了,具体的详细介绍见参考中提及的书目。关于SEH的基本应用,《Windows核心编程》绝对是最佳读物(其实个人一直认为《Windows核心编程》是Windows编程领域必看的第二本书,第一本是《Programming Windows》。关于SEH更深入的一点的知识可能就要参考一些能用汇编讲解的书籍了,《Windows用户态程序高效排错》算是其中讲的不错的一本。

首先,SEH也有像C++异常一样的语法,及类try-catch语法,在SEH中为__try-except语法,抛出异常从throw改为RaiseException,在MSDN中的语法描述为:

__try

{

   // guarded code

}

__except ( expression )

{

   // exception handler code

}

 

见一个实际使用的例子:

例1:

#include <iostream>

#include <windows.h>

using namespace std;

 

int main()

{

    __try

    {

       RaiseException(0, 0, 0, NULL);

    }

    __except(EXCEPTION_EXECUTE_HANDLER)

    {

       cout <<"Exception Raised." <<endl;

 

    }

 

    cout <<"Continue running" <<endl;

}

 

这可能是最简单的SEH的例子了,输出如下:

Exception Raised.

Continue running

 

这个例子和普通C++异常的try-catch类似,也很好理解。只不过catch换成了except。

因为C语言没有智能指针,那么就不能缺少finally的异常语法,与JAVA,Python等语言中的也类似,(这是C++中没有的)finally语法的含义就是无论如何(不管是正常还是异常),此句总是会执行,常用于资源释放。

 

例2:

 

#include <iostream>

#include <windows.h>

using namespace std;

 

int main()

{

    __try

    {

 

       __try

       {

           RaiseException(0, 0, 0, NULL);

       }

       __finally

       {

           cout <<"finally here." <<endl;

 

       }

    }

    __except(1)

    {

 

    }

 

    __try

    {

 

       __try

       {

           int i;

       }

       __finally

       {

           cout <<"finally here." <<endl;

 

       }

    }

    __except(1)

    {

 

    }

    cout <<"Continue running" <<endl;

    getchar();

}

 

这个实例看起来过于奇怪,因为没有将各个try-finally放入独立的模块之中,但是说明了问题:

1.     finally的语句总是会执行,无论是否异常finally here总是会输出。

2.     finally仅仅是一条保证finally语句执行的块,并不是异常处理的handle语句(与except不同),所以,假如光是有finally语句块的话,实际效果就是异常会继续向上抛出。(异常处理过程也还是继续)

3.     finally执行后还可以用except继续处理异常,但是SEH奇怪的语法在于finally与except无法同时使用,不然会报编译错误。

如下例:

    __try

    {

       RaiseException(0, 0, 0, NULL);

    }

    __except(1)

    {

 

    }

    __finally

    {

       cout <<"finally here." <<endl;

 

    }

 

VS2005会报告

error C3274: __finally 没有匹配的try

这点其实很奇怪,难道因为SEH设计过于老了?-_-!因为在现在的语言中finally都是允许与except(或类似的块,比如catch)同时使用的。C#,JAVA,Python都是如此,甚至在MS为C++做的托管扩展中都是允许的。如下例:(来自MSDN中对finally keyword [C++]的描述)

using namespace System;

 

ref class MyException: public System::Exception{};

 

void ThrowMyException() {

    throw gcnew MyException;

}

 

int main() {

    try {

       ThrowMyException();

    }

    catch ( MyException^ e ) {

       Console::WriteLine(  "in catch" );

       Console::WriteLine( e->GetType() );

    }

    finally {

       Console::WriteLine(  "in finally" );

    }

}

 

当你不习惯使用智能指针的时候常常会觉得这样会很好用。关于finally异常语法和智能指针的使用可以说是各有长短,这里提供刘未鹏的一种解释,(见参考5的RAII部分,文中比较的虽然是JAVA,C#,但是实际SEH也是类似JAVA的)大家参考参考。

 

SEH中还提供了一个比较特别的关键字,__leave,MSDN中解释如下

Allows for immediate termination of the __try block without causing abnormal termination and its performance penalty.

简而言之就是类似goto语句的抛出异常方式,所谓的没有性能损失是什么意思呢?看看下面的例子:

#include <iostream>

#include <windows.h>

using namespace std;

 

int main()

{

    int i = 0;

    __try

    {

       __leave;

       i = 1;

    }

    __finally

    {

       cout <<"i: " <<i <<" finally here." <<endl;

    }

 

 

    cout <<"Continue running" <<endl;

    getchar();

}

 

 

输出:

i: 0 finally here.

Continue running

实际就是类似Goto语句,没有性能损失指什么?一般的异常抛出也是没有性能损失的。

MSDN解释如下:

The __leave keyword

The __leave keyword is valid within a try-finally statement block. The effect of __leave is to jump to the end of the try-finally block. The termination handler is immediately executed. Although a goto statement can be used to accomplish the same result, a goto statement causes stack unwinding. The __leave statement is more efficient because it does not involve stack unwinding.

 

意思就是没有stack unwinding,问题是。。。。。。如下例,实际会导致编译错误,所以实在不清楚到__leave到底干啥的,我实际中也从来没有用过此关键字。

 

#include <iostream>

#include <windows.h>

using namespace std;

 

 

void fun()

{

    __leave;

}

 

int main()

{

    __try

    {

       fun();

    }

    __finally

    {

       cout <<" finally here." <<endl;

    }

 

 

    cout <<"Continue running" <<endl;

    getchar();

}

 

三、   提高篇
1.      SEH的优点
1)    一个很大的优点就是其对异常进程的完全控制,这一点是C++异常所没有的,因为其遵循的是所谓的终止设定。

这一点是通过except中的表达式来控制的(在前面的例子中我都是用1表示,实际也就是使用了EXCEPTION_EXECUTE_HANDLER方式。

EXCEPTION_CONTINUE_EXECUTION (–1)   表示在异常发生的地方继续执行,表示处理过后,程序可以继续执行下去。 C++中没有此语义。

EXCEPTION_CONTINUE_SEARCH (0)   异常没有处理,继续向上抛出。类似C++的throw;

EXCEPTION_EXECUTE_HANDLER (1)  异常被处理,从异常处理这一层开始继续执行。 类似C++处理异常后不再抛出。

 

 

2)    操作系统特性,不仅仅意味着你可以在更多场合使用SEH(甚至在汇编语言中使用),实际对异常处理的功能也更加强大,甚至是程序的严重错误也能恢复(不仅仅是一般的异常),比如,除0错误,访问非法地址(包括空指针的使用)等。这里可以用一个例子来说明:

#include <iostream>

#include <windows.h>

using namespace std;

 

 

 

int main()

{

    __try

    {

       int *p = NULL;

       *p = 0;

    }

    __except(1)

    {

       cout <<"catch that" <<endl;

   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值