Library tutorials & articles

Launching a process from Windows Forms

Page 2 of 3
  1. Introduction
  2. Creating a class to call a process
  3. Wrapping Up

Creating a class to call a process

A script or executable can be run using System.Diagnostics.Process.  The string FileName is set to the executable (e.g. perl.exe, Run.bat, ConsoleApplication.exe ).  The string Arguments is set to the command-line arguments for that executable (e.g. perlscript.pl, filename1.txt filename2.txt , etc).  The following code will start that executable.

    Process process = new Process();
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;
    process.StartInfo.CreateNoWindow = true;
    process.StartInfo.FileName = FileName;
    process.StartInfo.Arguments = Arguments;
    process.StartInfo.WorkingDirectory = WorkingDirectory;
    process.Start();

Notice that the standard output and standard error have both been redirected.  There are two StreamReaders in the Process class that can be used to read the output:  Process.StandardOutput and Process.StandardError .  Often, the output is not read until after the process has finished, as in the following:

    string output = process.StandardOutput.ReadToEnd();

Reading to the end will not work for this application, since we want to read the data as the process is running.

Multiple threads

The solution is to use multiple threads.  One thread is dedicated to running the process and two more threads are dedicated to reading the standard error and standard output.  This is mentioned in MSDN documentation.  Each of these two threads will run a simple function that sits in a loop reading from the stream until the stream is closed.

    void ReadStdOut()
    {
        string str;
        while ((str = process.StandardOutput.ReadLine()) != null)
        {
            // do something with str
        }
    }

After each line is read into str, we would like to notify a windows form to display the text.  Raising an event is probably the best way to accomplish this.  For every new line of text received (on either StandardOutput or StandardError) an event will be raised.  A windows form class can subscribe to these events and update a text box.  Simple, but it won't quite work without some additional work.

Important rule of windows forms

There is an important rule of windows forms and multithreading. Controls are (almost entirely) not thread safe.  This means that an event raised from any thread other than the UI Thread cannot use methods or properties of a control.  There are a few methods guaranteed to be safe including  Control.Invoke and  Control.BeginInvoke. These methods are used to run a function on the UI thread. 

Thankfully, we can inherit from the class AsyncOperation (written by I.D. Griffiths from the above mentioned MSDN article) to solve several problems.  First, this class allows us to raise an event on a UI thread of a hosting or target control.  The above function becomes:

    public delegate void DataReceivedHandler(object sender,
        DataReceivedEventArgs e);
    public event DataReceivedHandler StdOutReceived;
    void ReadStdOut()
    {
        string str;
        while ((str = process.StandardOutput.ReadLine()) != null)
        {
            FireAsync(StdOutReceived, this, new DataReceivedEventArgs(str));
        }
    }

FireAsync is a method provided by the class AsyncOperation.  It raises an event (or more specifically invokes any delegate) on the UI thread of a form or control.  StdOutReceived is the event that will be raised.   DataReceivedEventArgs is a class derived from EventArgs that has a single string containing the text to be displayed (its definition is not shown here for brevity).

The second thing AsyncOperation provides is a method of canceling a process.  Let's take a look at that class in more detail.

Inheriting from AsyncOperation

AsyncOperation is an abstract base class that assists in creating cancelable worker threads that can fire events back on a UI control (or form).  It provides two main methods that are called by a form class:  Start() and Cancel() .

AsyncOperation requires that one method be overridden: DoWork() . This method is called when the Start() method is called.  As the method runs, it is expected to watch for a flag CancelRequested that is set from a call to Cancel() .  If the flag is true, the method should acknowledge the cancel and return.

The implementation of DoWork() is as follows:

    void protected override void DoWork()()
    {
        // Start a new process for the cmd
        Process process = new Process();
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.RedirectStandardError = true;
        process.StartInfo.CreateNoWindow = true;
        process.StartInfo.FileName = FileName;
        process.StartInfo.Arguments = Arguments;
        process.StartInfo.WorkingDirectory = WorkingDirectory;
        process.Start();
       
        // Invoke stdOut and stdErr readers - each
        // has its own thread to guarantee that they aren't
        // blocked by, or cause a block to, the actual
        // process running (or the gui).
        new MethodInvoker(ReadStdOut).BeginInvoke(null, null);
        new MethodInvoker(ReadStdErr).BeginInvoke(null, null);
        // Wait for the process to end, or cancel it
        while (! process.HasExited)
        {
            Thread.Sleep(SleepTime); // sleep
            if (CancelRequested)
            {
                // Not a very nice way to end a process,
                // but effective.
                process.Kill();
                AcknowledgeCancel();
            }
        }
    }

The methods DoWork()ReadStdOut() , and ReadStdErr() , the properties FileName and Arguments , and the events StdOutReceived and StdErrReceived are all added to a class ProcessCaller which derives from AsyncOperation.  Both classes can be downloaded as part of the zipfile at the top of the page.

Comments

  1. 10 Apr 2004 at 11:06

    Perhaps you can use this software:
    http://www.accusoft.com/imaging/imagegear/ig_formats.asp


    Regards,
    Magnus Strand

  2. 22 Mar 2004 at 11:28
    hi
    i want to convert a postscript file to image with visual c# .net.
    who can help me?
  3. 01 Jan 1999 at 00:00

    This thread is for discussions of Launching a process from Windows Forms.

Leave a comment

Sign in or Join us (it's free).

Mike Mayer Mike Mayer is a Microsoft MVP in Visual C#.
AddThis

Related discussion

Related podcasts

  • Object-Oriented Programming in Ruby

    In this episode, I talk with Scott Bellware about object-oriented programming in Ruby, and Ruby's object model. This is taken from a private conversation, and the audio quality suffers at times. Much thanks to Scott for allowing this to be released.This episode of the Alt.NET Podcast is bro...

Events coming up

  • Aug 28

    St. Louis Day of .NET

    St. Charles, United States

    Technical conference with be 2 full days of content with over 40 sessions from local and national speakers, with topics such as:•.NET languages: C#, VB.NET•Technologies: WPF, Silverlight, WCF•Development tools: Visual Studio, TFS, Expression Blend

Want to stay in touch with what's going on? Follow us on twitter!