C# 如何重定向控制台程序';以线程安全的方式输出到文本框?

C# 如何重定向控制台程序';以线程安全的方式输出到文本框?,c#,multithreading,winforms,textbox,console-application,C#,Multithreading,Winforms,Textbox,Console Application,我无法将控制台输出重定向到windows窗体文本框。这个问题与线程有关。我正在以以下方式运行控制台应用程序 private void RunConsoleApp() { Process proc = new Process(); proc.StartInfo.FileName = "app.exe"; proc.StartInfo.Arguments = "-a -b -c"; proc.StartInfo.UseShellExecute = false;

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

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);
    }
}
然后使用此输出处理程序捕获并处理数据

// 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);
        }
    }
}
然后,控制台文本如下所示追加

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);
    }
}
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();
}
从我看到的所有文档和示例来看,这似乎是正确的方法,只是调用out_txtbx.Invoke时它会使应用程序崩溃

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


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

问题是,由于这条线路,应用程序陷入了“致命的拥抱”状态

proc.WaitForExit();
应该删除该行,方法应该如下所示

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);
    }
}
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();
}
并且应该提供一个事件处理程序

/// <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();
}
//
///控制台进程完成时要采取的操作
/// 
私有void进程退出(对象发送方,System.EventArgs e)
{
进程进程=(进程)发送方;
//请稍等片刻,以允许处理和附加所有控制台输出
//在附加成功/失败消息之前。
睡眠(40);
if(proc.ExitCode==0)
{
这个.AppendText(“Success.+Environment.NewLine”);
ExitBootloader();
}
其他的
{
这个.AppendText(“Failed.+Environment.NewLine”);
}
过程关闭();
}
试试看

这叫做死锁。主线程被阻塞,正在等待进程退出。这使它无法履行基本职责。比如保持用户界面的更新。并确保调度Control.Invoke()请求。这将阻止AppendText()方法完成。这将停止退出的过程。这会阻止UI线程通过WaitForExit()调用。“致命的拥抱”,又名死锁


不能阻止主线程。改用Process.Exited事件。

我这样做没有错误。如果在我拿到代码样本的时候还没有一个足够的答案,我会发布。请注意,这将是今晚晚些时候我回家的时候。我创建了一个TextWriter对象,该对象写入文本框,然后将Console.SetOut指向TextWriter。请参阅@decyclone:他说它在调用out_txtbx.Invoke的那一行。@SLaks没有错误,当我运行它或尝试跨入/越过out_txtbx.Invoke行时,应用程序只是挂起。Visual C#在调试时似乎不提供暂停(仅在断点上暂停),因此我不知道调用之后会发生什么。主Winforms线程是否正在忙于执行其他操作?单击调试工具栏上的暂停按钮,或在VS中按Ctrl+pause。