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

引言

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

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 提供了完善的异常处理机制,合理利用这些机制,可以让您的应用更加健壮、用户更加满意。

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


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