未捕获的异常
在.NET中,我们使用 try-catch-finally 来处理异常,但很难保证所有的异常都已经被捕获。这种未捕获的异常就会导致程序卡死,数据丢失等bug。
WPF中提供了处理这些未捕获的异常的方法,通过注册事件可以对未经处理的全局异常集中执行自定义操作,从而增加程序的稳定性。
UI线程上的异常
对于在UI线程上运行的代码抛出的的异常,Application都将引发一个 DispatcherUnhandledException。
当自己处理过该异常,不想WPF继续处理该异常时,需要将 Handled 属性设为 true。通俗来讲,如果不希望程序卡死,就需要将 Handled 属性设为 true。
全局异常
对于任何未处理的异常,都会引发UnhandledException。如果UI线程中异常未处理也会触发。
从.NET Framework 4开始,损坏进程状态异常将不引发该事件,如堆栈溢出,或者是访问冲突。因为默认情况下,公共语言运行时 (CLR) 并不把这些异常输出到托管代码,且不为它们调用 try/catch 块。
Task内未处理异常
对于Task内未处理的异常,Application将会引发一个UnobservedTaskException异常。
程序实例
在MainWindow上添加一个按钮
注册事件:
/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
//处理未捕获的UI异常
this.DispatcherUnhandledException += App_DispatcherUnhandledException;
//全局异常
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
//处理Task未捕获的异常
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
}
private void TaskScheduler_UnobservedTaskException(object sender,
UnobservedTaskExceptionEventArgs e)
{
MessageBox.Show(e.Exception.Message, "TaskScheduler_UnobservedTaskException", MessageBoxButtons.OK, MessageBoxIcon.Error);
//异常标记为“已察觉到”,这样就不会导致程序崩溃
e.SetObserved();
}
/// <summary>
/// 处理未捕获非UI线程的异常
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
MessageBox.Show("something is wrong.", "CurrentDomain_UnhandledException", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
/// <summary>
/// 处理未捕获的UI异常
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
//标识异常已经处理过了
//
e.Handled = true;
MessageBox.Show(e.Exception.Message, "DispatcherUnhandledException", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
- 点击按钮,直接抛出异常
private void Btn_Click(object sender, RoutedEventArgs e)
{
//直接抛出异常
throw new InvalidOperationException("throw Exception.");
}
- 点击按钮,Dispatcher抛出异常
private void Btn_Click(object sender, RoutedEventArgs e)
{
Dispatcher.BeginInvoke(new Action(() => { throw new InvalidOperationException("Dispatcher--throw Exception."); }));
}
- 点击按钮,Thread抛出异常
private void Btn_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(() => { throw new InvalidOperationException("Thread--throw Exception.");
});
t.Start();
}
点击按钮,抛出异常:
- 点击按钮,Task抛出异常
private void Btn_Click(object sender, RoutedEventArgs e)
{
//Thread t = new Thread(() => { throw new InvalidOperationException("Thread--throw Exception."); });
Task.Run(() => { throw new InvalidOperationException("Task--throw Exception."); });
}