首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在按钮单击事件中调用Process.WaitForExit()是阻塞程序

在按钮单击事件中调用Process.WaitForExit()是阻塞程序
EN

Stack Overflow用户
提问于 2019-11-08 22:25:52
回答 1查看 176关注 0票数 0

我也写了同样的应用程序,使用Windows窗体在按钮被按下后运行进程,并读取其标准输出。当我在button1_Click()中调用"test()“方法时,我的程序被阻塞了。但是当我在"Form1“构造函数中调用"test()”时,一切都像预期的那样工作。问题出在哪里?

代码语言:javascript
运行
复制
using System;
using System.Windows.Forms;

namespace DISMassistant
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            process1.StartInfo.RedirectStandardError = true;
            process1.StartInfo.RedirectStandardOutput = true;
            process1.StartInfo.UseShellExecute = false;
            process1.StartInfo.FileName = "cmd.exe";
            process1.StartInfo.Arguments = "/?";

        }

        public void button1_Click(object sender, EventArgs e)
        {

        }

        public void test()
        {
            process1.Start();

            process1.BeginOutputReadLine();
            process1.BeginErrorReadLine();

            process1.WaitForExit();
            process1.CancelOutputRead();
            process1.CancelErrorRead();
            process1.Close();
        }

        private void process1_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
        {
            if (string.IsNullOrEmpty(e.Data)) return;

            richTextBox1.Text += e.Data + "\n";
        }

        private void process1_ErrorDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
        {
            if (string.IsNullOrEmpty(e.Data)) return;

            richTextBox1.Text += e.Data + "\n";
        }
    }
}
EN

回答 1

Stack Overflow用户

发布于 2019-11-12 17:34:51

修复了示例代码中所有缺失的部分,就没有问题了(当然,除了您不想在图形用户界面应用程序中使用WaitForExit之外):

代码语言:javascript
运行
复制
// Warning - The code below is WRONG! Awful even, since it will _appear_ to work in some cases.
void Main()
{
    Application.Run(new Form1());
}

public class Form1 : Form
{
    Process process1 = new Process();
    Button button1;
    RichTextBox richTextBox1;

    public Form1()
    {
        button1 = new Button { Text = "Run" };
        button1.Click += button1_Click;
        Controls.Add(button1);

        richTextBox1 = new RichTextBox { Left = 100 };
        Controls.Add(richTextBox1);

        process1.StartInfo.RedirectStandardError = true;
        process1.StartInfo.RedirectStandardOutput = true;
        process1.StartInfo.UseShellExecute = false;
        process1.StartInfo.FileName = "cmd.exe";
        process1.StartInfo.Arguments = "/?";
    }

    public void button1_Click(object sender, EventArgs e)
    {
        test();
    }

    public void test()
    {
        process1.Start();

        process1.OutputDataReceived += process1_OutputDataReceived;
        process1.ErrorDataReceived += process1_ErrorDataReceived;

        process1.BeginOutputReadLine();
        process1.BeginErrorReadLine();

        process1.WaitForExit();
        process1.CancelOutputRead();
        process1.CancelErrorRead();
        process1.Close();
    }

    private void process1_OutputDataReceived(object sender, 
                                             System.Diagnostics.DataReceivedEventArgs e)
    {
        if (string.IsNullOrEmpty(e.Data)) return;

        richTextBox1.Text += e.Data + "\n";
    }

    private void process1_ErrorDataReceived(object sender, 
                                            System.Diagnostics.DataReceivedEventArgs e)
    {
        if (string.IsNullOrEmpty(e.Data)) return;

        richTextBox1.Text += e.Data + "\n";
    }
}

但是,这仍然是您不想做的事情。GUI控件不应该从创建它们的线程之外的其他线程访问。我在测试应用程序中没有遇到问题,但即使它正常工作,这也只是一场等待发生的灾难。

碰巧RichTextBoxText属性的处理有点奇怪。如果句柄还没有创建,Text只会更改类中的一个字段。它不执行任何多线程访问检查或其他任何操作。现在,当实际创建句柄时,此字段的值将实际应用于控件的文本-在正确的线程上。

这可能就是你看到自己怪异行为的原因。在表单构造函数中,句柄尚未创建。从后台线程访问仍然不是一个好主意,但在大多数情况下,它实际上不会破坏任何东西(您可能会丢失一些进程输出)。当显示表单时,将创建句柄,并且文本框显示“正确”输出。

当表单已经创建(并显示)时,这一点会发生变化。在单线程单元线程上,您需要自己处理多线程。无论出于什么原因,RichTextBox都不是(总是)那样的控件。因此,在STA上,代码碰巧工作-不可靠或不安全,但它不会挂起,也不会崩溃。

但您的UI线程似乎处于多线程单元中。在MTA中,当您尝试将文本分配给RichTextBox (使用创建的句柄)时,消息将被编组到UI线程。但是您的UI线程正忙于等待进程退出!因此,你被吊死了。

你怎么解决这个问题呢?

  1. 从不从创建它们的线程以外的线程访问图形用户界面控件。
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58768417

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档