從本篇文章開(kāi)始,將全面闡述__try,__except,__finally,__leave異常模型機(jī)制,它也即是Windows系列操作系統(tǒng)平臺(tái)上提供的SEH模型。主人公阿愚將在這里與大家分享SEH的學(xué)習(xí)過(guò)程和經(jīng)驗(yàn)總結(jié)。
SEH有兩項(xiàng)非常強(qiáng)大的功能。當(dāng)然,首先是異常處理模型了,因此,這篇文章首先深入闡述SEH提供的異常處理模型。另外,SEH還有一個(gè)特別強(qiáng)大的功能,這將在下一篇文章中進(jìn)行詳細(xì)介紹。
try-except入門(mén) SEH的異常處理模型主要由try-except語(yǔ)句來(lái)完成,它與標(biāo)準(zhǔn)C++所定義的異常處理模型非常類(lèi)似,也都是可以定義出受監(jiān)控的代碼模塊,以及定義異常處理模塊等。還是老辦法,看一個(gè)例子先,代碼如下:
//seh-test.c
#include <stdio.h>
void main()
{
puts("hello");
// 定義受監(jiān)控的代碼模塊
__try
{
puts("in try");
}
//定義異常處理模塊
__except(1)
{
puts("in except");
}
puts("world");
}
呵呵!是不是很簡(jiǎn)單,而且與C++異常處理模型很相似。當(dāng)然,為了與C++異常處理模型相區(qū)別,VC編譯器對(duì)關(guān)鍵字做了少許變動(dòng)。首先是在每個(gè)關(guān)鍵字加上兩個(gè)下劃線(xiàn)作為前綴,這樣既保持了語(yǔ)義上的一致性,另外也盡最大可能來(lái)避免了關(guān)鍵字的有可能造成名字沖突而引起的麻煩等;其次,C++異常處理模型是使用catch關(guān)鍵字來(lái)定義異常處理模塊,而SEH是采用__except關(guān)鍵字來(lái)定義。并且,catch關(guān)鍵字后面往往好像接受一個(gè)函數(shù)參數(shù)一樣,可以是各種類(lèi)型的異常數(shù)據(jù)對(duì)象;但是__except關(guān)鍵字則不同,它后面跟的卻是一個(gè)表達(dá)式(可以是各種類(lèi)型的表達(dá)式,后面會(huì)進(jìn)一步分析)。
try-except進(jìn)階
與C++異常處理模型很相似,在一個(gè)函數(shù)中,可以有多個(gè)try-except語(yǔ)句。它們可以是一個(gè)平面的線(xiàn)性結(jié)構(gòu),也可以是分層的嵌套結(jié)構(gòu)。例程代碼如下:
// 例程1
// 平面的線(xiàn)性結(jié)構(gòu)
#include <stdio.h>
void main()
{
puts("hello");
__try
{
puts("in try");
}
__except(1)
{
puts("in except");
}
// 又一個(gè)try-except語(yǔ)句
__try
{
puts("in try");
}
__except(1)
{
puts("in except");
}
puts("world");
}
// 例程2
// 分層的嵌套結(jié)構(gòu)
#include <stdio.h>
void main()
{
puts("hello");
__try
{
puts("in try");
// 又一個(gè)try-except語(yǔ)句
__try
{
puts("in try");
}
__except(1)
{
puts("in except");
}
}
__except(1)
{
puts("in except");
}
puts("world");
}
// 例程3
// 分層的嵌套在__except模塊中
#include <stdio.h>
void main()
{
puts("hello");
__try
{
puts("in try");
}
__except(1)
{
// 又一個(gè)try-except語(yǔ)句
__try
{
puts("in try");
}
__except(1)
{
puts("in except");
}
puts("in except");
}
puts("world");
}
try-except異常處理規(guī)則
try-except異常處理規(guī)則與C++異常處理模型有相似之處,例如,它們都是向上逐級(jí)搜索恰當(dāng)?shù)漠惓L幚砟K,包括跨函數(shù)的多層嵌套try- except語(yǔ)句。但是,它們的處理規(guī)則也有另外一些很大的不同之處,例如查找匹配恰當(dāng)?shù)漠惓L幚砟K的過(guò)程,在C++異常處理模型中,它是通過(guò)異常對(duì)象的類(lèi)型來(lái)匹配;但是在try-except語(yǔ)句的異常處理規(guī)則中,則是通過(guò)__except關(guān)鍵字后面括號(hào)中的表達(dá)式的值來(lái)匹配查找正確的異常處理模塊。還是看看MSDN中怎么說(shuō)的吧!摘略如下:
The compound statement after the __try clause is the body or guarded section. The compound statement after the __except clause is the exception handler. The handler specifies a set of actions to be taken if an exception is raised during execution of the body of the guarded section. Execution proceeds as follows:
1. The guarded section is executed.
2. If no exception occurs during execution of the guarded section, execution continues at the statement after the __except clause.
3. If an exception occurs during execution of the guarded section or in any routine the guarded section calls, the __except expression is evaluated and the value determines how the exception is handled. There are three values:
EXCEPTION_CONTINUE_EXECUTION (–1) Exception is dismissed. Continue execution at the point where the exception occurred.
EXCEPTION_CONTINUE_SEARCH (0) Exception is not recognized. Continue to search up the stack for a handler, first for containing try-except statements, then for handlers with the next highest precedence.
EXCEPTION_EXECUTE_HANDLER (1) Exception is recognized. Transfer control to the exception handler by executing the __except compound statement, then continue execution at the assembly instruction that was executing when the exception was raised.
Because the __except expression is evaluated as a C expression, it is limited to a single value, the conditional-expression operator, or the comma operator. If more extensive processing is required, the expression can call a routine that returns one of the three values listed above.
對(duì)查找匹配恰當(dāng)?shù)漠惓L幚砟K的過(guò)程等幾條規(guī)則翻譯如下:
1. 受監(jiān)控的代碼模塊被執(zhí)行(也即__try定義的模塊代碼);
2. 如果上面的代碼執(zhí)行過(guò)程中,沒(méi)有出現(xiàn)異常的話(huà),那么控制流將轉(zhuǎn)入到__except子句之后的代碼模塊中;
3. 否則,如果出現(xiàn)異常的話(huà),那么控制流將進(jìn)入到__except后面的表達(dá)式中,也即首先計(jì)算這個(gè)表達(dá)式的值,之后再根據(jù)這個(gè)值,來(lái)決定做出相應(yīng)的處理。這個(gè)值有三種情況,如下:
EXCEPTION_CONTINUE_EXECUTION (–1) 異常被忽略,控制流將在異常出現(xiàn)的點(diǎn)之后,繼續(xù)恢復(fù)運(yùn)行。
EXCEPTION_CONTINUE_SEARCH (0) 異常不被識(shí)別,也即當(dāng)前的這個(gè)__except模塊不是這個(gè)異常錯(cuò)誤所對(duì)應(yīng)的正確的異常處理模塊。系統(tǒng)將繼續(xù)到上一層的try-except域中繼續(xù)查找一個(gè)恰當(dāng)?shù)腳_except模塊。
EXCEPTION_EXECUTE_HANDLER (1) 異常已經(jīng)被識(shí)別,也即當(dāng)前的這個(gè)異常錯(cuò)誤,系統(tǒng)已經(jīng)找到了并能夠確認(rèn),這個(gè)__except模塊就是正確的異常處理模塊。控制流將進(jìn)入到__except模塊中。
上面的規(guī)則其實(shí)挺簡(jiǎn)單的,很好理解。當(dāng)然,這個(gè)規(guī)則也非常的嚴(yán)謹(jǐn),它能很好的滿(mǎn)足開(kāi)發(fā)人員的各種需求,滿(mǎn)足程序員對(duì)異常處理的分類(lèi)處理的要求,它能夠給程序員提供一個(gè)靈活的控制手段。
其中比較特殊的就是__except關(guān)鍵字后面跟的表達(dá)式,它可以是各種類(lèi)型的表達(dá)式,例如,它可以是一個(gè)函數(shù)調(diào)用,或是一個(gè)條件表達(dá)式,或是一個(gè)逗號(hào)表達(dá)式,或干脆就是一個(gè)整型常量等等。例如代碼如下:
// seh-test.c
// 異常處理模塊的查找過(guò)程演示
#include <stdio.h>
int seh_filer()
{
return 0;
}
void test()
{
__try
{
int* p;
puts("test()函數(shù)的try塊中");
// 下面將導(dǎo)致一個(gè)異常
p = 0;
*p = 45;
}
// 注意,__except關(guān)鍵字后面的表達(dá)式是一個(gè)函數(shù)表達(dá)式
// 而且這個(gè)函數(shù)將返回0,所以控制流進(jìn)入到上一層
// 的try-except語(yǔ)句中繼續(xù)查找
__except(seh_filer())
{
puts("test()函數(shù)的except塊中");
}
}
void main()
{
puts("hello");
__try
{
puts("main()函數(shù)的try塊中");
// 注意,這個(gè)函數(shù)的調(diào)用過(guò)程中,有可能出現(xiàn)一些異常
test();
}
// 注意,這個(gè)表達(dá)式是一個(gè)逗號(hào)表達(dá)式
// 它前部分打印出一條message,后部分是
// 一個(gè)常量,所以這個(gè)值也即為整個(gè)表達(dá)式
// 的值,因此系統(tǒng)找到了__except定義的異
// 常處理模塊,控制流進(jìn)入到__except模塊里面
__except(puts("in filter"), 1)
{
puts("main()函數(shù)的except塊中");
}
puts("world");
}
上面的程序運(yùn)行結(jié)果如下:
hello
main()函數(shù)的try塊中
test()函數(shù)的try塊中
in filter
main()函數(shù)的except塊中
world
Press any key to continue
這種運(yùn)行結(jié)果應(yīng)該是在意料之中吧!為了對(duì)它的流程進(jìn)行更清楚的分析,下圖描述出了程序的運(yùn)行控制流轉(zhuǎn)移過(guò)程,如下。
http://byfiles.storage.msn.com/x1pN1mp8dKYgTFQGzKRebME6105or51BGbjDrskKQ5x3cw-RNCRExH00cuzP8U6qAybuiCE9msw4yGhML-hVWfFOK8DnWPKA9WtlamUEAPnYIJs4c-bXtKEVQ
另外,對(duì)于__except關(guān)鍵字后面表達(dá)式的值,上面的規(guī)則中已經(jīng)做了詳細(xì)規(guī)定。它們有三種值,其中如果為0,那么系統(tǒng)繼續(xù)查找;如果為1,表示系統(tǒng)已經(jīng)找到正確的異常處理模塊。其實(shí)這兩個(gè)值都很好理解,可是如果值為-1的話(huà),那么處理將比較特殊,上面也提到了,此種情況下,“異常被忽略,控制流將在異常出現(xiàn)的點(diǎn)之后,繼續(xù)恢復(fù)運(yùn)行。”實(shí)際上,這就等同于說(shuō),程序的執(zhí)行過(guò)程將不受干擾,好像異常從來(lái)沒(méi)有發(fā)生一樣??匆粋€(gè)例程吧!代碼如下:
#include <stdio.h>
void main()
{
int j, zero;
puts("hello");
__try
{
puts("main()函數(shù)的try塊中");
zero = 0;
j = 10;
// 下面將導(dǎo)致一個(gè)異常
j = 45 / zero;
// 注意,異常出現(xiàn)后,程序控制流又恢復(fù)到了這里
printf("這里會(huì)執(zhí)行到嗎?值有如何呢?j=%d \n", j);
}
// 注意,這里把zero變量賦值為1,試圖恢復(fù)錯(cuò)誤,
// 當(dāng)控制流恢復(fù)到原來(lái)異常點(diǎn)時(shí),避免了異常的再次發(fā)生
__except(puts("in filter"), zero = 1, -1)
{
puts("main()函數(shù)的except塊中");
}
puts("world");
}
上面的程序運(yùn)行結(jié)果如下:
hello
main()函數(shù)的try塊中
in filter
這里會(huì)執(zhí)行到嗎?值有如何呢?j=45
world
Press any key to continue
呵呵!厲害吧!要知道C++異常處理模型可沒(méi)有這樣的能力。但是請(qǐng)注意,一般這項(xiàng)功能不能輕易采用,為什么呢?因?yàn)樗鼤?huì)導(dǎo)致不穩(wěn)定,再看下面一個(gè)示例,代碼如下:
#include <stdio.h>
void main()
{
int* p, a;
puts("hello");
__try
{
puts("main()函數(shù)的try塊中");
// 下面將導(dǎo)致一個(gè)異常
p = 0;
*p = 45;
printf("這里會(huì)執(zhí)行到嗎?值有如何呢?p=%d \n", *p);
}
// 注意,這里把p指針賦了一個(gè)合法的值,也即說(shuō),
// 當(dāng)控制流恢復(fù)到原來(lái)異常點(diǎn)時(shí),異常將不會(huì)再次發(fā)生
__except(puts("in filter"), p = &a, -1)
{
puts("main()函數(shù)的except塊中");
}
puts("world");
}
呵呵!大家猜猜上面的程序的運(yùn)行結(jié)果如何呢?是不是和剛才的那個(gè)例子一樣,異常也得以被恢復(fù)了。朋友們!還是親自運(yùn)行測(cè)試一把。哈哈!程序運(yùn)行結(jié)果是死了,進(jìn)行一個(gè)無(wú)限循環(huán)當(dāng)中,并且控制終端內(nèi)不斷輸出“in filter”信息。為什么會(huì)出現(xiàn)這種情況,難道MSDN中有關(guān)的闡述的有問(wèn)題嗎?或這個(gè)異常處理模型實(shí)現(xiàn)上存在BUG?NO!不是這樣的,實(shí)際上這就是由于表達(dá)式返回-1值時(shí),給程序所帶來(lái)的不穩(wěn)定性。當(dāng)然,MSDN中有關(guān)的闡述也沒(méi)有錯(cuò),那么究竟具體原因是為何呢?這是因?yàn)?,表達(dá)式返回-1值時(shí),系統(tǒng)將把控制流恢復(fù)到異常出現(xiàn)點(diǎn)之后繼續(xù)運(yùn)行。這意味著什么呢?也許大家都明白了,它這里的異?;謴?fù)點(diǎn)是基于一條機(jī)器指令級(jí)別上的。這樣就有很大的風(fēng)險(xiǎn),因?yàn)樯厦娴睦讨校^的異常恢復(fù)處理,也即p = &a語(yǔ)句,它實(shí)際上的確改變了p指針值,但是這個(gè)指針值是棧上的某個(gè)內(nèi)存區(qū)域,而真正出現(xiàn)異常時(shí),代表p指針值的很有可能是某個(gè)寄存器。呵呵!是不是挺費(fèi)解的,沒(méi)關(guān)系!還是看看調(diào)試界圖吧!如下:
http://byfiles.storage.msn.com/x1pN1mp8dKYgTFQGzKRebME62KovOI2zIOz3rCTVXO99-ku0JJm6Gg--r6FEmfo7NKpWBwPGnL6pvfxPZ2zpd1rP8MhDhEmzvQiib6XZ1TEJqu_HREcahE28g
try-except深入
上面的內(nèi)容中已經(jīng)對(duì)try-except進(jìn)行了全面的了解,但是有一點(diǎn)還沒(méi)有闡述到。那就是如何在__except模塊中獲得異常錯(cuò)誤的相關(guān)信息,這非常關(guān)鍵,它實(shí)際上是進(jìn)行異常錯(cuò)誤處理的前提,也是對(duì)異常進(jìn)行分層分級(jí)別處理的前提??上攵绻麤](méi)有這些起碼的信息,異常處理如何進(jìn)行?因此獲取異常信息非常的關(guān)鍵。Windows提供了兩個(gè)API函數(shù),如下:
LPEXCEPTION_POINTERS GetExceptionInformation(VOID);
DWORD GetExceptionCode(VOID);
其中GetExceptionCode()返回錯(cuò)誤代碼,而GetExceptionInformation()返回更全面的信息,看它函數(shù)的聲明,返回了一個(gè)LPEXCEPTION_POINTERS類(lèi)型的指針變量。那么EXCEPTION_POINTERS結(jié)構(gòu)如何呢?如下,
typedef struct _EXCEPTION_POINTERS { // exp
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;
} EXCEPTION_POINTERS;
呵呵!仔細(xì)瞅瞅,這是不是和上一篇文章中,用戶(hù)程序所注冊(cè)的異常處理的回調(diào)函數(shù)的兩個(gè)參數(shù)類(lèi)型一樣。是的,的確沒(méi)錯(cuò)!其中 EXCEPTION_RECORD類(lèi)型,它記錄了一些與異常相關(guān)的信息;而CONTEXT數(shù)據(jù)結(jié)構(gòu)體中記錄了異常發(fā)生時(shí),線(xiàn)程當(dāng)時(shí)的上下文環(huán)境,主要包括寄存器的值。因此有了這些信息,__except模塊便可以對(duì)異常錯(cuò)誤進(jìn)行很好的分類(lèi)和恢復(fù)處理。不過(guò)特別需要注意的是,這兩個(gè)函數(shù)只能是在 __except后面的括號(hào)中的表達(dá)式作用域內(nèi)有效,否則結(jié)果可能沒(méi)有保證(至于為什么,在后面深入分析異常模型的實(shí)現(xiàn)時(shí)候,再做詳細(xì)闡述)。看一個(gè)例程吧!代碼如下:
#include <windows.h>
#include <stdio.h>
int exception_access_violation_filter(LPEXCEPTION_POINTERS p_exinfo)
{
if(p_exinfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
{
printf("存儲(chǔ)保護(hù)異常\n");
return 1;
}
else return 0;
}
int exception_int_divide_by_zero_filter(LPEXCEPTION_POINTERS p_exinfo)
{
if(p_exinfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
{
printf("被0除異常\n");
return 1;
}
else return 0;
}
void main()
{
puts("hello");
__try
{
__try
{
int* p;
// 下面將導(dǎo)致一個(gè)異常
p = 0;
*p = 45;
}
// 注意,__except模塊捕獲一個(gè)存儲(chǔ)保護(hù)異常
__except(exception_access_violation_filter(GetExceptionInformation()))
{
puts("內(nèi)層的except塊中");
}
}
// 注意,__except模塊捕獲一個(gè)被0除異常
__except(exception_int_divide_by_zero_filter(GetExceptionInformation()))
{
puts("外層的except塊中");
}
puts("world");
}
上面的程序運(yùn)行結(jié)果如下:
hello
存儲(chǔ)保護(hù)異常
內(nèi)層的except塊中
world
Press any key to continue
呵呵!感覺(jué)不錯(cuò),大家可以在上面的程序基礎(chǔ)之上改動(dòng)一下,讓它拋出一個(gè)被0除異常,看程序的運(yùn)行結(jié)果是不是如預(yù)期那樣。
最后還有一點(diǎn)需要闡述,在C++的異常處理模型中,有一個(gè)throw關(guān)鍵字,也即在受監(jiān)控的代碼中拋出一個(gè)異常,那么在SEH異常處理模型中,是不是也應(yīng)該有這樣一個(gè)類(lèi)似的關(guān)鍵字或函數(shù)呢?是的,沒(méi)錯(cuò)!SEH異常處理模型中,對(duì)異常劃分為兩大類(lèi),第一種就是上面一些例程中所見(jiàn)到的,這類(lèi)異常是系統(tǒng)異常,也被稱(chēng)為硬件異常;還有一類(lèi),就是程序中自己拋出異常,被稱(chēng)為軟件異常。怎么拋出呢?還是Windows提供了的API函數(shù),它的聲明如下:
VOID RaiseException(
DWORD dwExceptionCode, // exception code
DWORD dwExceptionFlags, // continuable exception flag
DWORD nNumberOfArguments, // number of arguments in array
CONST DWORD *lpArguments // address of array of arguments
);
很簡(jiǎn)單吧!實(shí)際上,在C++的異常處理模型中的throw關(guān)鍵字,最終也是對(duì)RaiseException()函數(shù)的調(diào)用,也即是說(shuō),throw是 RaiseException的上層封裝的更高級(jí)一類(lèi)的函數(shù),這以后再詳細(xì)分析它的代碼實(shí)現(xiàn)。這里還是看一個(gè)簡(jiǎn)單例子吧!代碼如下:
#include <windows.h>
#include <stdio.h>
int seh_filer(int code)
{
switch(code)
{
case EXCEPTION_ACCESS_VIOLATION :
printf("存儲(chǔ)保護(hù)異常,錯(cuò)誤代碼:%x\n", code);
break;
case EXCEPTION_DATATYPE_MISALIGNMENT :
printf("數(shù)據(jù)類(lèi)型未對(duì)齊異常,錯(cuò)誤代碼:%x\n", code);
break;
case EXCEPTION_BREAKPOINT :
printf("中斷異常,錯(cuò)誤代碼:%x\n", code);
break;
case EXCEPTION_SINGLE_STEP :
printf("單步中斷異常,錯(cuò)誤代碼:%x\n", code);
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED :
printf("數(shù)組越界異常,錯(cuò)誤代碼:%x\n", code);
break;
case EXCEPTION_FLT_DENORMAL_OPERAND :
case EXCEPTION_FLT_DIVIDE_BY_ZERO :
case EXCEPTION_FLT_INEXACT_RESULT :
case EXCEPTION_FLT_INVALID_OPERATION :
case EXCEPTION_FLT_OVERFLOW :
case EXCEPTION_FLT_STACK_CHECK :
case EXCEPTION_FLT_UNDERFLOW :
printf("浮點(diǎn)數(shù)計(jì)算異常,錯(cuò)誤代碼:%x\n", code);
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO :
printf("被0除異常,錯(cuò)誤代碼:%x\n", code);
break;
case EXCEPTION_INT_OVERFLOW :
printf("數(shù)據(jù)溢出異常,錯(cuò)誤代碼:%x\n", code);
break;
case EXCEPTION_IN_PAGE_ERROR :
printf("頁(yè)錯(cuò)誤異常,錯(cuò)誤代碼:%x\n", code);
break;
case EXCEPTION_ILLEGAL_INSTRUCTION :
printf("非法指令異常,錯(cuò)誤代碼:%x\n", code);
break;
case EXCEPTION_STACK_OVERFLOW :
printf("堆棧溢出異常,錯(cuò)誤代碼:%x\n", code);
break;
case EXCEPTION_INVALID_HANDLE :
printf("無(wú)效句病異常,錯(cuò)誤代碼:%x\n", code);
break;
default :
if(code & (1<<29))
printf("用戶(hù)自定義的軟件異常,錯(cuò)誤代碼:%x\n", code);
else
printf("其它異常,錯(cuò)誤代碼:%x\n", code);
break;
}
return 1;
}
void main()
{
puts("hello");
__try
{
puts("try塊中");
// 注意,主動(dòng)拋出一個(gè)軟異常
RaiseException(0xE0000001, 0, 0, 0);
}
__except(seh_filer(GetExceptionCode()))
{
puts("except塊中");
}
puts("world");
}
上面的程序運(yùn)行結(jié)果如下:
hello
try塊中
用戶(hù)自定義的軟件異常,錯(cuò)誤代碼:e0000001
except塊中
world
Press any key to continue
上面的程序很簡(jiǎn)單,這里不做進(jìn)一步的分析。我們需要重點(diǎn)討論的是,在__except模塊中如何識(shí)別不同的異常,以便對(duì)異常進(jìn)行很好的分類(lèi)處理。毫無(wú)疑問(wèn),它當(dāng)然是通過(guò)GetExceptionCode()或GetExceptionInformation ()函數(shù)來(lái)獲取當(dāng)前的異常錯(cuò)誤代碼,實(shí)際也即是DwExceptionCode字段。異常錯(cuò)誤代碼在winError.h文件中定義,它遵循 Windows系統(tǒng)下統(tǒng)一的錯(cuò)誤代碼的規(guī)則。每個(gè)DWORD被劃分幾個(gè)字段,如下表所示:
http://byfiles.storage.msn.com/x1pN1mp8dKYgTFQGzKRebME6105or51BGbjDrskKQ5x3cw-RNCRExH00cuzP8U6qAybuiCE9msw4yGhML-hVWfFOK8DnWPKA9WtlamUEAPnYIJs4c-bXtKEVQ
例如我們可以在winbase.h文件中找到EXCEPTION_ACCESS_VIOLATION的值為0 xC0000005,將這個(gè)異常代碼值拆開(kāi),來(lái)分析看看它的各個(gè)bit位字段的涵義。
C 0 0 0 0 0 0 5 (十六進(jìn)制)
1100 0000 0000 0000 0000 0000 0000 0101 (二進(jìn)制)
第3 0位和第3 1位都是1,表示該異常是一個(gè)嚴(yán)重的錯(cuò)誤,線(xiàn)程可能不能夠繼續(xù)往下運(yùn)行,必須要及時(shí)處理恢復(fù)這個(gè)異常。第2 9位是0,表示系統(tǒng)中已經(jīng)定義了異常代碼。第2 8位是0,留待后用。第1 6 位至2 7位是0,表示是FACILITY_NULL設(shè)備類(lèi)型,它代表存取異??砂l(fā)生在系統(tǒng)中任何地方,不是使用特定設(shè)備才發(fā)生的異常。第0位到第1 5位的值為5,表示異常錯(cuò)誤的代碼。
如果程序員在程序代碼中,計(jì)劃拋出一些自定義類(lèi)型的異常,必須要規(guī)劃設(shè)計(jì)好自己的異常類(lèi)型的劃分,按照上面的規(guī)則來(lái)填充異常代碼的各個(gè)字段值,如上面示例程序中拋出一個(gè)異常代碼為0xE0000001軟件異常。
總結(jié)
?。?) C++異常模型用try-catch語(yǔ)法定義,而SEH異常模型則用try-except語(yǔ)法;
(2) 與C++異常模型相似,try-except也支持多層的try-except嵌套。
(3) 與C++異常模型不同的是,try-except模型中,一個(gè)try塊只能是有一個(gè)except塊;而C++異常模型中,一個(gè)try塊可以有多個(gè)catch塊。
?。?)與C++異常模型相似,try-except模型中,查找搜索異常模塊的規(guī)則也是逐級(jí)向上進(jìn)行的。但是稍有區(qū)別的是,C++異常模型是按照異常對(duì)象的類(lèi)型來(lái)進(jìn)行匹配查找的;而try-except模型則不同,它通過(guò)一個(gè)表達(dá)式的值來(lái)進(jìn)行判斷。如果表達(dá)式的值為1 (EXCEPTION_EXECUTE_HANDLER),表示找到了異常處理模塊;如果值為0 (EXCEPTION_CONTINUE_SEARCH),表示繼續(xù)向上一層的try-except域中繼續(xù)查找其它可能匹配的異常處理模塊;如果值為- 1(EXCEPTION_CONTINUE_EXECUTION),表示忽略這個(gè)異常,注意這個(gè)值一般很少用,因?yàn)樗苋菀讓?dǎo)致程序難以預(yù)測(cè)的結(jié)果,例如,死循環(huán),甚至導(dǎo)致程序的崩潰等。
(5) __except關(guān)鍵字后面跟的表達(dá)式,它可以是各種類(lèi)型的表達(dá)式,例如,它可以是一個(gè)函數(shù)調(diào)用,或是一個(gè)條件表達(dá)式,或是一個(gè)逗號(hào)表達(dá)式,或干脆就是一個(gè)整型常量等等。最常用的是一個(gè)函數(shù)表達(dá)式,并且通過(guò)利用GetExceptionCode()或GetExceptionInformation ()函數(shù)來(lái)獲取當(dāng)前的異常錯(cuò)誤信息,便于程序員有效控制異常錯(cuò)誤的分類(lèi)處理。
(6) SEH異常處理模型中,異常被劃分為兩大類(lèi):系統(tǒng)異常和軟件異常。其中軟件異常通過(guò)RaiseException()函數(shù)拋出。RaiseException()函數(shù)的作用類(lèi)似于C++異常模型中的throw語(yǔ)句。
本篇文章已經(jīng)對(duì)SEH的異常處理進(jìn)行了比較全面而深入的闡述,相信大家現(xiàn)在已經(jīng)對(duì)SEH的異常處理機(jī)制胸有成竹了。但是SEH的精華僅只如此嗎?非也,朋友們!繼續(xù)到下一篇的文章中,主人公阿愚將和大家一起共同探討SEH模型的另一項(xiàng)重要的機(jī)制,那就是“有效保證資源的清除”。這對(duì)于C程序可太重要了,因?yàn)樵贑++程序中,至少還有對(duì)象的析構(gòu)函數(shù)來(lái)保證資源的有效清除,避免資源泄漏,但C語(yǔ)言中則沒(méi)有一個(gè)有效的機(jī)制,來(lái)完成此等艱巨的任務(wù)。呵呵!SEH 雪中送炭,它提供了完美的解決方案,所以千萬(wàn)不要錯(cuò)過(guò),一起去看看吧!Let’s go!