WPF应用在Windows睡眠/唤醒后窗口无响应问题求助
问题根因
- 睡眠挂起时未清理已打开的
MainWindow实例,系统销毁窗口句柄后,静态变量仍持有无效实例,后续操作该实例会触发UI线程死锁 - 每次系统唤醒重复调用
ConfNotifyIcon(),未清理旧的NotifyIcon实例和绑定的事件委托,重复绑定导致消息队列堵塞 - 未在应用退出时解绑
SystemEvents.PowerModeChanged事件,导致内存泄漏和重复回调 - 跨线程操作UI的隐式问题:
System.Timers.Timer运行在线程池线程,部分场景下调度UI操作会和睡眠唤醒后的消息循环冲突
解决方案
1. 完善挂起时的资源清理逻辑
修改PrepareLock方法,同步清理MainWindow实例:
private void PrepareLock() { notifyIcon?.Dispose(); cw?.Close(); // 新增MainWindow清理逻辑 if (mw != null) { if (!mw.IsClosed) { mw.Close(); } mw = null; } }
2. 避免NotifyIcon事件重复绑定
修改ConfNotifyIcon方法,先清理旧实例再创建新实例:
private void ConfNotifyIcon() { // 先清理旧的NotifyIcon资源和绑定事件 if (notifyIcon != null) { notifyIcon.DoubleClick -= OnClick; notifyIcon.Dispose(); } notifyIcon = new Forms.NotifyIcon(); notifyIcon.Icon = new System.Drawing.Icon("Images/Wellformation.ico"); notifyIcon.DoubleClick += OnClick; notifyIcon.ContextMenuStrip = new Forms.ContextMenuStrip(); notifyIcon.ContextMenuStrip.Items.Add("Open", System.Drawing.Image.FromFile("Images/Wellformation.ico"), OnClick); notifyIcon.ContextMenuStrip.Items.Add("Close", System.Drawing.Image.FromFile("Images/Wellformation.ico"), OnClose); notifyIcon.ContextMenuStrip.Items.Add(new Forms.ToolStripSeparator()); notifyIcon.ContextMenuStrip.Items.Add("Exit", System.Drawing.Image.FromFile("Images/Wellformation.ico"), OnExit); }
3. 优化MainWindow打开逻辑,避免无效实例操作
修改OpenMain方法,每次打开前先清理旧实例:
private void OpenMain() { // 清理旧的MainWindow实例,忽略无效句柄异常 if (mw != null) { try { if (!mw.IsClosed) { mw.Close(); } } catch { // 捕获系统已销毁窗口句柄导致的异常 } mw = null; } mw = new MainWindow(); mw.Show(); mw.Activate(); }
4. 解绑系统事件避免泄漏
修改OnExit方法,退出前解绑PowerModeChanged事件:
private void OnExit(object sender, EventArgs e) { SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; PrepareLock(); Application.Current.Shutdown(); }
5. 可选优化:替换线程池Timer为WPF调度Timer
将ConfigWindow中的System.Timers.Timer替换为DispatcherTimer,完全在UI线程运行,避免跨线程调度冲突:
// 替换原有Timer定义 private System.Windows.Threading.DispatcherTimer at = new System.Windows.Threading.DispatcherTimer(); private void ActionTimer() { at.Interval = TimeSpan.FromMinutes(1); at.Tick += ChecktActions; at.Start(); } private void ChecktActions(object sender, EventArgs e) { // 原有逻辑无需修改,当前事件已在UI线程触发 }
内容的提问来源于stack exchange,提问作者Rich G
相关产品推荐
相关产品推荐

