人人做人人澡人人爽欧美,国产主播一区二区,久久久精品五月天,羞羞视频在线观看免费

當(dāng)前位置:蘿卜系統(tǒng)下載站 > 技術(shù)開發(fā)教程 > 詳細(xì)頁(yè)面

Visual C++ 中的結(jié)構(gòu)異常處理

Visual C++ 中的結(jié)構(gòu)異常處理

更新時(shí)間:2022-10-15 文章作者:未知 信息來(lái)源:網(wǎng)絡(luò) 閱讀次數(shù):

jimmy 戰(zhàn)志杰 編譯

本文編譯自Jeffrey Richter先生的“Advanced Windows”部分章節(jié)。
1、引言
在“C++中例外的處理”一文中(見計(jì)算機(jī)世界網(wǎng)2001年12月20日),我們討論了C++中的例外(或異常)處理。本文將進(jìn)一步探討Visual C++中的結(jié)構(gòu)異常處理。
想象一下,如果在編程過(guò)程中你不需要考慮任何錯(cuò)誤,你的程序永遠(yuǎn)不會(huì)出錯(cuò),有足夠的內(nèi)存,你需要的文件永遠(yuǎn)存在,這將是一件多么愉快的事。這時(shí)你的程序不需要太多的if語(yǔ)句轉(zhuǎn)來(lái)轉(zhuǎn)去,非常容易寫,容易讀,也容易理解。如果你認(rèn)為這樣的編程環(huán)境是一種夢(mèng)想,那么你就會(huì)喜歡結(jié)構(gòu)異常處理(structu reed exception handling)。
結(jié)構(gòu)異常處理的本質(zhì)就是讓你專心于如何去完成你的任務(wù)。如果在程序運(yùn)行過(guò)程中出現(xiàn)任何錯(cuò)誤,系統(tǒng)會(huì)接收(catch)并通知(notify)你。雖然利用結(jié)構(gòu)異常處理你不可能完全忽略你的程序出錯(cuò)的可能性,但是結(jié)構(gòu)異常處理確確實(shí)實(shí)允許你將你的主要任務(wù)與錯(cuò)誤處理分離開來(lái)。這種分離使得你可以集中精力于你的工作,而在以后在考慮可能的錯(cuò)誤。
結(jié)構(gòu)異常處理的主要工作是由編譯器來(lái)完成的,而不是由操作系統(tǒng)。編譯器在遇到例外程序段時(shí)需要產(chǎn)生額外的特殊代碼來(lái)支持結(jié)構(gòu)異常處理。所以,每一個(gè)編譯器產(chǎn)品供應(yīng)商可能使用自己的語(yǔ)法和規(guī)定。這里我們采用微軟的Visual C++編譯器來(lái)進(jìn)行討論。
注意不要將這里討論的結(jié)構(gòu)異常處理與C++中的異常處理混為一談。C++中的異常處理是另一種形式的異常處理,它使用了C++的關(guān)鍵詞catch和throw。
微軟最早在Visual C++版本2.0引進(jìn)結(jié)構(gòu)異常處理。結(jié)構(gòu)異常處理主要由兩部分組成:中斷處理(termination handling)和例外處理(exception handling)。
2、中斷處理句柄(termination handler)
2.1、中斷處理句柄定義
中斷處理句柄保證了,不論進(jìn)程如何離開另一程序段--這里稱之為守衛(wèi)體(guarded body),該句柄內(nèi)的程序段永遠(yuǎn)會(huì)被調(diào)用和執(zhí)行。微軟的Visual C++編譯器的中斷處理句柄語(yǔ)法為
__try {
// Guarded body
.
.
.
}
__finally {
// Termination handler
.
.
.
}
這里的__try和__finally勾畫出了中斷處理句柄的兩個(gè)部分。在上面的例子中,操作系統(tǒng)和編譯器一起保證了不論包含在__try內(nèi)的程序段出現(xiàn)何種情況,包含在__finally內(nèi)的程序段永遠(yuǎn)會(huì)被運(yùn)行。不論你在__try內(nèi)的程序段中調(diào)用return、goto或longjump,__finally內(nèi)的中斷處理句柄永遠(yuǎn)會(huì)被調(diào)用。其流程為
// 1、執(zhí)行try程序段前的代碼
__try {
// 2、執(zhí)行try程序段內(nèi)的代碼
}
__finally {
// 3、執(zhí)行finally程序段內(nèi)的代碼
}
// 4、執(zhí)行finally程序段后的代碼
2.2、幾個(gè)例子
下面我們通過(guò)幾個(gè)具體例子來(lái)討論中斷處理句柄是如何工作的。
2.2.1、例1--Funcenstein1
清單一給出了我們的第一個(gè)例子。
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函數(shù)代碼
在函數(shù)Funcenstein1中,我們使用了try-finally程序塊。但是它們并沒(méi)有為我們做多少工作:等待一個(gè)指示燈信號(hào),改變保護(hù)數(shù)據(jù)的內(nèi)容,將新的數(shù)據(jù)指定給一個(gè)局域變量dwTemp,釋放指示燈信號(hào),返回新的數(shù)據(jù)給調(diào)用函數(shù)。
2.2.2、例2--Funcenstein2
現(xiàn)在讓我們對(duì)Funcenstein1稍稍做一些改動(dòng),看看會(huì)出現(xiàn)什么情況(見清單二)。
DWORD Funcenstein2(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;
// Return the new value.
return (dwTemp);

__finally {
// 3. Allow others to use protected data.
ReleaseSemaphore(g_hSem, 1, NULL);

// 4. Continue processing--this code will never execute in this version.
dwTemp = 9;
return (dwTemp);

例2 Funcenstein2函數(shù)代碼
在函數(shù)Funcenstein2中,我們?cè)趖ry程序段里加入了一個(gè)return返回語(yǔ)句。該返回語(yǔ)句告訴編譯器,你想離開函數(shù)Funcenstein2并返回dwTemp內(nèi)的內(nèi)容5給調(diào)用函數(shù)。然而,如果此返回語(yǔ)句被執(zhí)行,本線程永遠(yuǎn)不會(huì)釋放指示燈信號(hào),其它線程也就永遠(yuǎn)不會(huì)得到該指示燈信號(hào)。你可以想象,在多線程程序中這是一個(gè)多么嚴(yán)重的問(wèn)題。
但是,使用了中斷處理句柄避免了這種情況發(fā)生。當(dāng)返回語(yǔ)句試圖離開try程序段時(shí),編譯器保證了在finally程序段內(nèi)的代碼得到執(zhí)行。所以,finally程序段內(nèi)的代碼保證會(huì)在try程序段中的返回語(yǔ)句前執(zhí)行。在函數(shù)Funcenstein2中,將調(diào)用ReleaseSemaphore放在finally程序段內(nèi)保證了指示燈信號(hào)會(huì)得到釋放。
在finally程序段內(nèi)的代碼被執(zhí)行后,函數(shù)Funcenstein2立即返回。這樣,因?yàn)閠ry程序段內(nèi)的return返回語(yǔ)句,任何finally程序段后的代碼都不會(huì)被執(zhí)行。因而Funcenstein2返回值是5,而不是9。
必須指出的是,當(dāng)遇到例2中這種過(guò)早返回語(yǔ)句時(shí),編譯器需要產(chǎn)生額外的代碼以保證finally程序段內(nèi)的代碼的執(zhí)行。此過(guò)程稱作為局域展開。當(dāng)然,這必然會(huì)降低整個(gè)程序的效率。所以,你應(yīng)該盡量避免使用這類代碼。在后面我們會(huì)討論關(guān)鍵詞__leave,它可以幫助我們避免編寫出現(xiàn)局域展開一類的代碼。
2.2.3、例3--Funcenstein3
現(xiàn)在讓我們對(duì)Funcenstein2做進(jìn)一步改動(dòng),看看會(huì)出現(xiàn)什么情況(見例3)。
DWORD Funcenstein3(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;
// Try to jump over the finally block.
goto ReturnValue;

__finally {
// 3. Allow others to use protected data.
ReleaseSemaphore(g_hSem, 1, NULL);

dwTemp = 9;
// 4. Continue processing.
ReturnValue:
return (dwTemp);

例3 Funcenstein3函數(shù)代碼
在函數(shù)Funcenstein3中,當(dāng)遇到goto語(yǔ)句時(shí)編譯器會(huì)產(chǎn)生額外的代碼以保證finally程序段內(nèi)的代碼得到執(zhí)行。但是,這一次finally程序段后ReturnValue標(biāo)簽后面的代碼會(huì)被執(zhí)行,因?yàn)閠ry或finally程序段內(nèi)沒(méi)有返回語(yǔ)句。函數(shù)的返回值是5。同樣,由于goto語(yǔ)句打斷了從try程序段到finally程序段的自然流程,程序的效率會(huì)降低。
2.2.4、例4--Funcfurter1
現(xiàn)在讓我們來(lái)看中斷處理真正展現(xiàn)其功能的一個(gè)例子。(見例4)。
DWORD Funcfurter1(void) {
DWORD dwTemp;
// 1. Do any processing here.
.
.
.
__try {
// 2. request permission to access protected data, and then use it.
WaitForSingleObject(g_hSem, INFINITE);
dwTemp = Funcinator(g_dwProtectedData);

__finally {
// 3. Allow others to use protected data.
ReleaseSemaphore(g_hSem, 1, NULL);

// 4. Continue processing.
return (dwTemp);

例4 Funcfurter1函數(shù)代碼
設(shè)想try程序段內(nèi)調(diào)用的Funcinator函數(shù)具有某種缺陷而造成無(wú)效內(nèi)存讀寫。在16位視窗應(yīng)用程序中,這會(huì)導(dǎo)致一個(gè)已定義好的錯(cuò)誤信息對(duì)話框出現(xiàn)。在用戶關(guān)閉對(duì)話框的同時(shí)該應(yīng)用程序也終止運(yùn)行。在不具有try-finally的Win32應(yīng)用程序中,這會(huì)導(dǎo)致程序終止運(yùn)行,指示燈信號(hào)永遠(yuǎn)不會(huì)得到釋放。這就造成了等待該指示燈信號(hào)的其它線程會(huì)永遠(yuǎn)等待下去。而將ReleaseSemaphore放在finally程序段內(nèi)則從根本上保證了不論何種情況出現(xiàn)指示燈信號(hào)都會(huì)得到釋放。
如果中斷處理句柄能夠處理由于無(wú)效內(nèi)存讀寫而造成的程序中斷,我們就完全有理由相信它能夠處理諸如setjump/longjump、break和continue這類的中斷轉(zhuǎn)移。事實(shí)也正是這樣。
2.3、小測(cè)試
下面一個(gè)例子(見清單五)請(qǐng)讀者猜測(cè)一下函數(shù)FuncaDoodleDoo的返回值。(答案為14)
DWORD FuncaDoodleDoo(void) {
DWORD dwTemp = 0;
while (dwTemp 〈 10) {
__try {
if (dwTemp == 2)
continue;
if (dwTemp == 3)
break;

__finally {
dwTemp++;

dwTemp++;
}
dwTemp += 10;
return (dwTemp);

FuncaDoodleDoo函數(shù)代碼
雖然中斷處理句柄能夠接收出現(xiàn)在try程序段內(nèi)的絕大部分異常情況,但是如果線程或進(jìn)程中斷執(zhí)行的話,則finally程序段內(nèi)的代碼不會(huì)被執(zhí)行。調(diào)用ExitThread或ExitProcess就會(huì)立即造成線程或進(jìn)程的中斷,而不會(huì)執(zhí)行finally程序段。另外,如果其它的應(yīng)用程序調(diào)用ExitThread或ExitProcess而造成你的線程或進(jìn)程中斷,你程序中的finally程序段也不會(huì)被執(zhí)行。一些C函數(shù)如abort會(huì)調(diào)用ExitProcess,也會(huì)導(dǎo)致你的finally程序段不被執(zhí)行。對(duì)此你無(wú)能為力。但你可以防止你自己提早調(diào)用ExitThread或ExitProcess。
2.4、應(yīng)用例子
我們已經(jīng)討論了中斷處理句柄的句法及語(yǔ)法,F(xiàn)在我們進(jìn)一步討論如何利用中斷處理句柄來(lái)簡(jiǎn)化一個(gè)比較復(fù)雜的編程問(wèn)題。
首先讓我們來(lái)看一個(gè)沒(méi)有使用中斷處理句柄的例子,程序源代碼見例6。
BOOL Funcarama1 (void) {
HANDLE hFile = INVALID_HANDLE_VALUE;
LPVOID lpBuf = NULL;
DWORD dwNumBytesRead;
BOOL fOk;
hFile = CreateFile("SOMEDATA.DAT", GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return (FALSE);
}
lpBuf = VitualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
if (lpBuf == NULL) {
CloseHandle(hFile);
return (FALSE);
}
fOk = ReadFile(hFile, lpBuf, 1024, &dwNumBytesRead, NULL);
if (!fOk || (dwNumBytesRead == 0)) {
VirtualFree(lpBuf, MEM_RELEASE | MEM_DECOMMIT);
CloseHandle(hFile);
return (FALSE);
}
// Do some calculation on the data.
.
.
.

// Clean up all the resources.
VirtualFree(lpBuf, MEM_RELEASE | MEM_DECOMMIT);
CloseHandle(hFile);
return (TRUE);
}
例6 沒(méi)有使用中斷處理句柄的Funcarama1函數(shù)代碼
在上例Funcarama1函數(shù)中,所有的錯(cuò)誤診斷使得該函數(shù)難以理解、維護(hù)和修改。當(dāng)然,我們可以對(duì)Funcarama1函數(shù)進(jìn)行一些改動(dòng),使其易于理解(見例7)。
BOOL Funcarama2 (void) {
HANDLE hFile = INVALID_HANDLE_VALUE;
LPVOID lpBuf = NULL;
DWORD dwNumBytesRead;
BOOL fOk, fSuccess = FALSE;
hFile = CreateFile("SOMEDATA.DAT", GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
lpBuf = VitualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
if (lpBuf != NULL) {
fOk = ReadFile(hFile, lpBuf, 1024, &dwNumBytesRead, NULL);
if (fOk || (dwNumBytesRead != 0)) {
// Do some calculation on the data.
.
.
.
fSuccess = TRUE;
}
}
VirtualFree(lpBuf, MEM_RELEASE | MEM_DECOMMIT);
}
CloseHandle(hFile);
return (fSuccess);
}
例7 沒(méi)有使用中斷處理句柄的Funcarama2函數(shù)代碼
雖然函數(shù)Funcarama2容易理解,但是仍然難于維護(hù)和修改。
現(xiàn)在讓我們來(lái)利用中斷處理句柄重寫Funcaram1函數(shù),其代碼如清單八。
BOOL Funcarama3 (void) {
HANDLE hFile = INVALID_HANDLE_VALUE;
LPVOID lpBuf = NULL;
__try {
DWORD dwNumBytesRead;
BOOL fOk;
hFile = CreateFile("SOMEDATA.DAT", GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return (FALSE);
}
lpBuf = VitualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
if (lpBuf == NULL) {
return (FALSE);
}
fOk = ReadFile(hFile, lpBuf, 1024, &dwNumBytesRead, NULL);
if (!fOk || (dwNumBytesRead == 0)) {
VirtualFree(lpBuf, MEM_RELEASE | MEM_DECOMMIT);
return (FALSE);
}
// Do some calculation on the data.
.
.
.

__finally {
// Clean up all the resources.
if (lpBuf != NULL)
VirtualFree(lpBuf, MEM_RELEASE | MEM_DECOMMIT);
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
}
// Continue processing.
return (TRUE);
}
例8 使用了中斷處理句柄的Funcarama3函數(shù)代碼
Funcarama3函數(shù)版的好處是所有的清除工作都集中在一個(gè)地方:finally程序段內(nèi)。這樣在我們需要對(duì)該函數(shù)增加新的條件語(yǔ)句時(shí),我們只需要在finally程序段內(nèi)簡(jiǎn)單增添一行清除語(yǔ)句就可以了,而不必回過(guò)頭來(lái)在每一出可能出錯(cuò)的地方添加清除語(yǔ)句。
Funcarama3函數(shù)的真正問(wèn)題在于其效率。我們以前說(shuō)過(guò)應(yīng)盡可能的避免在try程序段內(nèi)使用return語(yǔ)句。為了避免這種情況,微軟在它的編譯器里引進(jìn)了另一個(gè)關(guān)鍵詞__leave。利用關(guān)鍵詞__leave重寫的Funcarama3函數(shù)見例9。
BOOL Funcarama4 (void) {
HANDLE hFile = INVALID_HANDLE_VALUE;
LPVOID lpBuf = NULL;
// Assume that the function will not execute successfully.
BOOL fFunctionOk = FALSE;
__try {
DWORD dwNumBytesRead;
BOOL fOk;
hFile = CreateFile("SOMEDATA.DAT", GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
__leave;
}
lpBuf = VitualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
if (lpBuf == NULL) {
__leave;
}
fOk = ReadFile(hFile, lpBuf, 1024, &dwNumBytesRead, NULL);
if (!fOk || (dwNumBytesRead == 0)) {
VirtualFree(lpBuf, MEM_RELEASE | MEM_DECOMMIT);
__leave;
}
// Do some calculation on the data.
.
.
.
// Indicate that the entire function executed successfully.
fFunctionOk = TRUE;

__finally {
// Clean up all the resources.
if (lpBuf != NULL)
VirtualFree(lpBuf, MEM_RELEASE | MEM_DECOMMIT);
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
}
// Continue processing.
return (fFunctionOk);
}
例9 使用了中斷處理句柄和關(guān)鍵詞__leave的Funcarama4函數(shù)代碼
try程序段內(nèi)的關(guān)鍵詞__leave導(dǎo)致程序運(yùn)行指針直接跳到try程序段的結(jié)尾(你可以將此看成為跳到try程序段的結(jié)束花括弧)。這樣,因?yàn)榭刂屏鞒虒ⅰ白匀弧钡碾x開try程序段,進(jìn)入finally程序段,所以不需付出額外代價(jià)而導(dǎo)致效率降低。但是你需要引進(jìn)一個(gè)新的變量來(lái)指示整個(gè)函數(shù)的運(yùn)行是否成功。
從try程序段到finally程序段,控制流程既可以是自然進(jìn)入,也可以是由于異常的出現(xiàn)而導(dǎo)致控制流程過(guò)早離開try程序段而進(jìn)入finally程序段。為確定何種情況下造成finally程序段的運(yùn)行,我們可以調(diào)用AbnormalTermination函數(shù)來(lái)診斷。
BOOL AbnormalTermination(VOID);
該函數(shù)只能在finally程序段內(nèi)調(diào)用以診斷與此finally相對(duì)應(yīng)的try程序段是否是過(guò)早離開。如果AbnormalTermination的返回值是FALSE,表明程序流程是自然離開try程序段。否則,則是過(guò)早離開。
3、異常處理句柄(Exception handler)
3.1、異常(或例外)處理句柄的定義
異常(或例外)是你不希望出現(xiàn)的事件。在一個(gè)完好的應(yīng)用程序中,你不希望讀寫無(wú)效內(nèi)存地址或除數(shù)為零的情況出現(xiàn)。但是這類錯(cuò)誤的確會(huì)發(fā)生。在出現(xiàn)這類錯(cuò)誤時(shí),CPU會(huì)負(fù)責(zé)提出針對(duì)該類錯(cuò)誤的例外。當(dāng)CPU提出一個(gè)例外時(shí),我們稱之為硬件異常(或例外)(hardware exception)。操作系統(tǒng)和應(yīng)用程序自身也可以提出自己的異常。這類異常我們稱之為軟件異常(或例外)(software exception)。
當(dāng)一個(gè)硬件異;蜍浖惓1惶岢鰰r(shí),操作系統(tǒng)向你的程序提供一種機(jī)會(huì)使得你的程序可以診斷那類異常被提出并允許你的程序?qū)Υ诉M(jìn)行處理。異常處理句柄的語(yǔ)法為
__try {
// Guarded body
.
.
.
}
__except (exception filter) {
// Exception handler
.
.
.
}
請(qǐng)注意關(guān)鍵詞__except。當(dāng)你建立一個(gè)try程序段時(shí),它必須跟隨一個(gè)finally程序段或一個(gè)except程序段。一個(gè)try程序段不能同時(shí)既跟隨一個(gè)finally程序段又跟隨一個(gè)except程序段。一個(gè)try程序段也不能同時(shí)跟隨多個(gè)finally程序段或多個(gè)except程序段。但是,try-finally程序段卻可以嵌套在try-except程序段內(nèi),或try-except程序段嵌套在try-finally程序段內(nèi)。
3.2、幾個(gè)例子
不同于中斷處理句柄,異常處理句柄直接由操作系統(tǒng)執(zhí)行,編譯器不需要做太多工作。下面我們通過(guò)幾個(gè)具體例子來(lái)討論異常處理句柄是如何工作的。
3.2.1、例5--Funcmeister1函數(shù)
下面是一個(gè)使用了try-except異常處理句柄的函數(shù)Funcmeister1,其代碼見清單十。
DWORD Funcmeister1 (void) {
DWORD dwTemp;
// 1. Do any processing here.
.
.
.
__try {
// 2. Perform some operation.
dwTemp = 0;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
// 3. Handle an exception; this never executes.
.
.
.
}
// 3. Continue processing.
return (dwTemp);
}
例10 例5Funcmeister1函數(shù)代碼
在Funcmeister1函數(shù)中的try程序段內(nèi),我們簡(jiǎn)單地將dwTemp賦值為零。該操作不會(huì)導(dǎo)致任何異常的提出。所以,except程序段內(nèi)的程序永遠(yuǎn)不會(huì)被執(zhí)行。請(qǐng)注意,這有別于中斷處理句柄try-finally。在執(zhí)行了dwTemp賦值語(yǔ)句后的下一個(gè)執(zhí)行語(yǔ)句是return返回語(yǔ)句。
雖然我們不鼓勵(lì)在try程序段內(nèi)使用return, goto, continue和break語(yǔ)句,但是在異常處理句柄的try程序段內(nèi)使用這些語(yǔ)句不會(huì)象中斷處理句柄那樣造成運(yùn)行代碼的增加和效率下降。
3.2.2、例6--Funcmeister2函數(shù)
讓我們對(duì)Funcmeister1函數(shù)進(jìn)行一些改動(dòng),看看會(huì)出現(xiàn)什么情況。改動(dòng)后的函數(shù)見例11。
DWORD Funcmeister2 (void) {
DWORD dwTemp = 0;
// 1. Do any processing here.
.
.
.
__try {
// 2. Perform some operation(s).
dwTemp = 5 / dwTemp; // Generate an exception
dwTemp += 10; // Never excutes
}
__except ( /* 3. Evaluate filter. */ EXCEPTION_EXECUTE_HANDLER) {
// 4. Handle an exception; this never executes.
MessageBeep(0);
.
.
.
}

// 5. Continue processing.
return (dwTemp);
}
例11 例6Funcmeister2函數(shù)代碼
函數(shù)Funcmeister2中的try程序段dwTemp = 5 / dwTemp語(yǔ)句導(dǎo)致CPU提出一個(gè)硬件異常。當(dāng)該異常被提出時(shí),操作系統(tǒng)會(huì)尋找相對(duì)應(yīng)的except程序段的起始位置并評(píng)估其異常篩選表達(dá)式(exception filter expression)。異常篩選表達(dá)式可以取下列標(biāo)識(shí)符值之一。這些標(biāo)識(shí)符定義在Win32 EXCPT.H頭文件中。
標(biāo)識(shí)符 定義為
EXCEPTION_EXECUTE_HANDLER 1
EXCEPTION_CONTINUE_SEARCH 0
EXCEPTION_CONTINUE_EXECUTION -1
3.3、異常篩選(exception filter)
EXCEPTION_EXECUTE_HANDLER表明當(dāng)一個(gè)異常出現(xiàn)時(shí),運(yùn)行程序跳到except程序段轉(zhuǎn)而執(zhí)行except程序段內(nèi)的代碼。except程序段內(nèi)的代碼執(zhí)行完后,系統(tǒng)認(rèn)為該異常已處理完,接著繼續(xù)執(zhí)行except程序段后的代碼。
EXCEPTION_CONTINUE_EXECUTION表明當(dāng)一個(gè)異常出現(xiàn)時(shí),運(yùn)行程序不立即執(zhí)行except程序段內(nèi)的代碼而返回try程序段內(nèi)產(chǎn)生異常的語(yǔ)句繼續(xù)執(zhí)行該語(yǔ)句。
EXCEPTION_CONTINUE_SEARCH表明當(dāng)一個(gè)異常出現(xiàn)時(shí),運(yùn)行程序不執(zhí)行該except程序段內(nèi)的代碼而尋求由高一級(jí)的異常處理句柄來(lái)處理此異常。
Win32 WINBASE.H頭文件中定義了可能出現(xiàn)的各種異常代碼。我們可以通過(guò)調(diào)用GetExceptionCode函數(shù)來(lái)診斷何種異常被提出,從而決定異常處理句柄該采取何種行動(dòng)。GetExceptionCode函數(shù)定義為
DWORD GetExceptionCode(VOID);
它的返回值表明何種異常出現(xiàn)。下面的程序說(shuō)明如何調(diào)用GetExceptionCode函數(shù)。
__try {
x = 0;
y = 4 / x;
}
__except ((GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO) ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
// Handle divide by zero exception.
}
當(dāng)一個(gè)異常發(fā)生時(shí),操作系統(tǒng)會(huì)將有關(guān)該異常的信息儲(chǔ)存在三個(gè)結(jié)構(gòu)中,并將它們存放在提出此異常線程的堆棧里。這三個(gè)結(jié)構(gòu)是EXCEPTION_RECORD,CONTEXT,和EXCEPTION_POINTERS。EXCEPTION_RECORD儲(chǔ)存著與CPU無(wú)關(guān)的異常信息,CONTEXT則儲(chǔ)存著與CPU有關(guān)的異常信息。EXCEPTION_POINTERS結(jié)構(gòu)包含了兩個(gè)分別指向EXCEPTION_RECORD和CONTEXT的指針。
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;
} EXCEPTION_POINTERS
假如你的程序需要這些異常信息,你可以通過(guò)調(diào)用GetExceptionInformation函數(shù)來(lái)獲取。
LPEXCEPTION GetExceptionInformation(void);
GetExceptionInformation函數(shù)返回一個(gè)指向EXCEPTION_POINTERS結(jié)構(gòu)的指針。下面的函數(shù)說(shuō)明了如何調(diào)用GetExceptionInformation函數(shù)。
void FuncSkunk (void) {
// Declare variables that we can use to save the exception
// record and the context if an exception should occur.
EXCEPTION_RECORD SavedExceptRec;
CONTEXT SavedContext;
.
.
.
__try {
.
.
.
}
__except (
SavedExceptRec =
*(GetExceptionInformation())->ExceptionRecord,
SavedContext =
*(GetExceptionInformation())->ContextRecord,
EXCEPTION_EXECUTE_HANDLER) {
// We can use the SavedExceptRec and SavedContext
// variables inside the handler code block.
switch (SavedExceptRec.ExceptionCode) {
.
.
.
}
}
.
.
.
}
注意,在上面的異常篩選表達(dá)式程序中我們使用了C語(yǔ)言的“,”操作符。許多程序員對(duì)此并不是很熟悉。該操作符告訴編譯器從左到右運(yùn)行由“,”分離的各表達(dá)式。在所有的表達(dá)式都運(yùn)行完后,返回最后一個(gè)(或最右面的)表達(dá)式的值。
4、軟件異常(software exception)
至此為止我們所討論的是如何處理由CPU提出的硬件異常(hardware exception)。通常,操作系統(tǒng)或應(yīng)用程序自身提出的軟件異常也非常有用。例如,HeapAlloc函數(shù)就提供了一個(gè)非常好的利用軟件異常的例子。在調(diào)用HeapAlloc時(shí),你可以設(shè)置HEAP_GENERATE-EXCEPTIONS指示旗(flag)。這樣如果HeapAlloc不能滿足你的內(nèi)存分配要求,它會(huì)產(chǎn)生一個(gè)STATUS_NO_MEMORY軟件異常。
假如你想利用這個(gè)異常,你可以在你的try程序段內(nèi)繼續(xù)編寫你的代碼,如同內(nèi)存分配總是會(huì)成功一樣。如果內(nèi)存分配失敗,你可以利用except程序段來(lái)處理這個(gè)異;蚶胒inally程序段來(lái)做清除工作。
你的程序不需要知道你要處理的異常是軟件異常還是硬件異常。你利用try-finally和try-except來(lái)處理軟件異常和硬件異常的方式是一樣的。但是你可以讓你的程序象HeapAlloc函數(shù)一樣提出自己的異常。為了在你的程序中提出軟件異常,你需要調(diào)用RaiseException函數(shù)。
VOID RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags,
DWORD cArguments, LPDWORD lpArguments);
關(guān)于該函數(shù)的使用,請(qǐng)參考微軟的有關(guān)文獻(xiàn)。
5、結(jié)論
結(jié)構(gòu)異常處理由中斷處理和例外處理兩部分組成。采用結(jié)構(gòu)異常處理使得你可以將精力集中在你的程序應(yīng)用代碼設(shè)計(jì)上,從而使得應(yīng)用方案的設(shè)計(jì)更方便、具體。采用結(jié)構(gòu)異常處理編寫的程序更易于理解、修改和維護(hù),從而增加了程序的可讀性和維護(hù)性。

溫馨提示:喜歡本站的話,請(qǐng)收藏一下本站!

本類教程下載

系統(tǒng)下載排行

網(wǎng)站地圖xml | 網(wǎng)站地圖html
主站蜘蛛池模板: 五指山市| 特克斯县| 信宜市| 南陵县| 临城县| 都昌县| 松江区| 增城市| 大姚县| 玉环县| 巴塘县| 麻城市| 禄劝| 汽车| 张掖市| 赤壁市| 天台县| 卢湾区| 岑巩县| 双牌县| 乡宁县| 阿勒泰市| 彭州市| 竹山县| 莱阳市| 高阳县| 油尖旺区| 罗江县| 泰宁县| 普兰县| 崇义县| 霍州市| 湟中县| 柳林县| 丹东市| 南昌县| 阳新县| 建湖县| 凤山市| 杭锦旗| 揭西县|