【问题标题】:TextBox not getting updated from different thread unless MessageBox is shown除非显示 MessageBox,否则 TextBox 不会从不同的线程更新
【发布时间】:2014-06-26 23:42:05
【问题描述】:

我正在尝试执行命令行程序并将其实时打印输出到文本框:

private void btnExecute_Click(object sender, EventArgs e)
{
    ProcessStartInfo startInfo = new ProcessStartInfo();
    startInfo.WindowStyle = ProcessWindowStyle.Hidden;
    startInfo.CreateNoWindow = true;
    startInfo.FileName = Application.StartupPath + "\\Deps\\ats.exe";
    startInfo.UseShellExecute = false;
    startInfo.RedirectStandardOutput = true;

    using (Process exeProcess = Process.Start(startInfo))
    {
        exeProcess.OutputDataReceived += exeProcess_OutputDataReceived;
        exeProcess.BeginOutputReadLine();

        //MessageBox.Show("Hello"); //Notice this message box before calling WaitForExit                    
        exeProcess.WaitForExit(45000);
    }

    private void exeProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
    {
        if (txtOutput.InvokeRequired)
        {
            txtOutput.Invoke(new MethodInvoker(delegate { txtOutput.Text += Environment.NewLine + e.Data; }));
        }
    }
}

代码运行没有错误,但没有向 txtOutput 打印任何内容 但是,如果我取消注释消息框,则会显示 MessageBox 并实时更新 txtOutput 现在,如果我单击“确定”关闭 MessageBox,txtOutput 将再次停止更新!

这里到底发生了什么?为什么只有在显示 MessageBox 时才会更新 textBox?

【问题讨论】:

  • 我怀疑您实际上并没有像您想象的那样从控制台应用程序获取输出(我的控制台重定向有点生疏)。将一些调试代码放入您的委托中,以将e.Data 输出到调试窗口,以验证您实际上得到了一些输出。
  • @CodingGorilla,我知道我正在从控制台应用程序获取输出,因为只要显示 MessageBox,文本框就会显示输出,但只要我关闭消息框,输出就会停止更新跨度>

标签: c# multithreading user-interface


【解决方案1】:

好的,所以您遇到的问题可能是 exeProcess.WaitForExit(45000); 阻塞了 UI 线程。所以对txtOutput.Invoke的调用会向窗口消息队列发送一条消息,由UI线程处理。由于该 UI 线程因等待状态而被阻塞,因此它无法处理这些文本框帖子。

所以你需要做的是等待进程退出而不阻塞 UI 线程。有几种方法可以做到这一点,但一种方法是使用带有回调的进程Exited 事件。我在这里看不到足够的代码来给出一个完整的例子,但你想重写这样的东西:

private Process _exeProcess;

private void btnExecute_Click(object sender, EventArgs e)
{
    ProcessStartInfo startInfo = new ProcessStartInfo();
    startInfo.WindowStyle = ProcessWindowStyle.Hidden;
    startInfo.CreateNoWindow = true;
    startInfo.FileName = Application.StartupPath + "\\Deps\\ats.exe";
    startInfo.UseShellExecute = false;
    startInfo.RedirectStandardOutput = true;

    _exeProcess = Process.Start(startInfo);
    _exeProcess.OutputDataReceived += exeProcess_OutputDataReceived;
    _exeProcess.BeginOutputReadLine();    
    _exeProcess.Exited += ContinueOnYourWay;
}

private void ContinueOnYourWay(object sender, EventArgs e) 
{
    // Clean up
    _exeProcess.Dispose();
    _exeProcess = null;

    // More code here
}

private void exeProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
    if (txtOutput.InvokeRequired)
    {
        txtOutput.Invoke(new MethodInvoker(delegate { txtOutput.Text += Environment.NewLine + e.Data; }));
    }
 }

【讨论】:

  • 是的,这行得通,我将不得不稍微修改我的其余代码,我想我也可以等待:while (!exeProcess.HasExited) { //Thread.Sleep(500) ;应用程序.DoEvents(); }
  • 作为一般经验法则,在 UI 事件处理程序(即 UI 线程)中放置任何类型的等待通常是个坏主意。这几乎总是会导致您的 UI 冻结并变得无响应,并且在更高版本的 Windows 中会导致“此程序没有响应”对话框询问用户是否要终止它。
  • 刚刚看到对您之前评论的修改。 Application.DoEvents() 有效,它基本上屈服于WndProc 以允许它处理一些事件。我个人不会这样做,但它应该可以工作。
  • 感谢您的解释,我认为 Exited 事件将是一个很好的解决方案
猜你喜欢
  • 2021-05-14
  • 2019-05-09
  • 2017-03-11
  • 2011-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-09
  • 1970-01-01
相关资源
最近更新 更多