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

Launching a process and processing its standard output in real-time

Rate me:
Please Sign up or sign in to vote.
3.63/5 (10 votes)
30 May 20062 min read 46.7K   771   31   4
A patch for an article by Mike Mayer.

Introduction

When working in a project to burn a CD automatically from a backup directory, I had to evaluate the output of a console program.

Google helped me to find some articles doing similar work. The best I found was by Mike Mayer and published here: Launching a process and displaying its standard output. The article is great and got my 5!

I used Mike's ProcessCaller and AsyncOperation classes. But there were some real-time issues which forced me to make some source code changes which I want to publish here. (That's the reason why my article title is very similar to Mike's title.)

Real-time Issues:

In the StdOutReceived event handler of my application, besides writing them into a TextBox, I collected all input lines in a StringBuilder, and in the Completed event handler, I wanted to evaluate the contents of the StringBuilder. But is was empty! To find the reason, in the Completed event handler, I wrote a "*** Process Done ***" to the TextBox.

The output was:

Image 1

As to be seen, the Completed event overhauled all the StdOutReceived events.

I fixed this in this way:

In Mike's ProcessCaller class:

  • I added the properties "public bool EDone;" and "public bool SDone;"
  • I added an internal StringBuilder and a method to add content to the StringBuilder.
  • I added the property "public string Type;" to the DataReceivedEventArgs class.
  • I changed the ReadStdOut method to:
    C#
    protected virtual void ReadStdOut()
    {
      string str;
      while ((str = process.StandardOutput.ReadLine()) != null)
      {      
        FireAsync(StdOutReceived, this, new DataReceivedEventArgs("S", str));
      }
      FireAsync(StdOutReceived, this, new DataReceivedEventArgs("S", null));
    }
  • and the ReadStdErr method accordingly (with the type "E").
  • In my application, I changed the StdOutReceived event handler to:
    VB
    Private Sub callerRead(ByVal sender As Object, _
                ByVal e As DataReceivedEventArgs)
      Dim processCaller As ProcessCaller = CType(sender, ProcessCaller)
      ' text is null when end of read appeared in processCaller
      If IsNothing(e.Text) Then
        Select Case e.Type
          Case "S"
            ' store in processCaller that standard output has finished
            processCaller.SDone = True
          Case "E"
            ' store in processCaller that error output has finished
            processCaller.EDone = True
        End Select        Me.txtLog.AppendText(e.Type & " Finished")
        ' if both are finished, everything is done.
        ' I can evaluate etc.
        If processCaller.SDone And processCaller.EDone Then
            Evaluate(processCaller)
      Else
        Dim tmp As String = e.Text & Environment.NewLine
        ' append line to StringBuilder in processCaller
        processCaller.AddOutput(tmp)
        Me.txtLog.AppendText(e.Type & " " & tmp)
      End If
    End Sub

This gave the result:

Image 2

As to be seen, when the error output is completed, and at the very end, the standard output is done. So my evaluation processing here has the complete data.

There was one issue left: The "E" lines and "S" lines are not mixed. They depend on the order of invoking in ProcessCaller:

C#
new MethodInvoker(ReadStdErr).BeginInvoke(null, null);
new MethodInvoker(ReadStdOut).BeginInvoke(null, null);

So to reach a 99% "real-time" processing, I interrupted the ReadStdOut and ReadStdErr method threads, so that the method (and the ReadStdErr method accordingly) finally looks like:

C#
protected virtual void ReadStdOut()
{
  string str;
  while ((str = process.StandardOutput.ReadLine()) != null)
  {      
    FireAsync(StdOutReceived, this, 
              new DataReceivedEventArgs("S", str));
    // sleep for a very short time
    // to give ReadStdErr the ability to work
    Thread.Sleep(1); 
  }
  FireAsync(StdOutReceived, this, 
            new DataReceivedEventArgs("S", null));
}

Points of Interest

Would be nice if Mike changes his article accordingly!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Germany Germany
Peter Schlang, working with computers since 1974
Developing mainly for newspapers since 1981, first as employee of ATEX
Freelancer since 1987
Preferred language is VB: Starting with VB 1.0 and VBDOS, up to VB.NET

Comments and Discussions

 
Questioncannot derive from sealed type 'System.ComponentModel.AsyncOperation' Pin
pierre130519-Jun-07 20:28
pierre130519-Jun-07 20:28 
AnswerRe: cannot derive from sealed type 'System.ComponentModel.AsyncOperation' Pin
thewampek25-Mar-09 2:28
thewampek25-Mar-09 2:28 
GeneralRe: cannot derive from sealed type 'System.ComponentModel.AsyncOperation' Pin
Adriaan2b25-Nov-09 8:24
Adriaan2b25-Nov-09 8:24 

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.