Событие pConsole.StartInfo.RedirectStandardOutput и pConsole.Exited (c #) - PullRequest
3 голосов
/ 03 ноября 2008

У меня есть приложение с графическим интерфейсом, которое выполняет (в новом процессе) «консольные» приложения и анализирует вывод. Чтобы перенаправить вывод, я установил для pConsole.StartInfo.RedirectStandardOutput значение true. Я тоже подписываюсь на событие pConsole.Exited.

Проблема, которую я вижу, заключается в том, что мне нужно использовать Thread.Sleep () в обработчике события Exited для получения последних данных.

Мой обработчик события Exited выглядит так:

Thread.Sleep(100); // Wait for additional data (if any).
pConsole.OutputDataReceived -= new System.Diagnostics.DataReceivedEventHandler(this.localTerminal_DataAvailableEvent);
int exit = pConsole.ExitCode;
pConsole.Dispose();
pConsole = null;

Кажется, что событие Exited выполняется перед моим последним pConsole_DataAvailableEvent. Кто-нибудь знает, как / почему это происходит?

Я также использую мьютекс / блокировку, чтобы удостовериться, что событие Exited завершено, прежде чем я начну выполнять мое следующее консольное приложение.

Ответы [ 4 ]

3 голосов
/ 03 ноября 2008

Проблема почти наверняка заключается в буферизации вывода: процесс завершается, вызывая событие Exited, но некоторые выходные данные все еще находятся в буфере. Ваш взлом, вероятно, будет работать в некоторых случаях, но другие подходы могут быть более надежными. Рассмотрим:

1) Устранение обработчика события Exited и вместо этого проверка Process.HasExited в обработчике OutputDataReceived.

2) Не используйте обработчик OutputDataReceived, а просто вызовите метод Read () в потоке Process.StandardOutput. Выполните очистку после обработки, как только поток закроется.

2 голосов
/ 03 ноября 2008

Я не знаю, лучше ли это, но я только что посмотрел на что-то похожее, используя потоки для чтения обоих stderr / stdout, как показано ниже. Он включает несколько дополнительных потоков (чтобы избежать взаимоблокировок / сложного асинхронного кода), но, похоже, работает довольно надежно.

Ключевым моментом здесь является то, что I Join() в двух потоках, обрабатывающих ввод-вывод, поэтому я продолжаю работать только после того, как оба выходных потока будут полностью использованы.

            using (Process proc = Process.Start(psi))
            {
                Thread stdErr = new Thread(DumpStream(proc.StandardError, Console.Error));
                Thread stdOut = new Thread(DumpStream(proc.StandardOutput, Console.Out));
                stdErr.Name = "stderr reader";
                stdOut.Name = "stdout reader";
                stdErr.Start();
                stdOut.Start();
                proc.WaitForExit();
                stdOut.Join();
                stdErr.Join();
                if (proc.ExitCode != 0) {...} // etc
            }

    static ThreadStart DumpStream(TextReader reader, TextWriter writer)
    {
        return (ThreadStart) delegate
         {
             string line;
             while ((line = reader.ReadLine()) != null) writer.WriteLine(line);
         };
    }
1 голос
/ 03 ноября 2008

Я сильно подозреваю, что это просто операционная система, сбрасывающая любые выходные буферы. Похоже, что ваш обходной путь в порядке, хотя, очевидно, он уродлив (не ваша вина), а продолжительность сна может быть расточительно длинной в некоторых случаях и недостаточно длинной в некоторых других патологических случаях.

0 голосов
/ 04 января 2011

В дополнение к ответу Марка Гравелла

proc.StandardError, proc.StandardOutput оба имеют метод EndOfStream. Это будет полезно для определения случая, когда выходные данные не приводят к новой строке до того, как пользователь вводит / подсказывает

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...