Recipe 8.14. Capturing Standard Output for a ProcessProblemYou need to be able to capture standard output for a process you are launching. SolutionUse the RedirectStandardOutput property of the Process.StartInfo class to capture the output from the process. By redirecting the standard output stream of the process, you read it when the process terminates. UseShellExecute is a property on the ProcessInfo class that tells the runtime whether or not to use the Windows shell to start the process or not. By default, it is turned on (true) and the shell runs the program, which means that the output cannot be redirected. This needs to be set to off and then the redirection can occur. The UseShellExecute property is set to false to ensure this is not started using the Windows shell for your purposes here. In this example, a Process object for cmd.exe is set up with arguments to perform a directory listing, and then the output is redirected. A logfile is created to hold the resulting output and the Process.Start method is called. // See 12.21 for more info on redirection… Process application = new Process(); // Run the command shell. application.StartInfo.FileName = @"cmd.exe"; // Get a directory listing from the current directory. application.StartInfo.Arguments = @"/Cdir " + Environment.CurrentDirectory; Console.WriteLine("Running cmd.exe with arguments: {0}", application.StartInfo.Arguments); // Redirect standard output so we can read it. application.StartInfo.RedirectStandardOutput = true; application.StartInfo.UseShellExecute = false; // Create a logfile to hold the results in the current EXE directory. using (StreamWriter logger = new StreamWriter("cmdoutput.log")) { // Start it up. application.Start(); Once the process is started, the StandardOutput stream can be accessed and a reference to it held. The code then reads in the information from the output stream while the application runs and writes it to the logfile that was set up previously. Once the application finishes, the logfile is closed. // Get stdout. StreamReader output = application.StandardOutput; // Dump the output stream while the app runs. do { using (output) { char[] info = null; while (output.Peek() >= 0) { info = new char[4096]; output.Read(info, 0, info.Length); // Write to the logger. logger.Write(info, 0, info.Length); } } } while (!application.HasExited); } // Close the process object. application.Close(); cmdoutput.log holds information similar to the following output: Volume in drive C has no label. Volume Serial Number is DDDD-FFFF Directory of C:\C#Cookbook2\Code\CSharpRecipes\bin\Debug 08/28/2005 12:25 PM <DIR> . 08/28/2005 12:25 PM <DIR> .. 08/28/2005 12:25 PM 0 cmdoutput.log 08/15/2005 09:46 PM 489,269 CSharpCookbook.zip 08/28/2005 12:24 PM 450,560 CSharpRecipes.exe 08/28/2005 12:24 PM 1,031,680 CSharpRecipes.pdb 07/22/2005 08:28 AM 5,120 CSharpRecipes.vshost.exe 04/12/2005 10:15 PM 432 CSharpRecipes.vshost.xml 05/10/2005 10:14 PM 998 CSharpRecipes.vshost.xsd 03/29/2005 10:27 AM 432 CSharpRecipes.xml 05/10/2005 10:14 PM 998 CSharpRecipes.xsd 03/29/2005 10:27 AM 155 data.txt 04/12/2005 10:15 PM 134 HT.data 12/10/2003 10:11 PM 12,288 REGEX_Test.dll 08/20/2005 09:27 PM 16,384 SampleClassLibrary.dll 08/20/2005 09:27 PM 11,776 SampleClassLibrary.pdb 08/02/2005 08:56 PM 483 se1.object 08/02/2005 08:56 PM 480 se2.object 08/02/2005 08:56 PM 767 se3.object 08/02/2005 08:56 PM 488 se4.object 08/02/2005 08:56 PM 775 se5.object 04/12/2005 10:15 PM 1,369 TEST.DATA 04/12/2005 10:14 PM 327 TestBinSerXML.txt 21 File(s) 2,024,915 bytes 2 Dir(s) 98,005,683,712 bytes free DiscussionRedirecting standard output is a common task that can sometimes be of great use for tasks like automated build scenarios or test harnesses. While not quite as easy as simply placing > after the command line for a process at the command prompt, this approach is more flexible, as the stream output can be reformatted as XML or HTML for posting to a web site. This also provides the opportunity to send the data to multiple locations at once, which the simple command-line redirect function as provided by Windows is incapable of. Waiting to read from the stream until the application has finished ensures that there will be no deadlock issues. If the stream is accessed synchronously before this time, then the possibility exists for the parent to block the child. At a minimum, the child will wait until the parent has finished reading from the stream before it continues writing to it. So by postponing the read until the end, you allow the child to have less performance degradation at the cost of some additional time at the end. See AlsoSee Recipe 12.21; see the "ProcessStartInfo.RedirectStandardOutput Property" and "ProcessStartInfo.UseShellExecute Property" topics in the MSDN documentation. |