Click here to Skip to main content
15,880,503 members
Articles / Programming Languages / C#
Article

Solving Problems of Monitoring Standard Output and Error Streams of a Running Process

Rate me:
Please Sign up or sign in to vote.
4.91/5 (26 votes)
17 Mar 2011CPOL6 min read 96.7K   4.6K   47   19
Introduces a class to help solve several problems of reading standard output/input of a running process.

Introduction

The System.Diagnostic.Process class allows the execution (or spawning) of other programs from within a .NET application. At times, it may be necessary for a program to monitor the text output of a running process.

This article highlights some of the problems encountered when monitoring output streams via the System.Diagnostic.Process class, and presents a class solution to work around these issues.

At the end of the article, there are several references where you can find more in depth information on some of the topics discussed.

Background: Standard Error, Standard Output, Standard Input Streams

The UNIX Operating System was the first to establish a standard output (stdout), standard error (stderr), and standard input (stdin) stream mechanism. In the spirit of UNIX, these three streams could be treated like files and accessed with standard file read/write functionality. The standard output stream is primarily used for normal data reporting, the standard error is used to report errors or warning information, and the standard input is used to send input text to the program.

Microsoft Windows maintained this philosophy for processes. GUI processes are capable of writing to standard error/output, but this is rarely utilized except for debug type of messages.

Why Monitor Output Streams?

It may be necessary for one program to utilize functionality contained in another software. Often this is done by using an application programming interface (API), Web Services, .NET assemblies, and COM objects. In a perfect world, all software would provide easy to use, fully documented interfacing solutions, but in reality, this is sometimes not the case. It may be that there is not an interface solution for a particular command-line program, but by interacting with the running programs via its output streams, a reasonable level of integration can be achieved.

The Mono project has provided the capability to run on multiple UNIX type platforms, where command line programs are still heavily used. By being able to monitor output and send input to the processes, integration should be fairly straightforward with these command line programs.

Summary of Steps to Monitoring Output Via Standard Methods

(See MSDN documentation for additional explanation)

  • Create a System.Diagnostic.ProcessStartInfo object, and set the UseShellExecute property to false, and the RedirectStandardInput, RedirectStandardOutput, RedirectStandardError properties to true.
  • Call System.Diagnostic.Process.Start() passing in the pre-initialized ProcessStartInfo object to that method.
  • To read output asynchronously (do not block waiting of output), add an event handler to the Process.OutputDataReceived event and call Process.BeginOutputReadLine(). The handler will receive text when the process writes to its standard output stream.
  • To read output synchronously (block until process writes text to output stream), call the Process.Read(), Process.ReadLine(), or Process.ReadToEnd() methods.

Two Problems Encountered When Monitoring Output Streams

(See MSDN documentation additional explanation)

  1. For synchronous read operations, a deadlock condition may occur when the parent process is waiting on the child process to write text and the child process waits on the parent process to read its text.
  2. For asynchronous read operations, Process.OutputDataReceived and Process.ErrorDataReceived are only notified after a newline character is read. This could delay, or prevent, the notification of output text being written (at least until more text is written with a newline terminator). This situation happens with the Windows command line interpreter, "cmd.exe", where the command prompt is written without a newline character and the program is waiting for user input. For example, cmd.exe will write "C:\>" without a newline terminator.

Solutions to Stream Reading Problems

The MSDN documentation suggests having two threads - one for reading stdout and the other for reading stderr. The solution presented implements this suggestion, and additionally solves the problem of blocking on a stream read operation - waiting on a newline character.

The ProcessIoManager class is introduced in this project to address the existing problems of reading output streams. It implements two separate reader threads to monitor both the stdout and stderr streams in the background, and notifies via event handlers when text has been read.

The following pseudo code summarizes using ProcessIoManager:

C#
// Create and initialize ProcessInfo structure 
System.Diagnostics.ProcessInfo myProcessInfo = 
       < create and initialize ProcessInfo structure > ;
// Create and start a new process 
System.Diagnostics.Process myProcess = 
       System.Diagnostics.Process.Start ( myProcessInfo ) ;
// Create a new process manager to monitor and notify of output 
ProcessIoManager processIoMgr = new ProcessIoManager( myProcess ) ; 
// Add event handlers in order to be notified of stdout/stderr text read: 
processIoMgr.StderrTextRead += new StringReadEventHandler(this.OnStderrTextRead);
processIoMgr.StdoutTextRead += new StringReadEventHandler(this.OnStdoutTextRead); 
// Tell the manager to start monitoring the output 
processIoMgr.StartProcessOutputRead() ;
// Add code to the OnStderrTextRead() and OnStdoutTextRead() methods
// to handle the text that has been read from the streams. 
//STOP output monitoring (disposes of the reader threads) 
processIoMgr.StopMonitoringProcessOutput() ;

Pseudo code for threads monitoring and reading stdout/stderr streams

(See the ProcessIoManager.ReadStream(), ReadStandardOutputThreadMethod(), ReadStandardErrorThreadMethod() methods for the implementation of this pseudo-code)

  • Clear output buffer
  • Perform synchronous read of 1 character on stream (blocks on read until character is read)
  • Obtain a synchronization lock, blocking the other stream until all of the current stream is processed
  • Add the single character to output buffer:
  • while ( there are characters in output stream to be read) 
    {
        Read single output character Append character to output buffer 
        if ( character was a newline character) 
        { 
            Notify event handlers and pass in output buffer 
            as a string Clear output buffer 
        } 
    }
  • Notify event handlers of any remaining text in the output buffer
  • Clear output buffer

Summary of Project Code

The example project consists of a single Windows Form (MainForm) that contains a text box control, CmdWindowBoxSync, that uses the ProcessIoManager class to implement a command line window that can be used directly on a Windows Form. The control displays the stdout/stderr text, as well as lets the user type in text to be sent to the stdin process stream. CmdWindowBoxSync will notify listeners of stdout/stderr reads via the StdoutTextRead and StderrTextRead events. The command to execute is defaulted to "cmd.exe" and is executed by pressing the "Run And Monitor Process" button. A command line prompt will appear in the interactive text window:

MainForm Screen Shot

Caveats of the Current ProcessIoManager Stream Reader Threads

The stdout/stderr threads obtain a lock so that they can synchronize reading, so that all text is read from the stream before it relinquishes the lock. This might cause a problem where, for example, some stderr text is interspersed with many lines of stdout text. If this happens, the stderr text may not be received in the correct sequence.

Ideas on Putting Some Pieces Together: Command Line Program Automation

Now that stdout/stderr streams can be simultaneously monitored, command line tasks can now be automated. Instead of a user typing input, a program could be written to monitor and parse program output, formulate an appropriate text response, then send that response to the running process via the stdin stream.

This project was not meant to be a complete dissertation or an all-in-one solution to all problems with output streams, but meant to present a simple project to be used as a building block to solve larger problems.

Happy coding :)

Additional References

History

  • March 2011 - Curt C. - Initial submission of article and sample project.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Senior Software Developer with background history in developing software for both Unix and Windows based systems using a variety of languages and technologies, including: C#, C++, C, and Java.

Comments and Discussions

 
QuestionHow about discovering a secondary process Pin
mwpowellhtx26-Apr-18 13:12
mwpowellhtx26-Apr-18 13:12 
PraiseMy Vote of 5 Pin
PeOpLesChAmP2-Jun-17 6:47
PeOpLesChAmP2-Jun-17 6:47 
QuestionProcessIoManager in asp.net application Pin
Member 112502412-Aug-15 7:46
Member 112502412-Aug-15 7:46 
GeneralMy vote of 5 Pin
Member 1125024114-Jul-15 2:40
Member 1125024114-Jul-15 2:40 
QuestionError Pin
Sebek Kaa15-Jun-15 1:06
Sebek Kaa15-Jun-15 1:06 
GeneralMy vote of 5 Pin
Member 97880232-Mar-15 10:12
Member 97880232-Mar-15 10:12 
GeneralMy vote of 5 Pin
amitthk27-Nov-14 18:12
professionalamitthk27-Nov-14 18:12 
GeneralFork with enhancements Pin
Chris J14-May-14 5:48
Chris J14-May-14 5:48 
GeneralRe: Fork with enhancements Pin
Member 8523453-Nov-14 4:37
Member 8523453-Nov-14 4:37 
SuggestionFix for jumbled text on resize Pin
Alakh Sethi12-Dec-13 22:47
Alakh Sethi12-Dec-13 22:47 
Suggestiontypo? Pin
Ivan Ferrer22-Oct-12 5:08
Ivan Ferrer22-Oct-12 5:08 
QuestionExecution of remote process not working Pin
k0walski2k25-Apr-12 2:13
k0walski2k25-Apr-12 2:13 
Hi,

Thank you for code and tutorial.
Unfortunately this does not work for me when using PSEXEC against a remote machine.

My command is PSEXEC and Cmd Arguments is: cmd /c netstat -ano

It does not complete but this is exact same behaviour as when i use async. or sync. IO.

It's very frustrating but i think it must be problem with .NET itself as many people have this problem and the MSDN techs are no help on this issue.
AnswerRe: Execution of remote process not working Pin
DeyChandan15-Oct-12 23:34
DeyChandan15-Oct-12 23:34 
QuestionExcellent Code Pin
Member 332582215-Sep-11 3:42
Member 332582215-Sep-11 3:42 
AnswerRe: Excellent Code Pin
Curt C10-Jan-12 4:18
Curt C10-Jan-12 4:18 
GeneralMy vote of 5 Pin
winxpdll14-Sep-11 6:15
winxpdll14-Sep-11 6:15 
GeneralRe: My vote of 5 Pin
Curt C10-Jan-12 4:17
Curt C10-Jan-12 4:17 
GeneralMy vote of 5 Pin
Ian Good17-Mar-11 14:54
Ian Good17-Mar-11 14:54 
GeneralRe: My vote of 5 Pin
Curt C21-Mar-11 3:52
Curt C21-Mar-11 3:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.