Как читать для завершения процесса вывода асинхронно в С#?
У меня проблема с чтением вывода одного процесса асинхронно в С#.
Я нашел некоторые другие подобные вопросы на этом сайте, но они действительно не помогают мне.
Вот что я делаю:
- Сделать новый процесс
- Установить startinfo
-FileName, Аргументы, CreateNoWindow (true), UseShellExecute (false), RedirectStandardOutput (true)
- Добавить обработчик событий в OutputDataReceived;
- Запустить процесс, 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();
}
Ответы
Ответ 1
Кажется, что поток потока чтения асинхронно бит разбит - не все данные считываются до выхода процесса. Даже если вы вызываете 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 ...
}
Ответ 2
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
Ответ 3
Есть несколько вещей, которые мешают ему...
Возможно, консольное приложение использует "\ b" backspace, чтобы перезаписать процент, возможно, не сбрасывая поток stdout после каждой записи, а BeginOutputReadLine предположительно ждет конца строки, прежде чем давать вам данные.
Посмотрите, как вы справляетесь с процессом чтения. StandardOutput.BaseStream через BeginRead (этот код не является надлежащим асинхронным, и "\ b" s будут обрабатываться по-разному, если вы выполняете прогресс в форме):
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;
}
}
Ответ 4
Как насчет использования StreamReader в process.StandardOutput и использовании метода Read()?
http://msdn.microsoft.com/fr-fr/library/system.io.streamreader.read(v=vs.80).aspx