Как перенаправить вывод консольной программы в текстовое поле потокобезопасным способом? - PullRequest
6 голосов
/ 15 декабря 2010

У меня проблемы с перенаправлением вывода консоли в текстовое поле Windows Form.Проблема связана с темой.Я запускаю консольное приложение следующим образом,

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.

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


Решение (как указал Ганс Пассант)

Проблема в том, что приложение застряло в"смертельное объятие" в результате строки,

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();
}

Ответы [ 2 ]

9 голосов
/ 15 декабря 2010
proc.WaitForExit();

Это называется тупиком.Ваш главный поток заблокирован, ожидая завершения процесса.Это мешает ему выполнять основные обязанности.Например, обновлять интерфейс.И убедитесь, что запросы Control.Invoke () отправлены.Это останавливает метод AppendText () от завершения.Который останавливает процесс выхода.Который останавливает ваш поток пользовательского интерфейса от прохождения вызова WaitForExit ()."Смертельные объятия", он же тупик.

Вы не можете заблокировать свой главный поток.Вместо этого используйте событие Process.Exited.

0 голосов
/ 15 декабря 2010

попробуй

out_txtbx.Invoke(new AppendTextDelegate(this.AppendText), text);
...