Как читать, чтобы завершить вывод процесса асинхронно в C #? - PullRequest
7 голосов
/ 02 марта 2012

У меня проблема с чтением вывода одного процесса асинхронно в C #.Я нашел несколько других похожих вопросов на этом сайте, но они мне не очень помогли.Вот что я делаю:

  1. Создание нового процесса
  2. Установка startinfo -FileName, Аргументы, CreateNoWindow (true), UseShellExecute (false), RedirectStandardOutput (true)
  3. Добавьте обработчик события в OutputDataReceived;
  4. Запустите процесс, BeginOutputReadLine и затем WaitForExit ().

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

Пример:

%0,%1...%100
Finished.

Мой вывод:

%0
Finished. 

Вот текущий код моей программы:

StringBuilder sBuilder = new StringBuilder();
static void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
    sBuilder.AppendLine(e.Data);
}

static void CommandExecutor()
{
    Process process = new Process
    {
        StartInfo = new ProcessStartInfo
        {
            FileName = /*path of the program*/,
            Arguments = /*arguments*/,
            CreateNoWindow = true,
            UseShellExecute = false,
            WindowStyle = ProcessWindowStyle.Hidden,
            RedirectStandardOutput = true
        }
    };

    process.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);

    process.Start();

    process.BeginOutputReadLine();

    process.WaitForExit();
}

Ответы [ 4 ]

7 голосов
/ 11 июля 2013

Кажется, что чтение потока вывода асинхронно немного нарушено - не все данные читаются до завершения процесса. Даже если вы позвоните Process.WaitForExit(), и даже если вы тогда позвоните Process.Close() (или Dispose()), вы все равно сможете получить много данных впоследствии. См. http://alabaxblog.info/2013/06/redirectstandardoutput-beginoutputreadline-pattern-broken/ для полной записи, но решение в основном заключается в использовании синхронных методов. Однако, чтобы избежать тупиков, вам нужно вызвать одного из них в другом потоке:

using (var process = Process.Start(processInfo))
{
    // Read stderr synchronously (on another thread)

    string errorText = null;
    var stderrThread = new Thread(() => { errorText = process.StandardError.ReadToEnd(); });
    stderrThread.Start();

    // Read stdout synchronously (on this thread)

    while (true)
    {
        var line = process.StandardOutput.ReadLine();
        if (line == null)
            break;

        // ... Do something with the line here ...
    }

    process.WaitForExit();
    stderrThread.Join();

    // ... Here you can do something with errorText ...
}
6 голосов
/ 10 сентября 2014

Process.WaitForExit () будет ожидать окончания чтения асинхронного вывода / потока ошибок. К сожалению, это не относится к перегрузке Process.WaitForExit (timeout). Вот что делает класс Process внутренне:

// ...

finally
{
    if (processWaitHandle != null)
    {
        processWaitHandle.Close();
    }
    if (this.output != null && milliseconds == -1)
    {
        this.output.WaitUtilEOF();
    }
    if (this.error != null && milliseconds == -1)
    {
        this.error.WaitUtilEOF();
    }
    this.ReleaseProcessHandle(safeProcessHandle);
}

... Так что он будет ждать асинхронных чтений, только если не было тайм-аута! Чтобы исправить это, просто вызовите без параметров WaitForExit () после того, как WaitForExit (timeout) вернул true:

// ...

if (process.WaitForExit( 10 * 1000 ) && process.WaitForExit() )
{
 // Process'es OutputDataReceived / ErrorDataReceived callbacks will not be called again, EOF streams reached
}
else
{
   throw new Exception("timeout");
}

Подробнее читайте здесь: http://msdn.microsoft.com/en-us/library/ty0d8k56%28v=vs.110%29

1 голос
/ 02 марта 2012

Есть несколько вещей, которые мешают этому ... Консольное приложение, вероятно, использует клавишу "\ b" для перезаписи процента, возможно, оно не сбрасывается в поток stdout после каждой записи, и предположительно BeginOutputReadLineждет окончания строки перед тем, как предоставить вам данные.

Посмотрите, как вы справляетесь с процессом чтения.StandardOutput.BaseStream через BeginRead (этот код не является асинхронным, и "\ b" нужно будет обрабатывать иначеесли вы ставите прогресс в форме):

        while (true)
        {
            byte[] buffer = new byte[256];
            var ar = myProcess.StandardOutput.BaseStream.BeginRead(buffer, 0, 256, null, null);
            ar.AsyncWaitHandle.WaitOne();
            var bytesRead = myProcess.StandardOutput.BaseStream.EndRead(ar);
            if (bytesRead > 0)
            {
                Console.Write(Encoding.ASCII.GetString(buffer, 0, bytesRead));
            }
            else
            {
                myProcess.WaitForExit();
                break;
            }
        }
0 голосов
/ 02 марта 2012

А как насчет использования StreamReader для process.StandardOutput и метода Read ()?http://msdn.microsoft.com/fr-fr/library/system.io.streamreader.read(v=vs.80).aspx

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