跳到主要内容

WinUI 3异常处理:构造健壮的桌面应用

· 阅读需 5 分钟
作者:小烨App

引言​

任何应用程序都难免会遇到异常——网络断开、文件缺失、空引用、内存不足……关键在于如何优雅地处理这些异常,而不是让应用直接崩溃。

WinUI 3 作为新一代 Windows 桌面应用框架,提供了多层级的异常处理机制。本文将详细介绍 WinUI 3 中常见的异常处理方式,帮助您构建更健壮的应用。

一、异常处理的层次结构​

WinUI 3 应用中的异常可以在不同层级被捕获:

App 层异常 (UnhandledException)← 最外层,兜底
Page/Window 层异常 (try-catch)← 界面层
ViewModel/Service 层异常← 业务逻辑层
async/Task 异常 (TaskScheduler.UnobservedTaskException)← 异步任务

限于篇幅,本文介绍最基本常用的异常处理。

二、Application.Current.UnhandledException​

这是 WinUI 3 应用中最关键的全局异常处理入口。任何未被捕获的异常最终都会到达这里。

基本用法

在 App.xaml.cs 中:

public partial class App : Application {
public App() {
this.InitializeComponent();
// 订阅全局未处理异常事件
this.UnhandledException += OnAppUnhandledException;
}

private void OnAppUnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) {
// 标记异常已处理,阻止应用崩溃
e.Handled = true;

// 记录异常信息
LogException(e.Exception, "UnhandledException");

// 显示友好的错误提示
ShowErrorDialog("应用遇到了一些问题,已尝试恢复。");
}
}

关键点说明​

属性/方法说明
e.Exception获取异常对象,包含堆栈跟踪等信息
e.Handled = true标记异常已处理,应用不会崩溃
e.Handled = false标记未处理,应用会终止

⚠️ 重要提示​

UnhandledException 只能捕获 UI 线程上的异常! 后台线程、异步任务中的异常需要其他方式处理。

三、TaskScheduler.UnobservedTaskException​

处理 async/await 任务中未被观察到的异常。当 Task 被 GC 回收且异常未被观察时触发。

使用方法​

public App() {
this.InitializeComponent();
this.UnhandledException += OnAppUnhandledException;
// 订阅未观察到的任务异常
TaskScheduler.UnobservedTaskException += OnTaskUnobservedException;
}

private void OnTaskUnobservedException(object sender, UnobservedTaskExceptionEventArgs e) {
e.SetObserved(); // 标记异常已被观察
LogException(e.Exception, "UnobservedTaskException");
// 可以选择显示提示或记录日志
}

实际场景示例​

// 这样的代码可能会导致 UnobservedTaskException
async Task DangerousMethod() {
await Task.Run(() => {
throw new InvalidOperationException("后台操作失败");
});
}
// 调用但不等待或处理异常
_ = DangerousMethod(); // 异常可能被忽略

最佳实践​

始终使用 try-catch 包裹异步操作,或使用 await + try-catch 正确处理异常。

四、特定场景的异常处理​

文件 I/O 操作​

private async Task LoadFileAsync(string filePath) {
try {
var file = await StorageFile.GetFileFromPathAsync(filePath);
var content = await FileIO.ReadTextAsync(file);
// 处理文件内容...
}

catch (FileNotFoundException ex) {
await ShowErrorDialog($"文件不存在: {filePath}");
LogException(ex, "FileNotFound");
}

catch (UnauthorizedAccessException ex) {
await ShowErrorDialog($"没有权限访问文件");
LogException(ex, "UnauthorizedAccess");
}

catch (Exception ex) {
await ShowErrorDialog($"读取文件失败: {ex.Message}");
LogException(ex, "FileIOError");
}
}

网络请求异常​

private async Task<HttpResponseMessage> SendRequestAsync(string url) {
try
{
using var client = new HttpClient();
client.Timeout = TimeSpan.FromSeconds(30);
return await client.GetAsync(url);
}
catch (HttpRequestException ex)
{
LogException(ex, "NetworkError");
throw new NetworkException("网络连接失败,请检查网络设置", ex);
}
catch (TaskCanceledException ex)
{
LogException(ex, "RequestTimeout");
throw new TimeoutException("请求超时,请稍后重试", ex);
}
}

五、小烨远控的异常处理实践​

在实际开发中,异常处理不仅是为了防止崩溃,更是为了帮助开发者快速定位问题。

一键反馈​

在小烨远控中,我们设计了便捷的异常反馈机制:

如果您在使用小烨远控的过程中发生了异常,只需点击异常界面的一键反馈即可将错误信息反馈给我们,错误信息不包含任何个人数据。

功能特点​

特性说明
🔒 数据安全仅收集异常堆栈和系统环境信息,不包含任何用户数据
⚡ 一键操作无需填写表单,点击即可完成反馈
🔄 实时通知开发者可及时收到异常报告并修复

六、最佳实践总结​

✅ DO(推荐做法)​

  1. 始终在最外层设置 UnhandledException 兜底
  2. 记录详细的异常日志(类型、堆栈、时间、上下文)
  3. 给用户友好的提示,不要展示太琐碎的技术日志;
  4. 在适当的层级处理特定异常(如网络、文件操作)
  5. 提供异常反馈渠道,让用户能帮助改进应用

❌ DON'T(避免做法)​

  1. 不要吞掉异常而不记录(e.Handled = true 但啥也不做)
  2. 不要在 UI 线程执行耗时操作(用 async/await)
  3. 不要忽略 Task 的异常(使用 try-catch 或 await)
  4. 不要在 catch 中抛出相同异常(会丢失堆栈信息)
  5. 不要在生产环境显示完整的异常堆栈给用户

结语​

异常处理是桌面应用开发中不可忽视的基础设施。WinUI 3 提供了完善的异常处理机制,合理利用这些机制,可以让您的应用更加健壮、用户更加满意。

记住:好的异常处理不是让应用永不报错,而是当错误发生时,应用能够优雅降级、记录问题、帮助用户恢复操作。


如果您在使用小烨远控过程中遇到任何问题,欢迎通过异常界面的一键反馈功能告诉我们,帮助我们做得更好!