沒有被 Try/Catch 擷取的 SEHException
在後台執行緒中,我的應用程序會定期檢查網路文件夾(UNC 路徑)以獲取應用程序更新。它讀出文件的彙編版本,如下所示:
Try newVers = System.Reflection.AssemblyName.GetAssemblyName("\\server\app.exe").Version Catch ex As Exception ' ignore End Try這個片段經常被執行,到目前為止,我猜在多個客戶網站上總共執行了超過 100.000 次,沒有問題。
有時,
GetAssemblyName會引發 aFileNotFoundException,例如在無法訪問網路文件夾的情況下(可能會發生並且必須處理)。這個異常被Catch下面的塊擷取,一切正常。然而,在三個報告的案例中,該
GetAssemblyName電話引發了SEHException. 奇怪的是,這個異常並沒有被Catch下面的塊擷取,而是被我的全域未處理異常處理程序(System.AppDomain.CurrentDomain.UnhandledException)擷取。結果,應用程序崩潰。這是異常詳細資訊(不幸的是,我的錯誤處理常式沒有記錄異常的
ErrorCode和欄位):CanResumeCaught exception: System.Runtime.InteropServices.SEHException Message: External component has thrown an exception. Source: mscorlib TargetSite: System.Reflection.AssemblyName nGetFileInformation(System.String) StackTrace: at System.Reflection.AssemblyName.nGetFileInformation(String s) at System.Reflection.AssemblyName.GetAssemblyName(String assemblyFile) at SyncThread.Run() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()為什麼異常沒有被
Catch下面的塊擷取?(也許這是相關的:這只發生在客戶站點上,其中 UNC 路徑指向的伺服器不是本地網路的一部分,而是 VPN 上的遠端伺服器。)
從 .NET 4 開始,一些
SEHExceptions 表示損壞的程序狀態,並被稱為“損壞的狀態異常”。這些是諸如段錯誤/訪問衝突之類的事情,在檢測到記憶體損壞後會引發異常。雖然這些錯誤仍映射回託管 .NET
SEHExceptions,但預設情況下無法擷取它們,因此try { ... } catch (Exception ex) { ... }不會處理它們。您可以選擇處理這些異常(通過屬性或應用程序配置文件中的策略更改),但不建議這樣做,因為您的程序現在可能正在處理無效數據:
CLR 始終使用與程序本身引發的異常相同的機制將 SEH 異常傳遞給託管程式碼。只要程式碼不嘗試處理它無法合理處理的異常情況,這不是問題。大多數程序在訪問衝突後無法安全地繼續執行。不幸的是,CLR 的異常處理模型一直鼓勵使用者通過允許程序擷取 System.Exception 層次結構頂部的任何異常來擷取這些嚴重錯誤。但這很少是正確的做法。
編寫 catch (Exception e) 是一個常見的程式錯誤,因為未處理的異常會產生嚴重後果。但是您可能會爭辯說,如果您不知道函式會引發什麼錯誤,那麼您應該在程序呼叫該函式時防止所有可能的錯誤。在您考慮當您的程序可能處於損壞狀態時繼續執行意味著什麼之前,這似乎是一個合理的行動方案。有時中止並重試是最好的選擇:沒有人喜歡看到 Watson 對話框,但最好重新啟動程序而不是損壞數據。
擷取由他們不理解的上下文引起的異常的程序是一個嚴重的問題。但是您無法通過使用異正常範或其他一些契約機制來解決問題。託管程序能夠接收 SEH 異常通知也很重要,因為 CLR 是適用於多種應用程序和主機的平台。某些主機,例如 SQL Server,需要完全控制其應用程序的程序。與本機程式碼互操作的託管程式碼有時必須處理本機 C++ 異常或 SEH 異常。
但是大多數編寫 catch (Exception e) 的程序員並不真的想擷取訪問衝突。他們寧願在發生災難性錯誤時停止程序的執行,而不是讓程序在未知狀態下跛行。對於託管託管載入項(如 Visual Studio 或 Microsoft Office)的程序尤其如此。如果載入項導致訪問衝突,然後吞下異常,則主機可能會對其自身的狀態(或使用者文件)造成損害,而不會意識到出現問題。
如果您確實決定處理這些異常,則需要考慮很多 - 正如文章所述“編寫正確的程式碼來處理 CSE 並繼續安全地執行該過程非常困難。”
特別是,
finally異常已通過的任何塊都沒有被執行(因此,例如,任何文件句柄在收集之前都是懸空的),甚至可能會跳過受約束的執行區域!此外,您可能應該將此作為錯誤報告給 Microsoft,因為
GetAssemblyName不應該拋出這種異常。