jimmy 战志杰 编译
本文编译自Jeffrey Richter先生的“Advanced Windows”部分章节。
1、引言
在“C++中例外的处理”一文中(见
计算机世界网2001年12月20日),我们讨论了C++中的例外(或异常)处理。本文将进一步探讨Visual C++中的结构异常处理。
想象一下,如果在编程过程中你不需要考虑任何错误,你的程序永远不会出错,有足够的内存,你需要的文件永远存在,这将是一件多么愉快的事。这时你的
程序不需要太多的if语句转来转去,非常容易写,容易读,也容易理解。如果你认为这样的编程环境是一种梦想,那么你就会喜欢结构异常处理(structu reed exception handling)。
结构异常处理的本质就是让你专心于如何去完成你的任务。如果在程序运行过程中出现任何错误,系统会接收(catch)并通知(notify)你。虽然利用结构异常处理你不可能完全忽略你的
程序出错的可能性,但是结构异常处理确确实实允许你将你的主要任务与错误处理分离开来。这种分离使得你可以集中精力于你的工作,而在以后在考虑可能的错误。
结构异常处理的主要工作是由编译器来完成的,而不是由操作系统。编译器在遇到例外
程序段时需要产生额外的特殊代码来支持结构异常处理。所以,每一个编译器产品供应商可能使用自己的语法和规定。这里我们采用微软的Visual C++编译器来进行讨论。
注意不要将这里讨论的结构异常处理与C++中的异常处理混为一谈。C++中的异常处理是另一种形式的异常处理,它使用了C++的关键词catch和throw。
微软最早在Visual C++版本2.0引进结构异常处理。结构异常处理主要由两部分组成:中断处理(termination handling)和例外处理(exception handling)。
2、中断处理句柄(termination handler)
2.1、中断处理句柄定义
中断处理句柄保证了,不论进程如何离开另一程序段--这里称之为守卫体(guarded body),该句柄内的
程序段永远会被调用和执行。微软的Visual C++编译器的中断处理句柄语法为
__try {
// Guarded body
.
.
.
}
__finally {
// Termination handler
.
.
.
}
这里的__try和__finally勾画出了中断处理句柄的两个部分。在上面的例子中,操作系统和编译器一起保证了不论包含在__try内的程序段出现何种情况,包含在__finally内的程序段永远会被运行。不论你在__try内的
程序段中调用return、goto或longjump,__finally内的中断处理句柄永远会被调用。其流程为
// 1、执行try
程序段前的代码
__try {
// 2、执行try
程序段内的代码
}
__finally {
// 3、执行finally
程序段内的代码
}
// 4、执行finally
程序段后的代码
2.2、几个例子
下面我们通过几个具体例子来讨论中断处理句柄是如何工作的。
2.2.1、例1--Funcenstein1
清单一给出了我们的第一个例子。
DWORD Funcenstein1(void) {
DWORD dwTemp;
// 1. Do any processing here.
.
.
.
__try {
// 2. request permission to access protected data, and then use it.
WaitForSingleObject(g_hSem, INFINITE);
g_dwProtectedData = 5;
dwTemp = g_dwProtectedData;
}
__finally {
// 3. Allow others to use protected data.
ReleaseSemaphore(g_hSem, 1, NULL);
}
// 4. Continue processing.
return (dwTemp);
}
例1 Funcenstein1函数代码
在函数Funcenstein1中,我们使用了try-finally
程序块。但是它们并没有为我们做多少工作:等待一个指示灯信号,改变保护数据的内容,将新的数据指定给一个局域变量dwTemp,释放指示灯信号,返回新的数据给调用函数。
2.2.2、例2--Funcenstein2
现在让我们对Funcenstein1稍稍做一些改动,看看会出现什么情况(见清单二)。
DWORD Funcenstein2(void) {
DWORD dwTemp;
// 1. Do any processing here.
.
.
.
__try {
// 2. re