我想在控制台中读取表单中的进程输出(标准输出与一个流中的标准错误混合)。有办法怎么做?
我在考虑使用
ProcessStartInfo.UseShellExecute = true;
然后我无法异步读取输出。如果我设置
process.ProcessStartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += new DataReceivedEventHandler(partialOutputHandler);
然后我可以读取标准输出(我可以对标准错误做同样的事情)但我不知道如何模拟控制台的行为(混合stdout和stderr)。
注意:我知道Linux具有将标准错误流重定向到标准输出流的功能,但我无法将其谷歌用于.NET。
我必须错过一些非常简单的事情。
谢谢!
答案 0 :(得分:39)
你的意思是这样吗?
SynchronizationContext _syncContext;
MyForm()
{
_syncContext = SynchronizationContext.Current;
}
void StartProcess()
{
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);
}
答案 1 :(得分:3)
我找到了答案:
输出流被缓冲。没有办法得到真实的 插入到流中的项的顺序。实际上它 没有意义,因为两个流也可以同时写入 时间。它们彼此独立。因此,你可以做到最好 do是在每个到达时得到每个的顺序输出。
通常这不是问题,因为几乎所有的控制台应用程序都使用 输出和错误消息的标准输出。错误流 某些应用程序使用它,但消息通常是重复的 输出流中生成的错误。
来源: http://social.msdn.microsoft.com/Forums/uk/csharpgeneral/thread/192b6df7-9437-42cf-81c1-c125021735ba
答案 2 :(得分:2)
MSDN article声明:
可以同步读取重定向的StandardError流 异步。执行Read,ReadLine和ReadToEnd等方法 对进程的错误输出流进行同步读取操作。 这些同步读取操作在关联之前不会完成 进程写入其StandardError流,或关闭流。
相反,BeginErrorReadLine启动异步读取操作 StandardError流。此方法启用指定事件 流输出的处理程序并立即返回给调用者, 它可以在流输出定向到时执行其他工作 事件处理程序。
同步读取操作在调用者之间引入依赖关系 读取StandardError流和子进程写入 那条小溪。这些依赖项可能导致死锁条件。 当调用者从子进程的重定向流中读取时, 它取决于孩子。调用者等待读操作 直到孩子写入流或关闭流。当。。。的时候 子进程写入足够的数据来填充其重定向流,它是 依赖于父母。子进程在下一次写入时等待 操作直到父进程从完整流中读取或关闭 流。当调用者和孩子出现死锁情况时 进程等待彼此完成一个操作,也都不能 继续。您可以通过评估之间的依赖关系来避免死锁 来电者和孩子的过程。
同样适用于StandardOutput
,因此您只需异步读取两个流。
Merging
两个流合并为一个变得复杂,检测错误报告的输出和“产品”信息是什么。
答案 3 :(得分:0)
类似的示例,除了我为此目的使用StringBuilder将stdout和错误收集到单独的字符串中。
/// <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;
}