System.Diagnostics.Process RedirectStandardOutput 实时出现在 Winforms 文本框中的问题

发布于 2024-08-31 07:44:58 字数 4243 浏览 5 评论 0原文

我在 Winforms 文本框中实时呈现控制台应用程序的重定向输出时遇到问题。消息是逐行生成的,但是一旦需要与表单交互,就不会显示任何内容。

按照 Stackoverflow 和其他论坛上的许多示例,在流程完成之前,我似乎无法将流程的重定向输出显示在表单的文本框中。

通过将调试行添加到“stringWriter_StringWritten”方法并将重定向的消息写入调试窗口,我可以看到在进程运行期间到达的消息,但在进程完成之前这些消息不会出现在表单的文本框中。

感谢您对此的任何建议。

这是代码的摘录

public partial class RunExternalProcess : Form
{
    private static int numberOutputLines = 0;
    private static MyStringWriter stringWriter;

    public RunExternalProcess()
    {
        InitializeComponent();

        // Create the output message writter
        RunExternalProcess.stringWriter = new MyStringWriter();
        stringWriter.StringWritten += new EventHandler(stringWriter_StringWritten);

        System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();

        startInfo.FileName = "myCommandLineApp.exe";

        startInfo.UseShellExecute = false;
        startInfo.RedirectStandardOutput = true;
        startInfo.CreateNoWindow = true;
        startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;

        using (var pProcess = new System.Diagnostics.Process())
        {
            pProcess.StartInfo = startInfo;
            pProcess.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(RunExternalProcess.Process_OutputDataReceived);
            pProcess.EnableRaisingEvents = true;

            try
            {
                pProcess.Start();

                pProcess.BeginOutputReadLine();
                pProcess.BeginErrorReadLine();

                pProcess.WaitForExit();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
            finally
            {
                pProcess.OutputDataReceived -= new System.Diagnostics.DataReceivedEventHandler(RunExternalProcess.Process_OutputDataReceived);
            }
        }
    }

    private static void Process_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
    {
        if (!string.IsNullOrEmpty(e.Data))
        {
            RunExternalProcess.OutputMessage(e.Data);
        }
    }

    private static void OutputMessage(string message)
    {
        RunExternalProcess.stringWriter.WriteLine("[" + RunExternalProcess.numberOutputLines++.ToString() + "] - " + message);
    }

    private void stringWriter_StringWritten(object sender, EventArgs e)
    {
        System.Diagnostics.Debug.WriteLine(((MyStringWriter)sender).GetStringBuilder().ToString());

        SetProgressTextBox(((MyStringWriter)sender).GetStringBuilder().ToString());
    }

    private delegate void SetProgressTextBoxCallback(string text);

    private void SetProgressTextBox(string text)
    {
        if (this.ProgressTextBox.InvokeRequired)
        {
            SetProgressTextBoxCallback callback = new SetProgressTextBoxCallback(SetProgressTextBox);
            this.BeginInvoke(callback, new object[] { text });
        }
        else
        {
            this.ProgressTextBox.Text = text;
            this.ProgressTextBox.Select(this.ProgressTextBox.Text.Length, 0);
            this.ProgressTextBox.ScrollToCaret();
        }
    }
}

public class MyStringWriter : System.IO.StringWriter
{
    // Define the event.
    public event EventHandler StringWritten;

    public MyStringWriter()
        : base()
    {
    }

    public MyStringWriter(StringBuilder sb)
        : base(sb)
    {
    }

    public MyStringWriter(StringBuilder sb, IFormatProvider formatProvider)
        : base(sb, formatProvider)
    {
    }

    public MyStringWriter(IFormatProvider formatProvider)
        : base(formatProvider)
    {
    }

    protected virtual void OnStringWritten()
    {
        if (StringWritten != null)
        {
            StringWritten(this, EventArgs.Empty);
        }
    }

    public override void Write(char value)
    {
        base.Write(value);
        this.OnStringWritten();
    }

    public override void Write(char[] buffer, int index, int count)
    {
        base.Write(buffer, index, count);
        this.OnStringWritten();
    }

    public override void Write(string value)
    {
        base.Write(value);
        this.OnStringWritten();
    }
}

I'm having problems with the redirected output from a console application being presented in a Winforms textbox in real-time. The messages are being produced line by line however as soon as interaction with the Form is called for, nothing appears to be displayed.

Following the many examples on both Stackoverflow and other forums, I can't seem to get the redirected output from the process to display in the textbox on the form until the process completes.

By adding debug lines to the 'stringWriter_StringWritten' method and writing the redirected messages to the debug window I can see the messages arriving during the running of the process but these messages will not appear on the form's textbox until the process completes.

Grateful for any advice on this.

Here's an extract of the code

public partial class RunExternalProcess : Form
{
    private static int numberOutputLines = 0;
    private static MyStringWriter stringWriter;

    public RunExternalProcess()
    {
        InitializeComponent();

        // Create the output message writter
        RunExternalProcess.stringWriter = new MyStringWriter();
        stringWriter.StringWritten += new EventHandler(stringWriter_StringWritten);

        System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();

        startInfo.FileName = "myCommandLineApp.exe";

        startInfo.UseShellExecute = false;
        startInfo.RedirectStandardOutput = true;
        startInfo.CreateNoWindow = true;
        startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;

        using (var pProcess = new System.Diagnostics.Process())
        {
            pProcess.StartInfo = startInfo;
            pProcess.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(RunExternalProcess.Process_OutputDataReceived);
            pProcess.EnableRaisingEvents = true;

            try
            {
                pProcess.Start();

                pProcess.BeginOutputReadLine();
                pProcess.BeginErrorReadLine();

                pProcess.WaitForExit();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
            finally
            {
                pProcess.OutputDataReceived -= new System.Diagnostics.DataReceivedEventHandler(RunExternalProcess.Process_OutputDataReceived);
            }
        }
    }

    private static void Process_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
    {
        if (!string.IsNullOrEmpty(e.Data))
        {
            RunExternalProcess.OutputMessage(e.Data);
        }
    }

    private static void OutputMessage(string message)
    {
        RunExternalProcess.stringWriter.WriteLine("[" + RunExternalProcess.numberOutputLines++.ToString() + "] - " + message);
    }

    private void stringWriter_StringWritten(object sender, EventArgs e)
    {
        System.Diagnostics.Debug.WriteLine(((MyStringWriter)sender).GetStringBuilder().ToString());

        SetProgressTextBox(((MyStringWriter)sender).GetStringBuilder().ToString());
    }

    private delegate void SetProgressTextBoxCallback(string text);

    private void SetProgressTextBox(string text)
    {
        if (this.ProgressTextBox.InvokeRequired)
        {
            SetProgressTextBoxCallback callback = new SetProgressTextBoxCallback(SetProgressTextBox);
            this.BeginInvoke(callback, new object[] { text });
        }
        else
        {
            this.ProgressTextBox.Text = text;
            this.ProgressTextBox.Select(this.ProgressTextBox.Text.Length, 0);
            this.ProgressTextBox.ScrollToCaret();
        }
    }
}

public class MyStringWriter : System.IO.StringWriter
{
    // Define the event.
    public event EventHandler StringWritten;

    public MyStringWriter()
        : base()
    {
    }

    public MyStringWriter(StringBuilder sb)
        : base(sb)
    {
    }

    public MyStringWriter(StringBuilder sb, IFormatProvider formatProvider)
        : base(sb, formatProvider)
    {
    }

    public MyStringWriter(IFormatProvider formatProvider)
        : base(formatProvider)
    {
    }

    protected virtual void OnStringWritten()
    {
        if (StringWritten != null)
        {
            StringWritten(this, EventArgs.Empty);
        }
    }

    public override void Write(char value)
    {
        base.Write(value);
        this.OnStringWritten();
    }

    public override void Write(char[] buffer, int index, int count)
    {
        base.Write(buffer, index, count);
        this.OnStringWritten();
    }

    public override void Write(string value)
    {
        base.Write(value);
        this.OnStringWritten();
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

他不在意 2024-09-07 07:44:58
   pProcess.WaitForExit();

你正在挂起 UI 线程。在进程退出之前,它不会开始执行其正常职责,例如绘制文本框。同样,BeginInvoke() 委托目标在 UI 线程再次空闲之前无法运行。它们将堆积在调用队列中,在极端情况下,您将耗尽内存。即使进程退出后,UI 线程在清空队列时也会有一段时间没有响应。

你在这里不需要它。如有必要,请改用 Process.Exited 事件。

   pProcess.WaitForExit();

You are hanging the UI thread with that. It won't get around to doing its normal duties, like paint the text box, until the process has exited. Similarly, the BeginInvoke() delegate target cannot run until the UI thread goes idle again. They'll stack up in the invoke queue, in extreme cases you'll run out of memory. Even after the process exits, the UI thread will be unresponsive for some time while it works on emptying the queue.

You don't need it here. If necessary, use the Process.Exited event instead.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文