在开发过程中我们一般都用try/catch块来捕获异常,但不是每个异常我们都能捕获,程序总会出现一些意想不到的情况,抛出一些未捕获的异常,这时就要用到全局异常捕获,即在程序的最外层加上捕获未处理异常,以免程序奔溃,影响用户体验。
| 事件 | 描述 |
|---|---|
| Application.DispatcherUnhandledException | UI线程的未处理异常捕获 |
| AppDomain.UnhandledException | 非UI线程的未处理异常捕获 |
| TaskScheduler.UnobservedTaskException | Task内未处理异常捕获 |
- 构造中添加注册和退出事件
public App()
{
//注册开始和退出事件
Startup += App_Startup;
Exit += App_Exit;
}
- 注册事件中绑定异常处理事件
private void App_Startup(object sender, StartupEventArgs e)
{
//UI线程未捕获异常处理事件
DispatcherUnhandledException += App_DispatcherUnhandledException;
//Task线程内未捕获异常处理事件
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
//非UI线程未捕获异常处理事件
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
- 退出事件中添加关闭程序需要处理的业务
private void App_Exit(object sender, ExitEventArgs e)
{
//程序退出时需要处理的业务
}
- 提取异常信息函数
////// 提取异常信息 /// private static string ExceptionToString(Exception ex, string info) { StringBuilder str = new StringBuilder($"{DateTime.Now}, {info}发生了一个错误!{Environment.NewLine}"); if (ex.InnerException == null) { str.Append($"【对象名称】:{ex.Source}{Environment.NewLine}"); str.Append($"【异常类型】:{ex.GetType().Name}{Environment.NewLine}"); str.Append($"【详细信息】:{ex.Message}{Environment.NewLine}"); str.Append($"【堆栈调用】:{ex.StackTrace}"); } else { str.Append($"【对象名称】:{ex.InnerException.Source}{Environment.NewLine}"); str.Append($"【异常类型】:{ex.InnerException.GetType().Name}{Environment.NewLine}"); str.Append($"【详细信息】:{ex.InnerException.Message}{Environment.NewLine}"); str.Append($"【堆栈调用】:{ex.InnerException.StackTrace}"); } return str.ToString(); }
- UI线程未捕获异常处理函数
////// UI线程未捕获异常处理函数 /// private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) { try { e.Handled = true; string msg = ExceptionToString(e.Exception, "UI线程"); MessageBox.Show(msg, "系统错误", MessageBoxButton.OK, MessageBoxImage.Error); } catch (Exception ex) { string msg = ExceptionToString(ex, "UI线程 处理函数"); MessageBox.Show(msg, "系统错误", MessageBoxButton.OK, MessageBoxImage.Error); } }
备注:把 Handled 属性设为true,表示此异常已处理,程序可以继续运行,不会强制退出。
但不是所有的异常都可恢复,
如果异常是FileNotFoundException,程序可以在处理异常后继续运行,
如果异常是StackOverflowException,则无法再继续运行,即将关闭。
- Task线程内未捕获异常处理函数
////// Task线程内未捕获异常处理函数 /// private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { try { string msg = ExceptionToString(e.Exception, "Task线程"); MessageBox.Show(msg, "系统错误", MessageBoxButton.OK, MessageBoxImage.Error); e.SetObserved();//设置该异常已察觉(这样处理后就不会引起程序崩溃) } catch (Exception ex) { string msg = ExceptionToString(ex, "Task线程 处理函数"); MessageBox.Show(msg, "系统错误", MessageBoxButton.OK, MessageBoxImage.Error); } }
备注:该捕获存在延时
- 非UI子线程未捕获异常处理函数
////// 非UI线程未捕获异常处理函数 /// private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { try { string msg; if (e.ExceptionObject is Exception ex) { msg = ExceptionToString(ex, "非UI线程"); } else { msg = $"发生了一个错误!信息:{e.ExceptionObject}"; } MessageBox.Show(msg, "系统错误", MessageBoxButton.OK, MessageBoxImage.Error); } catch (Exception ex) { string msg = ExceptionToString(ex, "非UI线程 处理函数"); MessageBox.Show(msg, "系统错误", MessageBoxButton.OK, MessageBoxImage.Error); } }
备注:子线程异常处理完成后程序还是会结束,如果不希望程序退出则在App.config中添加如下内容
这里的



