我正在使用Process类从.NET程序运行cmd.exe并传入各种命令来执行程序,例如ipconfig,netstat等。问题是有时这些程序输出Error文本,它被发送到StandardError,但是没有办法知道在标准输出期间究竟在哪里确实打印了错误。我得到的只是错误信息。是否可以将StandardOutput和StandardError组合成一个同步流,以便按顺序获取所有数据?如果没有,那么如何组织来自两个不同流的输出以使其按正确顺序排列?
答案 0 :(得分:2)
好。我猜你真的不能指望STDOUT和STDERR按照它们输出的确切顺序排列。这是因为在系统级别,STDOUT和STDERR分别有自己的缓冲区。你可以想象这样的情况 -
(1)当'command'首先写入STDOUT时,缓冲区可能不会立即刷新。
(2)然后'command'写入STDERR,但是这次STDERR的缓冲区可能会被刷新。
在这种情况下,即使(1)首先出现,但我们最终获得的是(2)首先出现。
希望以下代码段可以提供帮助 -
(改进代码)
class OutputContext
{
public const int BufferSize = 1024;
public bool IsEof { get; set; }
public bool IsWaiting { get; set; }
public byte[] Buffer { get; set; }
public StreamReader Reader { get; set; }
public object Tag { get; set; }
public OutputContext(StreamReader r, object tag)
{
IsEof = false;
IsWaiting = false;
Buffer = new byte[BufferSize];
Reader = r;
Tag = tag;
}
}
Process proc;
void Callback(IAsyncResult ar)
{
lock (ar.AsyncState)
{
OutputContext ctx = ar.AsyncState as OutputContext;
int c = ctx.Reader.BaseStream.EndRead(ar);
ctx.IsWaiting = false;
if (c == 0)
{
ctx.IsEof = true;
return;
}
string content = Encoding.UTF8.GetString(ctx.Buffer, 0, c);
Console.Write(content);
Console.Out.Flush();
}
}
void RedirectOutput(OutputContext ctx)
{
lock (ctx)
{
if (ctx.IsEof)
{
return;
}
if (ctx.IsWaiting)
{
return;
}
ctx.IsWaiting = true;
IAsyncResult ar = ctx.Reader.BaseStream.BeginRead(ctx.Buffer, 0,
OutputContext.BufferSize, Callback, ctx);
}
}
void Run(string yourprog, string yourargs)
{
// If this is a GUI app, this shall not be run on the UI thread.
// Spin a new thread to handle it and wait for the thread to complete.
// And you can always accept the input as long as the UI thread is not
// blocked, and redirect the input to the target proc's stdin asycly.
proc = new Process();
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.FileName = yourprog;
proc.StartInfo.Arguments = yourargs;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardInput = true;
proc.Start();
OutputContext stdoutCtx = new OutputContext(proc.StandardOutput, "STDOUT");
OutputContext stderrCtx = new OutputContext(proc.StandardError, "STDERR");
while (!stdoutCtx.IsEof && !stderrCtx.IsEof)
{
RedirectOutput(stdoutCtx);
RedirectOutput(stderrCtx);
}
proc.WaitForExit();
}
}