MyPopWatcher and Delegate Fun

Sometimes I'm like a kid in a candy store when I realize just how easy some tasks are in VS.NET (2003). Not everything in VS.NET is fun and games: my event handlers are frequently blown away (see this thread -- one of many sightings of this bug).

But today's goodness shows how easy it is to make asynchronous web service calls from within an application. Strangely, I've never had to do this until programming the MyPopWatcher example (here) -- but I guess it's not that strange since, in a web environment, there's seldom a need for true asynchronous calls.

At any rate, I wanted to create a sample application for interfacing with the MyPopInfo web service. The quick-and-dirty first pass I had on this app did the call synchronously; and for this app, that's pretty acceptable. Unfortunately, though, my unrelenting standards lifetrap dictated that blocking the application while waiting for a response was no good.

My first thought was to create a background thread that handled the web service interaction, but this, too, was not an ideal solution. Then I realized how easy it is to initiate an asynchronous call to a web service. VS.NET does most of the work for you!

When adding a web reference to MyPopInfo, VS.NET automatically added the following methods to the MyPopInfo class:

public string GetInfo(string Username, string Password) {
    object[] results = this.Invoke("GetInfo", new object[] {
        Username, Password});

    return ((string)(results[0]));
}

public System.IAsyncResult BeginGetInfo(string Username, string Password, System.AsyncCallback callback, object asyncState) {
    return this.BeginInvoke("GetInfo", new object[] {
        Username, Password}, callback, asyncState);
}


Using the BeginGetInfo makes it very easy to call asynchronously. Using Reflector, you can dive into the System.Web.Services namespace and see how this works. A study for another time, though. To use the BeginGetInfo, we use a delegate to specify which method to call when the GetInfo completes (recall that MyPopInfo returns an XML string containing the number of e-mail messages in each mailbox):

private void WebServiceCallback(IAsyncResult result)
{
    //called when GetInfo completes
    string popresult = "";
    popresult = info.EndGetInfo(result);

    //process the xml response here
}


Now, instead of calling GetInfo, we'll call BeginGetInfo:

info = new MyPopInfo.MyPopInfo();

info.BeginGetInfo(txtUsername.Text, txtPassword.Text,
    new AsyncCallback(WebServiceCallback), null);


And away we go. The UI thread will no longer be blocked waiting for the call to complete.

The second half of the MyPopWatcher example was updating the systray icon as necessary. Since a timer control fires the web service call at the user defined interval, updates to the icons/menus requires a delegate. This is because a worker thread cannot directly access a control (specifically ones with a Windows handle) created in the main UI thread. In truth, you can probably do it and get away with it much of the time, but it's not thread safe and will crash the application in a most unpleasant way (try it out for fun) when it doesn't work.

First, I have a method that parses the XML response, which looks a bit like:

private void ParseXmlResponse(string Xml)
{
    XmlDocument doc = new XmlDocument();
    doc.LoadXml(Xml);
    
    //evaluate, change the icons, etc., here
}


We can't call ParseXmlResponse directly from the WebServiceCallback because of this threading issue (note: the issue is modifying the icons and menus, not the XML parsing). But we can do this using a delegate. A delegate is simply a "proxy" to a method ... a delegate declaration contains the signature to a method, and is typically used to communicate between a caller and a target (an ASyncCallback delegate was used when calling the web service).

So to complete the WebServiceCallback section, we'll declare a delegate (giving it the same signature as the method that parses the XML) and then call the delegate with a pointer to the method to be called...

delegate void ParseXmlDelegate(string Xml);
private void WebServiceCallback(IAsyncResult result)
{
    //called when GetInfo completes
    string popresult = "";
    popresult = info.EndGetInfo(result);    //we use a delegate here because we cannot directly
    //work with controls created in UI thread.
    ParseXmlDelegate dg = new ParseXmlDelegate(ParseXmlResponse);
    dg(popresult);
}


For a full source code listing or info on this project, visit this link.

In summary, I'm really happy with how quickly things can come together in VS.NET. Delegates have a ton of cool uses; in a web environment I've used them for custom events, but this small project was a good way to get exposed to two other typical uses.
Comments are closed

My Apps

Dark Skies Astrophotography Journal Vol 1 Explore The Moon
Mars Explorer Moons of Jupiter Messier Object Explorer
Brew Finder Earthquake Explorer Venus Explorer  

My Worldmap

Month List