首页
社区
课程
招聘
[原创]Windows系统程序设计之结构化异常处理
发表于: 2006-9-20 20:21 20914

[原创]Windows系统程序设计之结构化异常处理

2006-9-20 20:21
20914

目录:
一、        SEH的概念、特性
二、        SEH的基本使用方法
1、        结束异常程序
(1)try块的自然退出与非自然退出
(2)finally块的清理功能及对程序结构的影响
(3)关键字__leave
2、        异常处理程序
(1)异常处理的基本流程
(2)异常过滤器
(3)全局展开
(4)暂停全局展开
3、        未处理异常(顶层异常处理)
三、        SEH相关数据结构的介绍
1、        EXCEPTION_POINTER结构
2、        EXCEPTION_RECORD结构
3、        EXCEPTOIN_REGISTRATION结构
4、异常处理链结构图
四、        VC++编译器级SEH的具体实现
1、        VC扩展异常帧
2、        VC异常帧堆栈布局
3、        两个实例程序:显示异常帧信息
4、        实例分析及特性介绍
5、        VC中的定层异常处理
6、        VC搜索异常处理程序流程

五、参考资料

--------------------------------------------------------------------------

前言:
    对于这片文章应该是我写的最认真的一篇了,断断续续地写了将近一个月,从最初的复习性的回顾,收集更多资料,反复地整理思路,查阅Windows源码中的相关的源码,设计文章的整体框架,到每一个部分的详细设计,包括流程图的设计。每一个过程都进展的并不顺利,由于时间的关系每星期只能有2~3次的大段时间的在电脑面前,所以思路一直在被打断,整理文章写的并不流畅。有些思路或许已经不知道遗忘在哪个角落,但我也尽量把相关方面的知识点都讲到。本文的重点在于SEH原理方面的介绍,从单纯的使用角度来看,比较简单,但如果适当的使用SEH机制,这在很大程度上与使用者对SEH的理解程度有很大关系,因而对于具体的实现,文中只介绍重点及需要注意的地方,具体使用方法可参见参考文献[1]。

一、        SEH的概念、特性

        SEH,结构化异常处理,是作为一种系统机制引入到操作系统中的,本身与语言无关。在我们自己的程序中使用SEH可以让我们集中精力开发关键功能,而把程序中所可能出现的异常进行统一的处理,使程序显得更加简洁且增加可读性。

        当在程序中使用SEH时,就变成编译器相关的。其所造成的负担主要由编译程序来承担,例如编译程序会产生一些表(table)来支持SEH的数据结构,还会提供回调函数。

二、        SEH的基本使用方法

1、        结束异常程序
        一个结束异常程序能确保调用和执行一个代码块,对应与具体的实现,结束处理程序的结构如下所示:
                __try {
                        // 受保护的代码
                }
                __finally {
                        // 结束处理程序
                }
(1)try块的自然退出与非自然退出
        try块可能会因为return,goto,异常等非自然退出,也可能会因为成功执行而自然退出。但不论try块是如何退出的,finally块的内容都会被执行。        请看下面两个程序:


        通过使用结束处理程序,可以避免return语句的过早执行。当retrun 试图退出try块时,编译程序要保证finally块中的代码首先被执行。这事实上就是一个局部展开的过程,当从try块的过早退出强制控制转移到finally块时,都将引起局部展开。

(2)finally块的清理功能及对程序结构的影响
        写过软件的朋友一般都有这样一个影响:在编码的过程中需要加入需要检测,检测功能是否成功执行,若成功的话执行这个,不成功的话需要作一些额外的清理工作,例如释放内存,关闭句柄等。如果检测不是很多的话,倒没什么影响;但若又许多检测,且软件中的逻辑关系比较复杂时,往往需要化很大精力来实现繁芜的检测判断。结果就会使程序看起来结构比较复杂,大大降低程序的可读性,而且程序的体积也不断增大。

        事实上可以用SEH 来解决,把一些相关函数的清理代码都放在finally块,只需要在其中加一些适当的判断,不需要回到每个可能失败的地方添加清理代码。下面的FunSampleA函数是一个常规的函数,FunSampleB引入了SEH结束处理程序机制:


        这两个函数的功能是一样的。可以看到在FunSampleA中的清理函数(CloseHandle)到处都是,而在FunSampleB中的清理函数则全部集中在finally块,如果在阅读代码时只需看try块的内容即可了解程序流程。这两个函数本身都很小,可以细细体会下这两个函数的区别。

(3)关键字__leave

        在try块中使用__leave关键字会使程序跳转到try块的结尾,从而自然的进入finally块。

        对于上例中的FunSampleB,try块中的3个return完全可以用__leave来替换。两者的区别是用return会引起try过早退出系统会进行局部展开而增加系统开销,若使用__leave就会自然退出try块,开销就小的多。
       
        但有一种情况下必须使用__leave而不能使用return,即当finally块后还需要执行一定的功能,如下所示:


2、异常处理程序
异常处理程序能在程序发生异常时进行相应的处理,对应与具体的实现,异常处理程序的结构如下所示:
                __try {
                        // 受保护的代码
                }
                __except ( /*异常过滤器exception filter*/ ) {
                        // 异常处理程序exception handler
                }

(1)异常处理的基本流程(注:此流程图来源于参考资料[1])
  

(2)异常过滤器

异常过滤器只有三个可能的值(定义在Windows的Excpt.h中):
EXCEPTION_EXECUTE_HANDLER
EXCEPTION_CONTINUE_SERCH
EXCEPTION_CONTINUE_EXECUTION

下面是两种基本的使用方法:
方式一:直接使用过滤器的三个返回值之一
__try {
   ……
}
__except ( EXCEPTION_EXECUTE_HANDLER ) {
   ……
}

方式二:自定义过滤器
__try {
   ……
}
__except ( MyFilter( GetExceptionCode() ) )
{
   ……
}

LONG MyFilter ( DWORD dwExceptionCode )
{
        if ( dwExceptionCode == EXCEPTION_ACCESS_VIOLATION )
                return EXCEPTION_EXECUTE_HANDLER ;
        else
                return EXCEPTION_CONTINUE_SEARCH ;
}

(3)全局展开
        首先来看一下全局展开的基本流程(此流程图来源于参考资料[1]):


        接下来看一个全局展开的实例(源码GlobalUnwindSample.cpp):

#include <iostream.h>
#include <windows.h>

static unsigned int nStep = 1 ;

void Function_B ()
{
	int x, y = 0 ;
	__try {
		x = 5 / y ;	// 引发异常
	}
	__finally
	{
		cout << "Step " << nStep++ << " : 执行Function_B的finally块的内容" << endl ;
	}
}

void Function_A ( )
{
	__try {
		Function_B () ;
	}
	__finally
	{
		cout << "Step " << nStep++ << " : 执行Function_A的finally块的内容" << endl ;
	}
}

long MyExcepteFilter ( )
{
	cout << "Step " << nStep++ << " : 执行main的异常过滤器" << endl ;
	return EXCEPTION_EXECUTE_HANDLER ;
}

int main ()
{
	__try {
		Function_A () ;
	}
	__except ( MyExcepteFilter() )
	{
		cout << "Step " << nStep++ << " : 执行main的except块的内容" << endl ;
	}
	
	return 0 ;
}

/*输出结果:
Step 1 : 执行main的异常过滤器
Step 2 : 执行Function_B的finally块的内容
Step 3 : 执行Function_A的finally块的内容
Step 4 : 执行main的except块的内容
*/

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (20)
雪    币: 47147
活跃值: (20445)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
2
最初由 北极星2003 发布
标 题:Windows系统程序设计之结构化异常处理
作 者:北极星2003
时 间:2006-09-20
链 接:http://bbs.pediy.com/showthread.php?s=&postid=226905#post226905

........


这个不用自己填,设置精华后,论坛自动加上的。;)
2006-9-20 20:36
0
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
3
2006-9-20 20:40
0
雪    币: 179
活跃值: (131)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
4
北极星版版又出手了
十分感谢!!
2006-9-20 23:01
0
雪    币: 214
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
查阅Windows源码中的相关的源码


弱弱的问下.....
这句话的意思是?
2006-9-20 23:25
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
北极星大哥写的太精彩了`~~辛苦拉
2006-9-20 23:43
0
雪    币: 234
活跃值: (370)
能力值: ( LV9,RANK:530 )
在线值:
发帖
回帖
粉丝
7
文章精彩!
2006-9-21 01:07
0
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
8
最初由 colboy 发布
查阅Windows源码中的相关的源码



弱弱的问下.....
........


网上有Windows2000和NT的部分源码,我阅读其中的异常处理相关部分的代码,例如RtlDispatchException,RtlpVirtualUnwind,RtlVirtualUnwind等函数的具体实现,虽然不能完全看懂每个语句,但大致流程还是清楚了
2006-9-21 11:48
0
雪    币: 8201
活跃值: (2701)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
请参看https://www.openrce.org/articles/中的Reversing Microsoft Visual C++ Part I: Exception Handling。其中许多知识相当精彩。我曾发过帖子(http://bbs.pediy.com/showthread.php?s=&postid=195773#post195773),但无人翻译。其实,对于seh的实现,ms有自己的一套,而borland有自己的一套。有兴趣的可具体分析一下vc++、bc++、delphi,总结一下就会得到编译器实现的思路。但说白了,就是可以找到一种对应关系。
2006-9-21 13:02
0
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
10
最初由 option 发布
请参看https://www.openrce.org/articles/中的Reversing Microsoft Visual C++ Part I: Exception Handling。其中许多知识相当精彩。我曾发过帖子,但无人翻译。http://bbs.pediy.com/showthread.php?s=&postid=195773#post195773


你翻译了一段,怎么没继续下去?
另外,翻译相关的内容发到翻译版块比较适合
2006-9-21 13:06
0
雪    币: 8201
活跃值: (2701)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
因为时间和性格的关系,再者我认为学不好外文搞任何有点技术的东西都不利于进一步的发展。
2006-9-21 13:12
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
markmark
2006-9-21 22:02
0
雪    币: 8107
活跃值: (1955)
能力值: ( LV8,RANK:122 )
在线值:
发帖
回帖
粉丝
13
不错,标记一下
2006-9-22 12:33
0
雪    币: 441
活跃值: (149)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
14
谢谢楼主的文章

好文,做个记号,方便下次自己搜索方便!
2006-9-22 22:33
0
雪    币: 440
活跃值: (737)
能力值: ( LV9,RANK:690 )
在线值:
发帖
回帖
粉丝
15
绝对的精品
2006-9-24 16:21
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
https://www.openrce.org/articles/full_view/23
2006-9-28 22:37
0
雪    币: 253
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
17
精彩,学习了
2006-9-28 23:04
0
雪    币: 122
活跃值: (45)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
18
收藏,回去?坐!
2006-9-30 08:28
0
雪    币: 750
活跃值: (228)
能力值: ( LV9,RANK:780 )
在线值:
发帖
回帖
粉丝
19
做个记号,绝对的精品
谢谢
2006-9-30 09:12
0
雪    币: 200
活跃值: (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
强!!!!!!!!!!!!!!!!!顶一下
2006-9-30 12:10
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
非常感谢搂住,希望搂住继续给我们带来惊喜
2006-10-11 14:22
0
游客
登录 | 注册 方可回帖
返回
//