在进程运行时在文本框中显示子进程的标准输出

本文关键字:子进程 标准输出 显示 进程 运行时 文本 | 更新日期: 2023-09-27 18:14:38

我有一个textBox output_txtB的表单,我想运行cmd,执行一些命令并在文本框中显示标准输出。我想在执行每个命令后检索标准输出,而子进程正在工作。我在这里找到了一些解决方案:

将子进程的输出(stdout, stderr)重定向到Visual Studio的output窗口

c#在运行

时获取进程输出

但是它并没有解决我的具体问题,因为我想在文本框中显示标准输出(而不是在控制台或其他地方),并在执行每个命令后立即显示它-我不想在进程退出后检索完整的输出。

我已经尝试在子进程中使用OutputDataReceived事件,正如上面的链接所建议的,但是有一个问题,当我想引用在另一个线程上创建的文本框时,它会抛出InvalidOperationException。下面是我的代码:

        Process process = new Process();
        process.EnableRaisingEvents = true;
        process.StartInfo.FileName = "cmd";
        process.StartInfo.CreateNoWindow = true;
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.RedirectStandardInput = true;
        process.OutputDataReceived += (sender, args) =>
            {
                //here throws an exception
                output_txtB.AppendText(args.Data + Environment.NewLine);
            };
        process.Start();
        process.BeginOutputReadLine();
        //commands I want to execute
        process.StandardInput.WriteLine("example.exe");
        process.StandardInput.WriteLine("script.bat");
        process.StandardInput.WriteLine("ipconfig /all");
        process.StandardInput.WriteLine("dir");
        process.StandardInput.Close();
        process.WaitForExit();

异常的附加信息为:

跨线程操作无效:控制'output_txtB'从创建它的线程以外的线程访问。

任何想法,如何检索子进程的标准输出,并显示在文本框中,而子进程正在工作?

编辑:查看"example.exe"代码:

        Console.WriteLine("line 1");
        System.Threading.Thread.Sleep(2000);
        Console.WriteLine("line 2");
        System.Threading.Thread.Sleep(2000);
        Console.WriteLine("line 3");
        System.Threading.Thread.Sleep(2000);
        Console.WriteLine("line 4");
        System.Threading.Thread.Sleep(2000);
        Console.WriteLine("line 5");

我想要实现的是,每次在文本框 X中显示,进程在标准输出中接收它。但即使我使用Stefano的解决方案,OutputDataReceived事件似乎也会触发,并在进程退出后显示完整的进程输出。

在进程运行时在文本框中显示子进程的标准输出

你应该使用Invoke()。

在不同的线程中,不能更新视图。修改视图的逻辑应该在UI线程上完成,Invoke()就是为此而使用的。

很可能你有一个线程在后台运行并读取进程的输出。在这个线程中,如果你想更新UI,使用Control.Invoke()方法,如下所示。

我更喜欢这种语法,因为它是多么"自然":

    myControl.Invoke((Action)delegate
    {
        //You can put your UI update logic here
        myControl.Text = "Hello World from a different thread";
    });

此外,您不需要在正在修改的控件上直接调用Invoke。在任何控件中调用invoke(或直接在窗体上)将意味着其中的代码将在UI线程上运行,这意味着您也可以更新其他UI。

    myControl.Invoke((Action)delegate
    {
        //You can put your UI update logic here
        myControl.Text = "Hello World from a different thread";
        myOtherControl.Text = "Look I can update other controls";
    });
    myForm.Invoke((Action)delegate
    {
        myControl.Text = "Can even call invoke from my Form";
    }

这是在Windows窗体和WPF中从UI线程外部操作UI对象时常见的错误。

更改流程。OutputDataReceived:

process.OutputDataReceived += (s, args) =>
{
    this.output_txtB.BeginInvoke((MethodInvoker)delegate() { this.output_txtB.AppendText(args.Data + Environment.NewLine); });
};