WinUI 3桌面应用中无法捕获的Access violation错误排查求助
WinUI 3桌面应用Access Violation崩溃排查问题
环境与症状
- WinUI 3 v1.2,.NET 7,x64 Debug模式
- 仅特定操作序列后崩溃,Visual Studio Output窗口显示:
程序'[28716] xxxxxxx.exe'已退出,代码为3221225477 (0xc0000005) 'Access violation'。
- 已注册的全局异常处理器均未触发:
UnhandledException += App_UnhandledException; System.AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; Microsoft.UI.Xaml.Application.Current.UnhandledException += Current_UnhandledException;
窗口相关逻辑
次级窗口启动代码
if (App.ShellPage.SettingsStatusWindow) { StatusWindow = new StatusWindow(); // create new window StatusWindow.Activate(); } else { WeakReferenceMessenger.Default.Send(new CloseWindowMessage()); // close windows }
消息触发窗口关闭
WeakReferenceMessenger.Default.Register<CloseWindowMessage>(this, (r, m) => { WeakReferenceMessenger.Default.Unregister<TraceMessage>(this); WeakReferenceMessenger.Default.Unregister<CloseWindowMessage>(this); Close(); });
标题栏Close按钮触发关闭
private void StatusWindow_Closed(object sender, WindowEventArgs args) { Closed -= StatusWindow_Closed; WeakReferenceMessenger.Default.UnregisterAll(this); WeakReferenceMessenger.Default.Send(new WindowClosedMessage()); }
注:使用CommunityToolkit.Mvvm的WeakReferenceMessenger
异常现象差异
- 通过
CloseWindowMessage关闭窗口后,约1秒内触发Access Violation - 点击标题栏Close按钮关闭窗口后,后续操作无异常
- 尝试搭建小型复现应用,无法触发该错误
排查方案
1. 启用本机代码调试捕获崩溃
在项目属性的调试选项卡中,勾选「启用本机代码调试」。这样Access Violation发生时会直接触发调试中断,可查看完整的托管+本机调用栈,定位到具体的内存访问违规位置。
2. 追踪消息流向与生命周期
在消息发送、注册、注销的关键节点添加Debug日志,确认消息处理的时机是否正确:
// 发送CloseWindowMessage时记录接收者数量 Debug.WriteLine($"Sending CloseWindowMessage, recipients count: {WeakReferenceMessenger.Default.GetRegisteredRecipients<CloseWindowMessage>().Count()}"); WeakReferenceMessenger.Default.Send(new CloseWindowMessage()); // StatusWindow注销时记录实例哈希 Debug.WriteLine($"Unregistering StatusWindow {this.GetHashCode()}"); WeakReferenceMessenger.Default.UnregisterAll(this);
同时给StatusWindow添加析构函数,确认对象是否被正确回收:
~StatusWindow() { Debug.WriteLine($"StatusWindow {this.GetHashCode()} finalized"); }
3. 统一窗口关闭后的消息注销逻辑
当前两种关闭方式的注销逻辑不一致:消息触发时仅注销特定消息,标题栏关闭时注销所有消息。建议统一逻辑,先关闭窗口再注销消息,避免窗口关闭过程中还有消息触发:
WeakReferenceMessenger.Default.Register<CloseWindowMessage>(this, (r, m) => { Close(); // 窗口关闭完成后再统一注销所有消息 WeakReferenceMessenger.Default.UnregisterAll(this); });
4. 生成并分析崩溃转储
在Visual Studio的「调试→选项→调试→转储文件」中,开启「自动创建转储文件」,设置转储类型为「完整」。应用崩溃后生成的.dmp文件可直接在Visual Studio中打开,分析调用栈、内存对象,定位违规的内存引用。
5. 检查窗口实例的强引用
排查是否有其他模块持有StatusWindow的强引用,导致窗口关闭后无法被GC回收,进而引发后续的消息访问问题。可使用Visual Studio的内存诊断工具(调试→性能探查器→内存使用),在窗口关闭后拍摄内存快照,搜索StatusWindow实例是否仍存在。
内容的提问来源于stack exchange,提问作者aturnbul
相关产品推荐
相关产品推荐

