Как перенаправить вывод консольной программы в текстовое поле потокобезопасным способом?

У меня проблемы с перенаправлением вывода консоли в текстовое поле форм 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);
    }
}

Из всей документации и примеров, которые я видел это кажется правильным методом, за исключением того, что он приводит к сбою приложения при вызове out_txtbx.Invoke.

Что может быть нарушено и какие существуют альтернативные способы сделать это?


Решение (как указано by Hans Passant)

Проблема в том, что приложение застряло в «смертельных объятиях» из-за строки,

proc.WaitForExit();

Эта строка должна быть удалена, и метод должен выглядеть так,

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();
}
6
задан default locale 30 July 2014 в 08:00
поделиться