WPF不同工作线程创建显示窗口时InvalidOperationException异常排查
问题根源分析
这个异常是WPF线程亲和性机制导致的:所有WPF UI元素(包括UserPromptWindow)必须由创建它们的线程访问,且只能在该线程上执行UI操作。你的Execute()方法在独立线程中运行,直接创建并操作WPF窗口,后续任何跨线程的窗口访问(比如关闭、状态检查)都会触发这个异常。另外代码中混用WinForms的Application.DoEvents(),这和WPF的Dispatcher消息循环机制冲突,进一步加剧了线程上下文混乱。
修复方案
核心思路是将所有UI相关操作(窗口创建、显示、状态检查、关闭)调度到WPF的UI线程(Dispatcher线程)执行,同时替换WinForms的消息循环逻辑为WPF原生机制:
- 使用
Application.Current.Dispatcher确保窗口的创建和UI操作在UI线程完成 - 用WPF的
DispatcherFrame替代Thread.Sleep()+Application.DoEvents()来处理非模态窗口的等待逻辑 - 移除不必要的
GC.Collect(),WPF会自动管理窗口资源
修改后的代码
/// <summary> /// 执行序列,向用户显示指定字符串 /// </summary> /// <returns></returns> public override bool Execute() { bool bActionResult = true; string sMessageString; if (!GetStringFromParm("CmiActionMessageBox", m_StringToShow, out sMessageString)) { return false; } if (this.ConvertEscapeSequences) { sMessageString = this.GetStringConvertEscapeSequences(sMessageString); } bool? dr = null; // 将所有UI操作调度到UI线程执行 Application.Current.Dispatcher.Invoke(() => { // 在UI线程创建窗口 UserPromptWindow frm = new UserPromptWindow(sMessageString); frm.SetOKButtonText(m_sOkButtonName); frm.SetCancelButtonText(m_sCancelButtonName); frm.WindowStartupLocation = WindowStartupLocation.CenterOwner; if (m_bDialogIsModal) { // 模态窗口直接在UI线程显示 dr = frm.ShowDialog(); } else { // 窗口关闭时设置结果并退出等待帧 frm.Closed += (sender, e) => { dr = frm.ModelessResult; _frame.Continue = false; }; frm.Show(); // 创建DispatcherFrame处理消息循环,替代Sleep+DoEvents var _frame = new DispatcherFrame(); // 定时检查序列是否终止 DispatcherTimer timer = new DispatcherTimer(TimeSpan.FromMilliseconds(50), DispatcherPriority.Background, (s, e) => { if (SequenceMgr.Mgr.BgSequenceDone) { dr = false; if (frm.IsLoaded) { frm.Close(); } _frame.Continue = false; ((DispatcherTimer)s).Stop(); } }, Application.Current.Dispatcher); timer.Start(); Dispatcher.PushFrame(_frame); timer.Stop(); } }); #region 保存结果到变量(如果需要) if (m_bDialogSavesResult) { if (dr == true) { SequenceMgr.Mgr.SetVariableValue(m_sDialogResultVar, "True"); } else { SequenceMgr.Mgr.SetVariableValue(m_sDialogResultVar, "False"); } } #endregion #region 如果配置为点击取消按钮时停止序列,则将操作结果设为false if (m_bDialogCanStopSequence) { if (dr == false) { bActionResult = false; } } #endregion return bActionResult; }
关键修改点说明
- UI线程调度:所有窗口的创建、显示、关闭操作都通过
Dispatcher.Invoke()放到UI线程执行,彻底避免跨线程访问UI元素的问题。 - 非模态窗口等待逻辑:用
DispatcherFrame和DispatcherTimer替代Thread.Sleep()+WinForms.Application.DoEvents(),符合WPF的消息循环机制,不会阻塞UI线程,也不会引发线程冲突。 - 移除
GC.Collect():强制GC不仅没用,还可能干扰WPF的资源回收机制,窗口关闭后WPF会自动处理相关资源。
内容的提问来源于stack exchange,提问作者Jeff Huckins
相关产品推荐
相关产品推荐

