Windows核心编程 第2 4章 异常处理程序和软件异常
異常處理程序和軟件異常
????C?P?U引發的異常,就是所謂的硬件異常(hardware?exception)。操作系統和應用程序
也可以引發相應的異常,稱為軟件異常(software?exception)。
當出現一個硬件或軟件異常時,操作系統向應用程序提供機會來考察是什么類型的異常被引發,并能夠讓應用程序自己來處理異常。下面就是異常處理程序的文法:
注意-?-?e?x?c?e?p?t關鍵字。每當你建立一個t?r?y塊,它必須跟隨一個f?i?n?a?l?l?y塊或一個e?x?c?e?p?t塊。一個try?塊之后不能既有f?i?n?a?l?l?y塊又有e?x?c?e?p?t塊。但可以在t?r?y?-?e?x?c?e?p?t塊中嵌套t?r?y?-?f?i?n?a?l?l?y塊,反過來也可以。
???與結束處理程序(__try{}__finally{})不同,異常過濾器(?exception?filter)和異常處理程序是通過操作系統直接執行的,編譯程序在計算異常過濾器表達式和執行異常處理程序方面不做什么事。
盡管在結束處理程序的t?r?y塊中使用r?e?t?u?r?n、g?o?t?o、c?o?n?t?i?n?u?e和b?r?e?a?k語句遭到強烈地反對,但在異常處理程序的t?r?y塊中使用這些語句不會產生速度和代碼規模方面的不良影響。這樣的語句出現在與e?x?c?e?p?t塊相結合的t?r?y塊中不會引起局部展開的系統開銷。
例子1(可以直接捕獲到異常,不會內存您訪問故障的窗體,__try{}__finally{}會彈窗)
解釋__except()里面的三種參數:
EXCEPTION_EXECUTE_HANDLER
這個值的意思是要告訴系統:“我認出了這個異常。即,我感覺這個異常可能在某個時候發生,我已編寫了代碼來處理這個問題,現在我想執行這個代碼。”
FuncOren1中/0導致硬件中斷,然后開始回溯到FuncOstimpy1發現有except(同時參數是EXCEPTION_EXECUTE_HANDLER),然后就開始繼續執行finally對finally開始全局展開,執行完成finally代碼之后回溯到上一層?FuncOstimpy1里去執行__except代碼。結果是先輸出finally然后輸出except。
暫停全局展開:書上說是在類似上面的例子中,如果finally里面有return語句就會終止全局展開,然而沒編譯過去。
?
EXCEPTION_CONTINUE_EXECUTION
?
????這里,首先遇到的問題是在我們試圖向?p?c?h?B?u?ff?e?r所指向的緩沖區中放入一個字母‘?J’時發生的。因為這里沒有初始化p?c?h?B?u?ff?e?r,使它指向全局緩沖區g?_?s?z?B?u?ff?u?r。p?c?h?B?u?ff?e?r實際指向N?U?L?L。C?P?U將產生一個異常,并計算與異常發生的?t?r?y塊相關聯的e?x?c?e?p?t塊的異常過濾器。在e?x?c?e?p?t塊中,對O?i?l?F?i?l?t?e?r函數傳遞了p?c?h?B?u?ff?e?r變量的地址。
????當O?i?l?F?i?l?t?e?r獲得控制時,它要查看*?p?p?c?h?B?u?ff?e?r是不是N?U?L?L,如果是,把它設置成指向全局緩沖區g?_?s?z?B?u?ff?e?r。然后這個過濾器返回E?X?C?E?P?T?I?O?N?_?C?O?N?T?I?N?U?E?_?E?X?E?C?U?T?I?O?N。當系統看到過濾器的值是E?X?C?E?P?T?I?O?N?_?C?O?N?T?I?N?U?E?_?E?X?E?C?U?T?I?O?N時,系統跳回到產生異常的指令,試圖再執行一次。這一次,指令將執行成功,字母‘J’將放在g?_?s?z?B?u?ff?e?r的第一個字節。
????隨著代碼繼續執行,我們又在?t?r?y塊中碰到除以0的問題。系統又要計算過濾器的值。這一次,O?i?l?F?i?l?t?e?r看到*?p?p?c?h?B?u?ff?e?r不是N?U?L?L,就返回E?X?C?E?P?T?I?O?N?_?E?X?E?C?U?T?E?_?H?A?N?D?L?E?R,這是告訴系統去執行e?x?c?e?p?t塊中的代碼。這會顯示一個消息框,用文本串報告發生了異常。
實際執行結果是輸出了一次Function?completed?但第一個MessageBoxA(NULL?,pchBuffer?,"tit"?,MB_OK);??并沒有執行
EXCEPTION_CONTINUE_SEARCH
取值?E?X?C?E?P?T?I?O?N?_CONTINUE_?SEARCH。這個標識符是告訴系統去查找前面與一個?e?x?c?e?p?t塊相匹配的t?r?y塊,并調用這個t?r?y塊的異常處理器。
GetExceptionCode
????一個異常過濾器在確定要返回什么值之前,必須分析具體情況。例如,異常處理程序可能知道發生了除以0引起的異常時該怎么做,但是不知道該如何處理一個內存存取異常。異常過濾器負責檢查實際情況并返回適當的值。
?
軟件異常
迄今為止,我們一直在討論硬件異常,也就是?C?P?U捕獲一個事件并引發一個異常。在代碼中也可以強制引發一個異常。這也是一個函數向它的調用者報告失敗的一種方法。傳統上,失敗的函數要返回一些特殊的值來指出失敗。函數的調用者應該檢查這些特殊值并采取一種替代的動作。通常,這個調用者要清除所做的事情并將它自己的失敗代碼返回給它的調用者。這種錯誤代碼的逐層傳遞會使源程序的代碼變得非常難于編寫和維護。
另外一種方法是讓函數在失敗時引發異常。用這種方法,代碼更容易編寫和維護,而且也執行得更好,因為通常不需要執行那些錯誤測試代碼。實際上,僅當發生失敗時也就是發生異常時才執行錯誤測試代碼。
但令人遺憾的是,許多開發人員不習慣于在錯誤處理中使用異常。這有兩方面的原因。第一個原因是多數開發人員不熟悉S?E?H。即使有一個程序員熟悉它,但其他程序員可能不熟悉它。如果一個程序員編寫了一個引發異常的函數,但其他程序員并不編寫S?E?H框架來捕獲這個異常,那么進程就會被操作系統結束。
????開發人員不使用S?E?H的第二個原因是它不能移植到其他操作系統。許多公司的產品要面向多種操作系統,因此希望有單一的源代碼作為產品的基礎,這是可以理解的。?S?E?H是專門針對Wi?n?d?o?w?s的技術。
本段討論通過異常返回錯誤有關的內容。首先,看一看?Windows?Heap函數,例如H?e?a?p?C?r?e?a?t?e、h?e?a?p?A?l?l?o?c等。回顧第1?8章的內容,我們知道這些函數向開發人員提供一種選擇。通常當某個堆(?h?e?a?p)函數失敗,它會返回?N?U?L?L來指出失敗。然而可以對這些堆函數傳遞H?E?A?P?_?G?E?N?E?R?AT?E?_?E?X?C?E?P?T?I?O?N?S標志。如果使用這個標志并且函數失敗,函數不會返回N?U?L?L,而是由函數引發一個?S?TAT?U?S?_?N?O?_?M?E?M?O?RY軟件異常,程序代碼的其他部分可以用S?E?H框架來捕獲這個異常。
如果想利用這個異常,可以編寫你的?t?r?y塊,好像內存分配總能成功。如果內存分配失敗,可以利用e?x?c?e?p?t塊來處理這個異常,或通過匹配?t?r?y塊與f?i?n?a?l?l?y塊,清除函數所做的事。這非常方便。
程序捕獲軟件異常采取的方法與捕獲硬件異常完全相同。也就是說,前一章介紹的內容可同樣適用于軟件異常。
本節重討論如何讓你自己的函數引發軟件異常,作為指出失敗的方法。實際上,可以用類似于微軟實現堆函數的方法來實現你的函數:讓函數的調用者傳遞一個標志,告訴函數如何指出失敗。引發一個軟件異常很容易,只需要調用R?a?i?s?e?E?x?c?e?p?t?i?o?n函數:
?
????第一個參數?d?w?E?x?c?e?p?t?i?o?n?C?o?d?e是標識所引發異常的值。?H?e?a?p?A?l?l?o?c函數對這個參數設定S?TAT?U?S?_?N?O?_?M?E?M?O?RY。如果程序員要定義自己的異常標識符,應該遵循標準?Wi?n?d?o?w?s錯誤代碼的格式,像Wi?n?E?r?r?o?r.?h文件中定義的那樣。參閱表2?4?-?1。
如果要建立你自己的異常代碼,要填充D?W?O?R?D的4個部分:
?
R?a?i?s?e?E?x?c?e?p?t?i?o?n的第二個參數?d?w?E?x?c?e?p?t?i?o?n?F?l?a?g?s,必須是?0或E?X?C?E?P?T?I?O?N?_N?O?N?C?O?N?T?I?N?U?A?B?L?E。本質上,這個標志是用來規定異常過濾器返回?E?X?C?E?P?T?I?O?N?_CONTINUE?_EXECUTION來響應所引發的異常是否合法。如果沒有向?R?a?i?s?e?E?x?c?e?p?t?i?o?n傳遞EXCEPTION_?NONCONTINUABLE參數值,則過濾器可以返回?E?X?C?E?P?T?I?O?N?_?C?O?N?T?I?N?U?E?_E?X?E?C?U?T?I?O?N。正常情況下,這將導致線程重新執行引發軟件異常的同一?C?P?U指令。但微軟已做了一些動作,所以在調用R?a?i?s?e?E?x?c?e?p?t?i?o?n函數之后,執行會繼續進行。
如果你向R?a?i?s?e?E?x?c?e?p?t?i?o?n傳遞了E?X?C?E?P?T?I?O?N?_?N?O?N?C?O?N?T?I?N?U?A?B?L?E標志,你就是在告訴系統,你引發異常的類型是不能被繼續執行的。這個標志在操作系統內部被用來傳達致命(不可恢復)的錯誤信息。另外,當?H?e?a?p?A?l?l?o?c引發S?TAT?U?S?_?N?O?_?M?E?M?O?RY軟件異常時,它使用E?X?C?E?P?T?I?O?N?_?N?O?N?C?O?N?T?I?N?U?A?B?L?E標志來告訴系統,這個異常不能被繼續。意思就是沒有辦法強制分配內存并繼續運行。
如果一個過濾器忽略E?X?C?E?P?T?I?O?N?_?N?O?N?C?O?N?T?I?N?U?A?B?L?E并返回E?X?C?E?P?T?I?O?N?_?C?O?N?T?I?N?U?E?_E?X?E?C?U?T?I?O?N,系統會引發新的異常:E?X?C?E?P?T?I?O?N?_?N?O?N?C?O?N?T?I?N?U?A?B?L?E?_?E?X?C?E?P?T?I?O?N。
當程序在處理一個異常的時候,有可能又引發另一個異常。比如說,一個無效的內存存取有可能發生在一個f?i?n?a?l?l?y塊、一個異常過濾器、或一個異常處理程序里。當發生這種情況時,系統壓棧異常。回憶一下G?e?t?E?x?c?e?p?t?i?o?n?I?n?f?o?r?m?a?t?i?o?n函數。這個函數返回EXCEPTION_?POINTERS結構的地址。E?X?C?E?P?T?I?O?N?_?P?O?I?N?T?E?R?S的E?x?c?e?p?t?i?o?n?R?e?c?o?r?d成員指向一個EXCEPTION_?R?E?C?O?R?D結構,這個結構包含另一個?E?x?c?e?p?t?i?o?n?R?e?c?o?r?d成員。這個成員是一個指向另外的?E?X?C?E?P?T?I?O?N?_R?E?C?O?R?D的指針,而這個結構包含有關以前引發異常的信息。
通常系統一次只處理一個異常,并且E?x?c?e?p?t?i?o?n?R?e?c?o?r?d成員為N?U?L?L。然而如果處理一個異常的過程中又引發另一個異常,第一個E?X?C?E?P?T?I?O?N?_?R?E?C?O?R?D結構包含有關最近引發異常的信息,并且這個E?X?C?E?P?T?I?O?N?_?R?E?C?O?R?D結構的E?x?c?e?p?t?i?o?n?R?e?c?o?r?d成員指向以前發生的異常的E?X?C?E?P?T?I?O?N?_R?E?C?O?R?D結構。如果增加的異常沒有完全處理,可以繼續搜索這個?E?X?C?E?P?T?I?O?N?_?R?E?C?O?R?D結構的鏈表,來確定如何處理異常。
R?a?i?s?e?E?x?c?e?p?t?i?o?n的第三個參數n?N?u?m?b?e?r?O?f?A?rg?u?m?e?n?t?s和第四個參數p?A?rg?u?m?e?n?t?s,用來傳遞有關所引發異常的附加信息。通常,不需要附加的參數,只需對?p?A?rg?u?m?e?n?t?s參數傳遞N?U?L?L,這種情況下,?R?a?i?s?e?E?x?c?e?p?t?i?o?n函數忽略?n?N?u?m?b?e?r?O?f?A?rg?u?m?e?n?t?s參數。如果需要傳遞附加參數,n?N?u?m?b?e?r?O?f?A?rg?u?m?e?n?t?s參數必須規定由p?A?rg?u?m?e?n?t?s參數所指向的U?L?O?N?G?_?P?T?R數組中的元素數目。這個數目不能超過E?X?C?E?P?T?I?O?N?_?M?A?X?I?M?U?M?_?PA?R?A?M?E?T?E?R?S,EXCEPTION_?MAXIMUM_
PARAMETERS?在Wi?n?N?T.?h中定義成1?5。
在處理這個異常期間,可使異常過濾器參照?E?X?C?E?P?T?I?O?N?_?R?E?C?O?R?D結構中的?N?u?m?b?e?rP?a?r?a?m?e?t?e?r?s和E?x?c?e?p?t?i?o?n?I?n?f?o?r?m?a?t?i?o?n成員來檢查n?N?u?m?b?e?r?O?f?A?rg?u?m?e?n?t?s和p?A?rg?u?m?e?n?t?s參數中的信息。
你可能由于某種原因想在自己的程序中產生自己的軟件異常。例如,你可能想向系統的事件日志發送通知消息。每當程序中的一個函數發現某種問題,你可以調用?R?a?i?s?e?E?x?c?e?p?t?i?o?n并讓某些異常處理程序上溯調用樹查看特定的異常,或者將異常寫到日志里或彈出一個消息框。你還可能想建立軟件異常來傳達程序內部致使錯誤的信息。
?
總結
以上是生活随笔為你收集整理的Windows核心编程 第2 4章 异常处理程序和软件异常的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows核心编程 第23章 结束处
- 下一篇: Windows核心编程 第2 5章 未处