Friday, February 11, 2005

 

Exception Handling and Management

1.
C++编译器怎么实现异常处理

原始出处:http://www.codeproject.com/cpp/exceptionhandler.asp
翻译:sdssly,ancienttale


对于VC++实现异常处理的深入探讨

导论

相比较其他传统的语言,C++的一个变革的特征是支持异常处理。相对于传统语言的不清楚容易错误的错误处理机制,C++的异常处理是一个非常好的替代。在正常的代码和错误处理代码之间清楚的分割使得程序非常整洁和宜于维护。本文讨论编译器怎么实现异常处理。假设读者熟悉异常处理的语法。 本文包含一个异常处理的VC++的库来替代VC++的异常处理,使用这个函数:

install_my_handler();

在这以后,程序中发生的任何异常(包含抛出异常到 stack unwinding,调用catch块和继续执行)都使用我自己的异常处理库。

译者注:当异常出现时,正常的执行流被中断,异常处理机制开始在当前范围寻找匹配的处理函数。如果找不到,把当前函数从栈中弹出,在调用者中继续寻找。这个过程称为stack unwinding

像其他C++特征一样,C++的标准并没有指定异常处理的实现机制。这使得C++实现者可以使用任何实现机制。我将讲述VC++怎么实现的。VC++把异常处理置于SHE(Structured exception hangling)的上面。SHE是windows操作系统提供的结构化的异常处理。

SEH导论

在本讨论中,我将考虑那些显式的异常。例如被0除,空指针访问等。当异常出现,中断会产生,控制被转到OS。 OS调用异常处理,检查从异常发出的函数开始的函数调用顺序,执行stack unwinding和控制转移。我们可以开发自己的异常处理函数,在OS中注册。OS就会在异常事件时调用它们。

Windows定义了一个特别的结构用来注册:

EXCEPTION_REGISTRATION:

struct EXCEPTION_REGISTRATION

{

EXCEPTION_REGISTRATION *prev;

DWORD handler;

};

要注册自己的异常处理函数,创建这个结构并将它的地址保存在段(由FS寄存器指向)的0偏移处。如下面的伪汇编指令:

mov FS:[0], exc_regp

结构中的prev字段表示EXCEPTION_REGISTRATION链表。当我们注册了这个EXCEPTION_REGISTRATION结构,我们使用这个prev字段保存以前注册的结构的地址。

关于异常回调函数,windows要求异常处理的信号,定义在excp.h文件中:

EXCEPTION_DISPOSITION (*handler)(

_EXCEPTION_RECORD *ExcRecord,

void * EstablisherFrame,

_CONTEXT *ContextRecord,

void * DispatcherContext);

现在你可以忽略所有的参数和返回类型。下面的程序在OS中注册了一个异常处理句柄并将产生一个被0除的异常。这个异常被抓到并将打印一个消息:

#include

#include

using std::cout;

using std::endl;

struct EXCEPTION_REGISTRATION

{

EXCEPTION_REGISTRATION *prev;

DWORD handler;

};

EXCEPTION_DISPOSITION myHandler(

_EXCEPTION_RECORD *ExcRecord,

void * EstablisherFrame,

_CONTEXT *ContextRecord,

void * DispatcherContext)

{

cout << "In the exception handler" << g_div =" 0;" preg =" ®" handler =" (DWORD)myHandler;" prev =" (EXCEPTION_REGISTRATION*)" j =" 10" ret =" myfun();这样myfun()返回的结果就放到了ret里,如果你写成myfun();而不理它的返回值,你虽然不理它,但是仍然会有它返回值存放的地方,这就是一个临时的对象,在vc的调试环境了,看auto变量的页面就可以看到临时的变量)。请注意上面描述只是很典型的情况。而实际上,编译器可能会储存所有或部分的变量到寄存器里,以便获得更快的执行速度(译注:编译器优化)。堆栈是一个处理器(CPU)级就支持的概念(译注:之所以这么说,因为汇编代码里就有push和pop" var_10 =" dword" var_c =" dword" var_4 =" dword"> 里,用来描述变量运行时的类型。catchblock结构的第二个成员(图5)就是一个指向type_info结构的指针,代表了catch块参数的运行时的类型。type_info类重载了==操作符,用来判断两个类型是不是完全相同的类型。因此,所有的异常处理都要做这个比较(调用==操作符重载函数)来确认catchblock参数的type_info和产生的异常的type_info是否相等,从而判断当前的catch块能不能捕获当前异常。

异常处理从funcinfo结构里知道了catchblock的参数,但是怎么知道当前异常的type_info呢,当编译器遇到这样的语句

throw E();

它为这个抛出的异常创建一个excpt_info结构,参看图6。请注意名字可能和VC++编译器使用的有所不同,而且我只列出了有关的项。如图所示,异常的type_info可以通过excpt_info结构来访问。有些时候,异常处理要销毁异常(当catch块完成),也可能需要拷贝异常(在调用catch块之前),为了帮助异常处理完成这些任务,编译器产生异常的析构函数,拷贝构造函数和取异常对象的大小的函数(通过excpt_info结构)



如果catch块的参数是一个基类,产生的异常是它的派生类,异常处理仍然应该在异常产生时调用这个catch块。然而,比较这两个种类(基类和派生类)将返回false,因为这两个类型本来不是同一种类型。不管是type_info类提供的成员函数还是操作符,都不能判断两个类一个是不是另一个的子类。但是,在这样的情况下,异常处理却确实能捕获到这样的异常。这是怎么做到的呢?实际上,编译器为这个异常产生了更多的信息。如果异常类是从别的类派生的,那么etypeinfo_table(在结构excpt_info结构里)包含了etype_info(扩展的type_info,我命名的)指针指向所有的父类,这样异常处理比较catch块的type_info和catch块参数的所有的type_info(自己和自己的所有基类的type_info)。只要有一个匹配成功,catch块就会被执行。

在我总结这一节之前,至少还有一个问题,就是异常处理是怎么知道当前产生的异常的excpt_info在哪里?我将在下面回答这个问题。

VC++把throw语句编译成和下面类似的语句:

//throw E(); //compiler generates excpt_info structure for E.
E e = E(); //create exception on the stack
_CxxThrowException(&e, E_EXCPT_INFO_ADDR);
_CxxThrowException 把控制权传递给操作系统(通过软件中断,参看函数RaiseException),同时传递两个参数。操作系统在准备调用异常回调函数时,把这两个函数打包到结构_EXCEPTION_RECORD里。接着操作系统找到FS:[0]处的异常处理链头的第一个EXCEPTION_REGISTRATION结构,调用结构里的handle。指向EXCEPTION_REGISTRATION 的指针也就是异常处理的第二个参数。再提一下,在VC++里每一个函数在自己的那一帧堆栈上创建它自己的EXCEPTION_REGISTRATION 结构,同时把这个结构注册到系统。第二个参数对异常处理很重要,通过它可以找到像ID这样的成员(没有ID就不能确定catch块)。这个参数也能使异常处理知道函数的堆栈帧(这对清除本帧变量很有用),同时也能往下找出更多的EXCEPTION_REGISTRATION节点(这对清除堆栈很有用)。第一个参数是一个指向_EXCEPTION_RECORD结构的指针,通过它异常处理能找到异常对象的指针和excpt_info结构。异常处理的返回值定义在EXCPT.H里

(译注:

typedef enum _EXCEPTION_DISPOSITION {

ExceptionContinueExecution,

ExceptionContinueSearch,

ExceptionNestedException,

ExceptionCollidedUnwind }

EXCEPTION_DISPOSITION; )

EXCEPTION_DISPOSITION (*handler)(
_EXCEPTION_RECORD *ExcRecord,
void * EstablisherFrame,
_CONTEXT *ContextRecord,
void * DispatcherContext);
你能忽略最后的两个参数。异常处理的返回值是一个枚举(EXCEPTION_DISPOSITION类型)。在前面我们已经说到,如果异常处理不能找到catch块,它就返回ExceptionContinueSearch给操作系统。其他的不太重要的信息在结构_EXCEPTION_RECORD里,这个结构定义在WINNT.H里:

struct _EXCEPTION_RECORD
{
DWORD ExceptionCode;
DWORD ExceptionFlags;
_EXCEPTION_RECORD *ExcRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
DWORD ExceptionInformation[15];
} EXCEPTION_RECORD;
ExceptionInformation数组的个数和入口的种类由ExceptionCode决定。如果ExceptionCode表示异常是个C++异常(ExceptionCode是0x06d7363,当通过throw来产生异常就会出现这样的情况),那么ExceptionInformation数组包含了指向异常对象和excpt_info结构的指针。而其他的异常,基本上都没有入口。其他的异常有除0,访问拒绝等,都能在WINNT.H里找到它们对应的值。

异常处理通过EXCEPTION_RECORD结构的ExceptionFlags成员来决定异常时采取什么动作。如果这个值是EH_UNWINDING (定义在except.inc里),提示异常处理清除堆栈正在进行,这时,异常处理应该清除了函数堆栈帧然后返回。清除函数帧包括这样的动作,找到所有的在异常发生时还没有释放的局部变量,然后调用它们的析构函数。这点下一节继续讨论。否则,异常处理不得不在函数里继续查找匹配的catch块,然后调用找到的catch块。

2.
好文大家一起引用--Mark Treadwell

Thanks to Mark Treadwell, Ohad Israeli and CCBoy

From http://www.cnblogs.com/ccboy/archive/2004/07/18/25340.html

This is a re-post of Ohad Israeli's one, I found this very useful and included here for my reference.
Many Thanks to Mark Treadwell and Ohad Israeli for sharing the info.
Mark Treadwell has a great post with lots of sources regarding exception management
MSJ, Matt Pietrek: A Crash Course in Structured Exception Handling
MSDN Mag, Matt Pietrek: Under the Hood - New Vectored Exception Handling in Windows XP
Blog, Chris Brumme: The Exception Model
PAG, MSDN, Exception Management Architecture Guide [article only]
PAG, MSDN, Exception Management Application Block for .NET [article only]
Cat Francis, MSDN, Introduction to Exception Handling in Visual Basic .NET [article only]
Eric Gunnerson, MSDN, Writing Exceptional Code [article only]
Eric Gunnerson, MSDN, The Well-Tempered Exception [article and code: exception.exe]
MSDN Library, .NET Framework Class Library, Exception Class
MSDN Library, .NET Framework SDK, .NET Framework Developer's Guide, Handling and Throwing Exceptions, with the following subsections:
Exceptions Overview - Provides an overview of common language runtime exceptions
Exception Class - Describes the elements of an exception object
Exception Hierarchy - Describes the exceptions that most exceptions derive from
Exception Handling Fundamentals - Explains how to handle exceptions using Catch, Throw, and Finally statements
Using the Try/Catch Block to Catch Exceptions - Describes how to use the Try/Catch block to handle exceptions
Using Specific Exceptions in a Catch Block - Describes how to catch specific exceptions
Throwing Exceptions - Describes how to throw exceptions as well as how to catch and then re-throw exceptions
Using User-Defined Exceptions - Describes how to create your own exception classes
Using User-Filtered Exceptions - Describes how to set up filtered exceptions
Using the Finally Block - Explains how to use the Finally statement in an exception block
Best Practices for Handling Exceptions - Describes suggested methods for handling exceptions
Handling COM Interop Exceptions - Describes how to handle exceptions thrown and caught in unmanaged code
Jesse Liberty, MSDN Magazine, December 2002, .NET Exceptions: Make the Transition from Traditional Visual Basic Error Handling to the Object-Oriented Model in .NET [article only]
Wesner Moise, Assert Enhancements [article and code, assert_src.zip]
Kevin McFarlane, Code Project, Managing Unhandled Exceptions in .NET [article and code]
Marc Clifton, Code Project, A Treatise on Using Debug and Trace classes, including Exception Handling [article and code, DebugTreatise_src.zip]
Jason Bock, Creating a Reusable Error Dialog for .NET Applications [article and code]
Kathleen Dollard, Visual Studio Magazine, Display Dissected Exceptions [article and code]
2004.07.21补上另外的一些 使用异常管理程序块来记录错误 http://www.zdnet.com.cn/developer/code/story/0,2000081534,39159209-1,00.htm

Instrumenting Your Code for Exception Managementhttp://www.awprofessional.com/articles/article.asp?p=102216&seqNum=5

Custom publishers for the Exception Management Application Blockhttp://builder.com.com/5100-6374_14-5110833.html

Examining the Exception Management Application Block (EMAB) http://aspnet.4guysfromrolla.com/demos/printPage.aspx?path=/articles/032404-1.aspx

Automate Exception Logginghttp://www.ftponline.com/vsm/2004_06/magazine/columns/gettingstarted/default_pf.aspx

Custom Exception Handling http://weblogs.asp.net/erobillard/archive/2004/04/14/113471.aspx

Rich Custom Error Handling with ASP.NEThttp://msdn.microsoft.com/asp.net/community/authors/elirobillard/default.aspx?pull=/library/en-us/dnaspp/html/customerrors.asp



<< Home

This page is powered by Blogger. Isn't yours?