托管異常處理建構在Windows作業系統的結構化異常處理之上,通常稱為SEH。這意味着CLR了解如何在SEH和托管異常系統之間進行互操作,這是一個非常關鍵的點,因為SEH基于異常代碼的概念,而托管異常處理則表示使用托管類型的異常。CLR相應地将SEH異常映射到托管異常,具體取決于引發SEH異常的方式和引發者。
注意:下面的讨論重點是運作在Windows作業系統上的桌面CLR。雖然讨論的目的是幫助了解這個概念,但是它使用了一些将來可能會改變的實作細節來說明。
托管代碼中的同步異常(Synchronous exceptions)
當托管代碼使用throw關鍵字引發異常時,它已經執行個體化了一個托管異常對象,該對象将表示引發的異常。這将傳遞給CLR,CLR線上程上設定一些與異常相關的狀态,并調用Kernel32的RaiseException API來引發托管異常。此API的第一個參數是引發異常的SEH異常代碼,CLR傳遞0xE0434F4D(托管異常SEH代碼)。
這時,作業系統進入處理場景,開始在引發異常的線程堆棧上尋找SEH異常處理程式。CLR将其函數之一注冊為OS的異常處理程式,以處理托管代碼引起的異常。當它看到CLR SEH異常代碼時,它知道正在引發托管異常,并繼續查找線程狀态以檢索與異常相關的詳細資訊(例如,辨別表示引發異常的托管異常對象)。
是以,在同步托管抛出的情況下,很容易将SEH異常映射到托管異常類型。
托管代碼中的異步異常(Asynchronous exceptions)
簡單地說,異步異常是在沒有顯式抛出的情況下引發的異常。如果執行算術操作(例如除以零異常)或使用可能導緻通路沖突(AV)等異常的不安全托管代碼,則在托管代碼中可能會發生這種情況。異步異常的有趣之處在于,它們是使用它們唯一的SEH異常代碼來表示的。例如AV用0xc000005表示,除以0(整數)用0xC0000094表示,除以0(浮點數)用0xC000008E表示,這裡列出了常見的異常,異常代碼值可以在WinNT.h中找到。
當在托管代碼中引發此類異常時,作業系統再次開始在引發異常的線程堆棧上查找異常處理程式。當調用CLR的異常處理程式時,它知道所讨論的異常不是托管代碼同步抛出的,因為異常代碼不是CLR SEH異常代碼。是以,它不再查找與異常相關的詳細資訊的線程狀态,而是将SEH異常映射到一個托管異常類型。例如,除以零異常(整數和浮點)使用System.DivideByZeroException表示。
同樣,當在托管代碼中生成真正的AV時,它的異常代碼為0xc000005。由于托管代碼具有空引用的概念,是以運作時很少再進行檢查來确定AV是否表示嘗試使用空引用。如果是,則映射到System.NullReferenceException。否則,它将映射到System.AccessViolationException類型(在v2.0和更高版本的運作時中)。
在Native代碼中引發異常
CLR檢視SEH異常的另一種方式是,托管代碼使用平台調用服務(簡稱PInvoke)等機制調用Native代碼。
在下面的示例中,堆棧自上而下增長,藍色架構表示托管代碼,而紅色架構表示Native代碼:

接下來會發生什麼?
- 當作業系統從特定SEH異常的抛出點為堆棧上的第一個托管幀調用CLR的異常處理程式(假設堆棧上沒有上下文轉換)時,此映射僅完成一次。
- 由于此映射是基于SEH異常代碼完成的,是以CLR知道給定的托管異常執行個體是否表示真正的Native SEH異常。例如,CLR知道System.AccessViolationException執行個體是否表示真正的AV或是托管代碼引發的正常托管異常。為什麼這很重要?好吧,當CLR确定了異常的損壞嚴重性時,它就會這樣做!