将子进程的输出(stdout、stderr)重定向到Visual Studio的输出窗口

15

目前我正在使用以下代码从C#程序启动批处理文件:

System.Diagnostics.Process.Start(@"DoSomeStuff.bat");
我希望能够将子进程输出(stdout和stderr)重定向到Visual Studio的输出窗口(特别是Visual C# Express 2008)。有没有办法实现这一点? (另外:使其不会在子进程完成时将所有内容都缓冲起来然后一次性输出到输出窗口。)
(顺便说一下:目前,我可以让“父”进程的stdout(但不是stderr)出现在输出窗口中,方法是将我的程序设置为“Windows应用程序”而不是“控制台应用程序”。如果在Visual Studio之外运行程序则无效,但在我的特定情况下这是可以接受的。)

所有的模块都在这里。重定向进程的输出,使用跟踪工具将其输出到输出窗口。 - Hans Passant
你解决了如何重定向子进程的输出吗?下面给出的答案可以重定向父进程的输出,但不能重定向子进程的输出。 - Fiona
4个回答

26
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += (sender, args) => Console.WriteLine(args.Data);
process.Start();
process.BeginOutputReadLine();

process.WaitForExit();

对于Error,使用相同的方法,只需将这些方法/属性名称中的Output替换即可。


7
这个答案大部分是正确的,但还有一些遗漏的地方。特别是:在 Begin...ReadLine 之前需要先调用 Start,并在之后加上 WaitForExit。另外将 RedirectStandardInput 设置为 true 可以解决一个由于批处理文件中某个程序(具体来说是 plink)需要有效 stdin 而导致的问题,即使它没有使用 stdin。 - Andrew Russell
1
WaitForExit() 可能会导致无限等待。始终使用超时调用该方法:process.WaitForExit(10000) => 等待10秒钟。 - Sachin Joseph

8
这个变化对我有效 - 我现在发布它是因为我希望早点找到它。请注意,这只是从实际代码中提取的片段,因此可能存在微不足道的错误。
该技术基于一些MSDN代码。我还没有弄清楚如何使输出窗口“即时”更新。它只会在此任务返回之后更新。
// Set this to your output window Pane
private EnvDTE.OutputWindowPane _OutputPane = null;

// Methods to receive standard output and standard error

private static void StandardOutputReceiver(object sendingProcess, DataReceivedEventArgs outLine)
{
   // Receives the child process' standard output
   if (! string.IsNullOrEmpty(outLine.Data)) {
       if (_OutputPane != null)
           _OutputPane.Write(outLine.Data + Environment.NewLine);
   }
}

private static void StandardErrorReceiver(object sendingProcess, DataReceivedEventArgs errLine)
{
   // Receives the child process' standard error
   if (! string.IsNullOrEmpty(errLine.Data)) {
       if (_OutputPane != null)
           _OutputPane.Write("Error> " + errLine.Data + Environment.NewLine);
   }
}

// main code fragment
{
    // Start the new process
    ProcessStartInfo startInfo = new ProcessStartInfo(PROGRAM.EXE);
    startInfo.Arguments = COMMANDLINE;
    startInfo.WorkingDirectory = srcDir;
    startInfo.UseShellExecute = false;
    startInfo.RedirectStandardOutput = true;
    startInfo.RedirectStandardError = true;
    startInfo.CreateNoWindow = true;
    Process p = Process.Start(startInfo);
    p.OutputDataReceived += new DataReceivedEventHandler(StandardOutputReceiver);
    p.BeginOutputReadLine();
    p.ErrorDataReceived += new DataReceivedEventHandler(StandardErrorReceiver);
    p.BeginErrorReadLine();
    bool completed = p.WaitForExit(20000);
    if (!completed)
    {
        // do something here if it didn't finish in 20 seconds
    }
    p.Close();
}

@paulm,你说的“不会得到正确的顺序”是什么意思?这段代码在重定向stderr时对我无效(stdout有效)。有任何想法为什么吗? - rboy
使用我的问题的答案 - 这样不会按正确顺序获取stderr和stdout,即如果应用程序执行out、err、out、err,则可能会得到out、out、err、err等。 - paulm

2
这里发生的情况是Visual Studio在输出窗口中显示程序的调试输出。也就是说,如果您使用Trace.WriteLine,它会出现在输出窗口中,因为默认的跟踪侦听器。一些方式下,您的Windows窗体应用程序(当它使用Console.WriteLine时;我假设您正在使用Console.WriteLine)也会写入调试输出,而Visual Studio会捕捉到它。除非您明确地捕获输出并将其重定向到您的输出中,否则它不会对子进程执行相同的操作。

这基本上就是我想问的 - 你如何完成最后一步?(而且,我意识到我在问题中漏掉了这个:如何“随时发生”?) - Andrew Russell

-5

您是否考虑使用DefaultTraceListener

    //Create and add a new default trace listener.
    DefaultTraceListener defaultListener;
    defaultListener = new DefaultTraceListener();
    Trace.Listeners.Add(defaultListener);

3
这根本没有回答我的问题。而且从链接的 MSDN 页面可以看到:“此类的实例会自动添加到 Debug.Listeners 和 Trace.Listeners 集合中。明确添加第二个 DefaultTraceListener 会导致调试器输出窗口中出现重复的消息和断言的重复消息框”,这使得你的答案比完全没有用还要糟糕。 - Andrew Russell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接