Wrapping an external app in a class
Written by Ian Elliot   
Monday, 18 April 2011
Article Index
Wrapping an external app in a class
Exit events and cross threading
Invoke method

Exit events

Ideally we should notice when the user closes the application that we started. You can do this by polling the HasExited property of the Process object but it is also possible to generate and handle an event when the process terminates. That is we can opt to receive an event from the process when it terminates.

To do this you need to add:

NPad.EnableRaisingEvents = true;

and add an Exited event handler to the Process object:

NPad.Exited +=new EventHandler(Npad_Exited);

The event handler is defined in the usual way as part of the NotePad class:

void Npad_Exited(object sender, EventArgs e)
{
...
}

Of course in the event handler we can do anything that we like, including disposing of the class that wraps the NotePad application.


Passing the event

We now have a way that that NotePad class can respond to the event that the application it has started running has terminated. However given that the idea is to make the wrapper customisable via properties and methods, we really should pass the event on to the next layer of code up i.e. the code that is making use of the NotePad class to gain access to the Notepad application.

This should be easy enough but there are some subtle problems.

To pass the event on all we need to do is declare a suitable event in the class:

public event EventHandler Exited;

Now our NotePad class has an Exited event of its own but we need to implement the machinery to raise the event. Notice that for simplicity the same delegate type, i.e. EventHandler, is used rather than a custom tyoe.  To raise the event all we have to do is call delegate – notice that this calls each of the functions that have been assigned to the delegate.

void Npad_Exited(object sender, EventArgs e)
{
if (Exited != null)
{
Exited(this, e);
}
}

This is getting a little involved but the basic idea is simple enough: because when the Npad.Exited event is raised as the application process terminates, it calls the function assigned to its delegate i.e. Npad_Exited. This in turn calls the Exited delegate and hence any functions assigned to it.

To hook up to this event all we have to do is:

NotePad NP = new NotePad();
NP.Exited+=new EventHandler(NP_Exited);

and, of course, supply the code for the event handler:

private void NP_Exited(object sender, 
EventArgs e)
{
Application.Exit();
}

If you try this out you should discover that the application that created the NotePad object stops running at the same time as it does.

You can use the same technique for passing on other events related to the process that that class creates.

Cross threading

The code given above contains a subtle “feature”. As long as you restrict the event handler to working with objects it has created, or with static objects, there is nothing more to add.

But suppose instead of closing the application you simply want to hide the form so that it can be used again - with a new instance of the NotePad, say. If you change the event handler to:

private void NP_Exited(object sender, 
EventArgs e)
{
this.Visible = false;
}

then when you run the program an unhandled exception occurs –

System.InvalidOperationException

Although if you are using Visual Studio Express you will have to view the Output Window to see it. This is a cross threading error and it is intended to protect you from treating controls as threadsafe when they aren’t.

This is, or should be, a familiar concern to anyone trying to create a multi-threaded program, but in this case the multi-threaded nature is a by-product of using a separate process. When the process raises the event it uses its own thread and we pass this on using the same thread. If that thread attempts to access a control that it didn’t create the error message results.

So what is the solution?

 

Banner



Last Updated ( Monday, 18 April 2011 )