15

At the moment I am starting a batch file from my C# program with:

System.Diagnostics.Process.Start(@"DoSomeStuff.bat");

What I would like to be able to do is redirect the output (stdout and stderr) of that child process to the Output window in Visual Studio (specifically Visual C# Express 2008).

Is there a way to do that?

(Additionally: such that it's not all buffered up and then spat out to the Output window when the child process finishes.)


(BTW: At the moment I can get stdout (but not stderr) of the parent process to appear in the Output window, by making my program a "Windows Application" instead of a "Console Application". This breaks if the program is run outside Visual Studio, but this is ok in my particular case.)

Andrew Russell
  • 26,924
  • 7
  • 58
  • 104
  • All the pieces are here. Redirect output for the process, use Trace to get it in the Output window. – Hans Passant Sep 04 '10 at 14:27
  • Have you figured out how to redirect the output of a child process? The answers given below could redirect the output of a parent process but not a child one. – Fiona Jan 31 '17 at 23:56

4 Answers4

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();

Same idea for Error, just replace Output in those method/property names.

Dai
  • 141,631
  • 28
  • 261
  • 374
Mark H
  • 13,797
  • 4
  • 31
  • 45
  • 7
    This answer is mostly right. It's missing some stuff. In particular: `Start` call before `Begin...ReadLine` and `WaitForExit` afterwards. Also setting `RedirectStandardInput` to true fixed a problem caused by one of the programs (specifically plink) in the batch file wanting a valid stdin, even though it didn't use it. – Andrew Russell Sep 04 '10 at 14:29
  • 1
    `WaitForExit()` can cause an infinite wait. Always call the method with a timeout: `process.WaitForExit(10000)` => waits 10 seconds. – Sachin Joseph Sep 04 '17 at 08:44
8

A variation of this works for me --posting this now because I wish I'd found it earlier. Note that this is just a fragment extracted from the real code so there may be trivial errors.

The technique is based on some MSDN code. What I haven't been able to figure out is how to get the output window to update "on the fly". It doesn't update until after this task returns.

// 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();
}
UweBaemayr
  • 1,861
  • 1
  • 18
  • 21
  • @paulm what do you mean by it won't get the right order? This code isn't working for me in redirecting the stderr (stdout is working). Any idea why? – rboy Jan 15 '15 at 00:16
  • Use the answer from my question - this won't get stderr and stdout in the right ordering, i.e if the app does out,err,out,err you might get out,out,err,err etc – paulm Jan 17 '15 at 12:10
2

What's going on here is that Visual Studio is displaying the debug output from the program in the Output Window. That is: if you use Trace.WriteLine, it'll appear in the Output Window, because of the default trace listener.

Somehow, your Windows Form application (when it uses Console.WriteLine; I'm assuming you're using Console.WriteLine) is also writing debug output, and Visual Studio is picking this up.

It won't do the same for child processes, unless you explicitly capture the output and redirect it along with your output.

Roger Lipscombe
  • 89,048
  • 55
  • 235
  • 380
  • That's basically what I'm trying to ask - how do you do the last bit? (And, I realised I was missing this from my question: how to do it "as it happens"?) – Andrew Russell Sep 04 '10 at 13:40
-5

Have you considered using a DefaultTraceListener ?

    //Create and add a new default trace listener.
    DefaultTraceListener defaultListener;
    defaultListener = new DefaultTraceListener();
    Trace.Listeners.Add(defaultListener);
Igby Largeman
  • 16,495
  • 3
  • 60
  • 86
GenEric35
  • 7,083
  • 3
  • 24
  • 25
  • 3
    This doesn't even *remotely* answer my question. And from the linked MSDN page: "An instance of this class is automatically added to the Debug.Listeners and Trace.Listeners collections. Explicitly adding a second DefaultTraceListener causes duplicate messages in the debugger output window and duplicate message boxes for asserts.", making your answer worse than totally useless. – Andrew Russell Sep 04 '10 at 12:53