這一篇文章來談談對於WPF應用程序開發中的未捕獲異常的處理。
首先,我們當然是要求應用程序開發人員,盡可能地在程序可能出現異常的地方都去捕捉異常,使用try…catch的方式。但是總是有一些意外的情況可能會發生,這就導致會出現所謂的“未捕獲異常(UnhandledException)”。對於這一類異常,如果我們沒有一個合適的策略進行處理,則當其發生的時候,會給用戶帶來不太好的使用體驗。例如下面這樣
備注:這個截圖是在Windows 8上面做的,其他操作系統看到的界面可能略有不同。
用戶看到這個窗口的時候,其實一般只能點擊Close the prograrm按鈕。也就是說,這種情況下會導致用戶無法繼續使用這個程序,而且他們還得不到任何具體的消息:到底發生了什么事情了?除非他們去查看Windows的事件日志。(但一般的用戶是不太會這個操作的)
我們可以看到在Windows事件日志中,會有兩個具體的事件。首先是一個.NET Runtime的事件
然后是一個Application Error的事件
通常來說,這樣的用戶體驗有值得改進的地方。我們雖然不能防止異常的產生,但是當意外發生的時候,我們應該要以更好地方式地通知到用戶,或者盡可能地不要影響用戶當前的操作。
在WPF這種應用程序中,會有兩大類未處理異常:一類是在UI線程拋出來的,例如點擊了用戶界面上面的某個控件,然后執行某個代碼的時候,遇到了異常;另一類是非UI線程跑出來的,例如在一個多線程的程序里面,工作線程的代碼遇到了異常。
對於UI線程的未處理異常,我們可以通過監控下面這個事件來處理
Application.Current.DispatcherUnhandledException http://msdn.microsoft.com/en-us/library/system.windows.application.dispatcherunhandledexception.aspx
一個參考代碼如下:
using System; using System.Windows; namespace WpfApplicationExceptionSample { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application { public App() { Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException; } void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) { MessageBox.Show("我們很抱歉,當前應用程序遇到一些問題,該操作已經終止,請進行重試,如果問題繼續存在,請聯系管理員.", "意外的操作", MessageBoxButton.OK, MessageBoxImage.Information);//這里通常需要給用戶一些較為友好的提示,並且后續可能的操作 e.Handled = true;//使用這一行代碼告訴運行時,該異常被處理了,不再作為UnhandledException拋出了。 } } }
運行的效果大致如下
對於非UI線程拋出的未處理異常,我們需要監控另外一個事件來處理
AppDomain.CurrentDomain.UnhandledException http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx
一個參考代碼如下
using System; using System.Windows; namespace WpfApplicationExceptionSample { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application { public App() { AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; } void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { MessageBox.Show("我們很抱歉,當前應用程序遇到一些問題,該操作已經終止,請進行重試,如果問題繼續存在,請聯系管理員.", "意外的操作", MessageBoxButton.OK, MessageBoxImage.Information); } } }
令人不解的是,這個事件中沒有和前面那個事件一樣的e.Handled參數,就是說,雖然這樣是可以捕捉到非UI線程的異常,而且也可以進行相應的處理,但是應用程序還是會退出,也就是說這個異常還是被當作是未處理異常繼續匯報給Runtime。
為了改進這一點,我們可以通過修改配置文件來實現。
<?xml version="1.0" encoding="utf-8" ?> <configuration> <runtime> <legacyUnhandledExceptionPolicy enabled="1"/> </runtime> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> </configuration>
這里的legacyUnhandledExceptionPolicy,如果enabled=1的話,用意是使用早期版本的異常處理策略。