You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 13:17:12