.NET 4.0新特性-- Corrupted State Exceptions

作爲程序員,我想很多人應該都有過跟異常打交道的經歷。而且相信也有很多人也都寫過catch(Exception e){//blabla}這種把所有未知異常一股腦兒捕獲並處理掉的代碼吧。不管是爲敷衍客戶也好,讓程序繼續運行以避免糟糕的用戶體驗也罷,在微軟眼中,這種處理方式都是不對滴,特別是當你的程序是作爲一個插件寄存在別的程序如VS,Offcie中時,這種情況下對有些嚴重的異常如訪問衝突我們更應當是讓程序結束而不是繼續運行。然而很多時候,我們並不清楚哪些異常是嚴重的,哪些是可以讓程序繼續運行的,因爲在.NET 4.0以前,CLR會很忠實的把所有大大小小的異常一股腦兒的都拋給程序員處理。不過這個問題在4.0以後會得到很好的解決了。因爲對有些嚴重的會引起進程崩潰的異常的處理以後會由CLR來統一處理而不再交給我們可憐的程序員了。下面我將對這種異樣處理做一些簡單的介紹。

爲什麼需要Corrupted State Exceptions

異常有大有小,小的如字符串爲空,這些一般是用戶輸入問題,它不會引起整個程序或者系統中相關進程出現崩潰的情況;大的如訪問衝突異常,這可能是你的程序在做一些可能會引起操作系統崩潰的事情,這種異常一般都比較嚴重,一般如果出現這種異常,通常程序應該做的是結束當前進程,然後老老實實向用戶報告你犯傻了並提示他重啓程序。不過在.NET 4.0以前,CLR是很相信程序員不會搞出一些諸如catch(Exception e){return;}這種不負責任的代碼的,因此它不分輕重緩急,只要是異常,它統統都會拋出來,這裏面不僅僅有託管代碼的異常,也有一些.NET程序員不太好看懂的COM和WIN32異常。CLR相信程序員在捕獲異常的時候會只處理他們清楚的異常,但很多時候,作爲開發人員,由於上面有老闆,下面有客戶,我們真的很難做人,想想如果老闆動不動就聽又客戶抱怨他們只不過點了兩下按鈕程序就報錯然後結束了,他還能給你加薪麼?雖然很多時候我們清楚我們的代碼不會出問題,但我們很難保證天時地利人和樣樣俱全,爲了給老闆和客戶一個交代,這時候很多人都會選擇去捕獲所有的異常,然後記錄下異常信息,然後程序繼續彪悍的跑下去。

看似一些都很完美,客戶不會再像以前那麼頻繁的抱怨程序down掉,老闆也就高興了。但有人不高興。小的未知異常當然不會捅大的簍子,但對有些可能導致程序甚至操作系統崩潰的異常如果不中斷程序的話可能影響的就是一大片了。這個時候客戶可能不會抱怨你,但他會抱怨微軟出了個爛操作系統,一天到晚藍屏,或者他會抱怨微軟的Office或者IE太爛,他只不過加載了一個插件,結果整個Outlook就報錯崩掉了。你是省事了,但微軟得來被黑鍋,而且他還不知道這個黑鍋裏面到底是咋回事。

當然上面是玩笑,不過不管怎樣,從程序安全和穩定的角度來看catch(Exception e)確實不是一個好的編程習慣,然而木已成舟,既然無法避免程序員偷懶,微軟只能採取一些補救措施了,這就是CLR新的異常處理機制的作用。

CLR怎麼處理Corrupted State Exception

自4.0以後,CLR不會主動給你拋出所有異常了,對於那些它認爲是危險的,可能導致進程崩潰的異常它會標記爲Corrupted StateException並自己處理掉而不是拋給程序員來做,如AccessViolationException這種繼承自SystemException的異常就會被當做Corrupted StateException來處理。不過這裏要注意的是,僅僅異常類型是可能會危險級別的異常還不夠,CLR還會判斷拋出異常的所有者,如果它發現是由操作系統拋出的訪問衝突則會認爲這是狀態崩潰異常,但如果異常是由用戶代碼拋出,則CLR不會對其做特殊處理,它仍然會像以前一樣將其正常拋出。

下面我們給出具體例子來演示.NET 4.0中CLR處理Corrupted State Exception的情況。

首先,我們想辦法制造一個這種嚴重的異常,然後使用try,catch來看CLR是如何處理這段代碼的。

unsafe static void T2(){
try
{
int* pd = stackalloc int[1];
pd[
1911] = 2;
Console.WriteLine(pd[
1111]);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}

在這段代碼中,即使用戶代碼捕獲所有通用異常,程序仍然會終止並拋出AccessViolationException異常信息,這是因爲AccessViolationException是Corrupted State異常,並且它是由系統拋出。所以這裏CLR不會把這個異常交給用戶處理,而是主動報告錯誤,中斷進程,然後退出程序。

我們再看另外一段代碼:

static void T3(){
try
{
throw new System.AccessViolationExceptio();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}

這裏雖然代碼拋出的仍然是一個AccessViolationException異常,但由於它是在用戶代碼中拋出,因此這裏CLR並不會將其攔截自己處理,而是仍然把處理的責任交給了用戶。因爲這裏這個異常並非系統產生的,它認爲用戶清楚這個異常並能對其負責。所以這個方法執行之後程序不會中斷,而是繼續執行。

另外CLR也只會將這種危險的Corrupted State Exceptions交給它自己處理,對於其他的不會引起嚴重問題的異常它仍然像以往一樣拋給程序員自己負責。

如何繼續捕獲Corrupted State Exceptions

那麼CLR包了這塊的異常處理是不是意味着以後我們程序員就沒得選只能老老實實向用戶報告我們的產品不行,然後讓老闆炒我們魷魚了呢?那些.NET 4.0以前發佈的,處處是漏洞的產品我們怎麼處理?

雖然微軟不再那麼相信程序員是負責人的人,但它也做那麼絕。雖然默認.NET 4.0以後CLR會處理這些異常,程序員也不用再操心這些危險的異常了。但你仍然可以繼續你以往敷衍上司的做法。並且微軟還提供了兩種方式。

首先對於以往的程序,微軟提供了兩種選擇:

1. 如果你想把以往舊的代碼在.NET Framework 4.0下編譯但又不想改代碼的話,你可以在你的程序的配置文件中添加一個新的節點:legacyCorruptedState­­ExceptionsPolicy=true,它使得你的代碼仍能按照以前處理異常的方式來繼續運行。

2. 如果你不想有任何改變,直接把以前已經編譯好的程序在.NET Framework 4.0下運行則不需要任何改變,CLR會保證所有的異常仍然按照以往的方式處理。

其次,對於那些使用了.NET Framework 4.0 但又想自己處理這些導致程序狀態崩潰的異常,微軟同樣提供了選擇,他們在.NET 4.0中增加了一個新的命名空間:System.Runtime.ExceptionServices,這裏面有個特性類叫做HandleProcessCorruptedStateExceptionsAttribute,你只需要在相應方法上添加這個屬性,CLR就會把所有的異常處理交給你做,就像以前一樣。e.g.

代碼
[HandledProcessCorruptedStateExceptions]
public static int Main(){
try
{
// 捕獲任何系統中出現的異常
CallMainProgramLoop();
}
catch (Exception e)
{
//這裏我們可以捕獲任何異常
// 這裏我們會捕獲任何異常,包括普通級別的異常和系統級的異常,
    
// 並且我們清楚知道系統可能會拋出一些高級別的導致程序崩潰的異常
System.Console.WriteLine(e.Message);
return 1;
}
return 0;
}

 

當然要注意的是這個特性只能應用在方法上。

總結

異常處理常常是程序員心中的一塊心病,儘管微軟認爲自己得爲縱容程序員濫用異常捕獲負責然後添加了這個新的異常處理機制,不過在他們看來,那種catch(Exception e)的行爲仍然是不對的。他們認爲異常的出現表明當前程序的狀態出現了問題,而程序員應當清楚這些錯誤的狀態所造成的後果,所以程序員應當捕獲具體的異常並作出正確的處理,而不是因爲偷懶或者省事去簡單處理所有異常。

參考資料:

Handling Corrupted State Exceptions        作者:Andrew Pardoe     http://msdn.microsoft.com/en-us/magazine/dd419661.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章