Как асинхронно читать стандартный поток вывода и стандартный поток ошибок сразу
Я хочу прочитать вывод процесса в форме, как есть в консоли (стандартный вывод смешивается со стандартной ошибкой в одном потоке). Есть ли способ, как это сделать?
Я думал об использовании
ProcessStartInfo.UseShellExecute = true;
но затем я не могу асинхронно читать вывод. Если я установил
process.ProcessStartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += new DataReceivedEventHandler(partialOutputHandler);
тогда я могу читать стандартный вывод (я могу сделать то же самое для стандартной ошибки), но я не знаю, как имитировать поведение консоли (смешение stdout и stderr).
Примечание. Я знаю, что Linux имеет функцию перенаправления стандартного потока ошибок в стандартный поток вывода, но мне не удалось выполнить его для .NET.
Мне нужно пропустить что-то очень легкое.
Спасибо!
Ответы
Ответ 1
Я нашел ответ:
Выходные потоки буферизуются. Невозможно получить истинный последовательный порядок элементов, вставленных в потоки. На самом деле это мало смысла, поскольку оба потока могут быть написаны тоже при одном и том же время. Они независимы друг от друга. Поэтому лучшее, что вы можете do - получить последовательный вывод от каждого из них по мере их поступления.
Как правило, это не проблема, поскольку почти все консольные приложения используют стандартный вывод для вывода и сообщений об ошибках. Поток ошибок используется некоторыми приложениями, но сообщения, как правило, являются дубликатами ошибки, генерируемые в выходном потоке.
Источник: http://social.msdn.microsoft.com/Forums/uk/csharpgeneral/thread/192b6df7-9437-42cf-81c1-c125021735ba
Ответ 2
Ты имеешь в виду что-то подобное?
SynchronizationContext _syncContext;
MyForm()
{
_syncContext = SynchronizationContext.Current;
}
void StartProcess()
{
using (var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "myProcess.exe",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
}
})
{
process.OutputDataReceived += (sender, args) => Display(args.Data);
process.ErrorDataReceived += (sender, args) => Display(args.Data);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit(); //you need this in order to flush the output buffer
}
}
void Display(string output)
{
_syncContext.Post(_ => myTextBox.AppendText(output), null);
}
Ответ 3
В статье MSDN говорится:
Перенаправленный поток StandardError можно читать синхронно или асинхронно. Такие методы, как Read, ReadLine и ReadToEnd, выполняют синхронных операций чтения в потоке вывода ошибок процесса. Эти операции синхронного чтения не выполняются до тех пор, пока Процесс записывает в поток StandardError или закрывает поток.
Напротив, BeginErrorReadLine запускает асинхронные операции чтения на поток StandardError. Этот метод позволяет назначенное событие обработчик для вывода потока и немедленно возвращается к вызывающему, который может выполнять другую работу, в то время как поток обработчик событий.
Операции синхронного чтения вводят зависимость между вызывающим абонентом чтение из потока StandardError и запись дочернего процесса в этот поток. Эти зависимости могут привести к условиям взаимоблокировки. Когда вызывающий абонент читает из перенаправленного потока дочернего процесса, это зависит от ребенка. Вызывающий абонент ждет операции чтения пока ребенок не напишет в поток или не закрывает поток. Когда дочерний процесс записывает достаточно данных для заполнения перенаправленного потока, это в зависимости от родителя. Детский процесс ждет следующей записи пока родитель не прочитает полный поток или не закрывает поток. Условие взаимоблокировки возникает, когда вызывающий и дочерний процесс ждет друг друга, чтобы завершить операцию, и ни одна из них не может продолжить. Вы можете избежать взаимоблокировок, оценивая зависимости между вызывающий и дочерний процесс.
То же самое относится к StandardOutput
, поэтому вы просто читаете оба потока асинхронно.
Merging
оба потока в один затрудняют обнаружение того, какой вывод является сообщением об ошибках, и какова информация о продукте.
Ответ 4
Пример такого же типа, за исключением того, что я собираю stdout и error в отдельные строки, используя для этого StringBuilder.
/// <summary>
/// Executes command
/// </summary>
/// <param name="cmd">command to be executed</param>
/// <param name="output">output which application produced</param>
/// <param name="transferEnvVars">true - if retain PATH environment variable from executed command</param>
/// <returns>true if process exited with code 0</returns>
static bool ExecCmd(string cmd, out String output, bool transferEnvVars = false)
{
ProcessStartInfo processInfo;
Process process;
if (transferEnvVars)
cmd = cmd + " && echo --VARS-- && set";
processInfo = new ProcessStartInfo("cmd.exe", "/c " + cmd);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
process = Process.Start(processInfo);
// Executing long lasting operation in batch file will hang the process, as it will wait standard output / error pipes to be processed.
// We process these pipes here asynchronously.
StringBuilder so = new StringBuilder();
process.OutputDataReceived += (sender, args) => { so.AppendLine(args.Data); };
StringBuilder se = new StringBuilder();
process.ErrorDataReceived += (sender, args) => { se.AppendLine(args.Data); };
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
output = so.ToString();
String error = se.ToString();
if (transferEnvVars)
{
Regex r = new Regex("--VARS--(.*)", RegexOptions.Singleline);
var m = r.Match(output);
if (m.Success)
{
output = r.Replace(output, "");
foreach ( Match m2 in new Regex("(.*?)=([^\r]*)", RegexOptions.Multiline).Matches(m.Groups[1].ToString()) )
{
String key = m2.Groups[1].Value;
String value = m2.Groups[2].Value;
Environment.SetEnvironmentVariable(key, value);
}
}
}
if(error.Length != 0)
output += error;
int exitCode = process.ExitCode;
if (exitCode != 0)
Console.WriteLine("Error: " + output + "\r\n" + error);
process.Close();
return exitCode == 0;
}