如何以线程安全的方式将控制台程序的输出重定向到文本框? [英] How do I redirect a console program's output to a text box in a thread safe way?

查看:27
本文介绍了如何以线程安全的方式将控制台程序的输出重定向到文本框?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法将控制台输出重定向到 Windows 窗体文本框.问题与线程有关.我正在通过以下方式运行控制台应用程序,

I am having trouble redirecting console output to a windows forms text box. The problem is thread related. I am running a console app in the following way,

private void RunConsoleApp()
{
    Process proc = new Process();
    proc.StartInfo.FileName = "app.exe";
    proc.StartInfo.Arguments = "-a -b -c";
    proc.StartInfo.UseShellExecute = false;

    // set up output redirection
    proc.StartInfo.RedirectStandardOutput = true;
    proc.StartInfo.RedirectStandardError = true;    
    proc.EnableRaisingEvents = true;
    proc.StartInfo.CreateNoWindow = true;

    // Set the data received handlers
    proc.ErrorDataReceived += proc_DataReceived;
    proc.OutputDataReceived += proc_DataReceived;

    proc.Start();
    proc.BeginErrorReadLine();
    proc.BeginOutputReadLine();
    proc.WaitForExit();

    if (proc.ExitCode == 0)
    {
        out_txtbx.AppendText("Success." + Environment.NewLine);
    }
    else
    {
        out_txtbx.AppendText("Failed." + Environment.NewLine);
    }
}

然后使用此输出处理程序捕获和处理数据,

and then capture and process the data with this output handler,

// Handle the date received by the console process
void proc_DataReceived(object sender, DataReceivedEventArgs e)
{
    if (e.Data != null)
    {
        if ((e.Data.EndsWith("DONE.")) || (e.Data.EndsWith("FAILED.")) ||
            (e.Data.StartsWith("RESET")))
        {
            // This crashes the application, but is supposedly the correct method
            this.AppendText(e.Data + Environment.NewLine);

            // This works, but the debugger keeps warning me that the call
            // is not thread safe
            //out_txtbx.AppendText(e.Data + Environment.NewLine);
        }
    }
}

然后像这样附加控制台文本,

The console text is then appended like this,

delegate void AppendTextDelegate(string text);

// Thread-safe method of appending text to the console box
private void AppendText(string text)
{
    // Use a delegate if called from a different thread,
    // else just append the text directly
    if (this.out_txtbx.InvokeRequired)
    {
        // Application crashes when this line is executed
        out_txtbx.Invoke(new AppendTextDelegate(this.AppendText), new object[] { text });
    }
    else
    {
        this.out_txtbx.AppendText(text);
    }
}

从我看到的所有文档和示例中,这似乎是正确的方法,只是在调用 out_txtbx.Invoke 时它会使应用程序崩溃.

From all the documentation and examples I have seen this appears to be the correct method, except that it is crashing the application when out_txtbx.Invoke is called.

什么可能被破坏,有什么替代方法可以做到这一点?

What could be broken and what alternative ways are there to do this?

解决方案(正如 Hans Passant 所指出的)

Solution (as pointed out by Hans Passant)

问题是应用程序由于这条线而陷入了致命的拥抱",

The problem is that the app is stuck in a "deadly embrace" as a result of the line,

proc.WaitForExit();

该行应该被删除,方法应该是这样的,

That line should be removed and the method should look like this,

private void RunConsoleApp()
{
    Process proc = new Process();
    proc.StartInfo.FileName = "app.exe";
    proc.StartInfo.Arguments = "-a -b -c";
    proc.StartInfo.UseShellExecute = false;

    // set up output redirection
    proc.StartInfo.RedirectStandardOutput = true;
    proc.StartInfo.RedirectStandardError = true;    
    proc.EnableRaisingEvents = true;
    proc.StartInfo.CreateNoWindow = true;

    // Set the data received handlers
    proc.ErrorDataReceived += proc_DataReceived;
    proc.OutputDataReceived += proc_DataReceived;

    // Configure the process exited event
    proc.Exited += new EventHandler(ProcExited);

    proc.Start();
    proc.BeginErrorReadLine();
    proc.BeginOutputReadLine();

    // This blocks the main thread and results in "deadly embrace"
    // The Process.Exited event should be used to avoid this.
    //proc.WaitForExit();
}

并且应该提供一个事件处理程序,

and an event handler should be provided,

/// <summary>
/// Actions to take when console process completes
/// </summary>
private void ProcExited(object sender, System.EventArgs e)
{
    Process proc = (Process)sender;

    // Wait a short while to allow all console output to be processed and appended
    // before appending the success/fail message.
    Thread.Sleep(40);

    if (proc.ExitCode == 0)
    {
        this.AppendText("Success." + Environment.NewLine);
        ExitBootloader();
    }
    else
    {
        this.AppendText("Failed." + Environment.NewLine);
    }

    proc.Close();
}

推荐答案

proc.WaitForExit();

这叫做死锁.您的主线程被阻塞,等待进程退出.这使它无法履行基本职责.就像保持 UI 更新一样.并确保调度 Control.Invoke() 请求.这会阻止 AppendText() 方法完成.这会停止退出过程.这会阻止您的 UI 线程通过 WaitForExit() 调用.致命的拥抱",又名僵局.

It is called deadlock. Your main thread is blocked, waiting for the process to exit. That stops it from taking care of essential duties. Like keeping the UI updated. And making sure that Control.Invoke() requests are dispatched. That stops the AppendText() method from completing. Which stops the process for exiting. Which stops your UI thread from ever getting past the WaitForExit() call. "Deadly embrace", aka deadlock.

你不能阻塞你的主线程.请改用 Process.Exited 事件.

You cannot block your main thread. Use the Process.Exited event instead.

这篇关于如何以线程安全的方式将控制台程序的输出重定向到文本框?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆