Click here to Skip to main content
13,836,661 members
Click here to Skip to main content
Add your own
alternative version


78 bookmarked
Posted 14 Jul 2004
Licenced CPOL

Asynchronous processing of functions and webservice calls.

Rate this:
Please Sign up or sign in to vote.
An article on implementing the Whidbey (.NET 2.0) BackgroundWorker component in VB.NET 1.1, and extending it to support multiple arguments.

Sample Image - backgroundworker.png


A project I have been working on has required the implementation of asynchronous processing, particularly for long running calls to web services, so I started to investigate the Microsoft Application Block for asynchronous processing, and reading articles on multi-threading and so on. One of the articles I came across was Roy Osherove's blog on implementing the Background Worker process from .NET 2.0 in .NET 1.1. This seemed exactly what I was after, especially once I had read the original article by Joval Löwy, except for three things. There was no mention of how to use this to call a webservice, it was only possible to pass in one argument, so for execution of more complex functions you would have to pass in the arguments as an array, or hashtable, and it was written in C# so I couldn't put it into my code, as the project is in VB.NET. Yes, I know I could reference a C# assembly or project in VB.NET, but it would be cleaner for my solution to port the entire code to VB.NET.


The asynchronous call pattern has been explored many times now in .NET, and is described in detail in such articles as Creating a Simplified Asynchronous Call Pattern for Windows Forms Applications by David Hill.

Whidbey will introduce a component in the System.ComponentModel namespace called BackgroundWorker which encapsulates the complexities of asynchronous calls in a simple component which can be invoked from code, or dropped onto Form in the designer. It exposes methods and events for executing the asynchronous function, updating progress, canceling the execution, and completion of the execution.

As mentioned above, Joval Löwy and Roy Osherove have implemented a version of this class in C# for .NET v1.1, which will be sufficient for many people. I have ported in into VB.NET, and added a few tweaks to throw similar exceptions to the Whidbey version, when it is sub-classed. Then I extended it, and demonstrates how to use this to call webservices asynchronously.

Converting to VB.NET

This was not without its quirks.

The best practice for raising events tells us that we should always test that the event is not null before raising the event. That is to say, the event must have handlers. All the articles then give a code sample something like this:

if(SomeEvent != null)
   SomeEvent(this, args);

The C# implementations of BackgroundWorker both used this standard. But how do I do that in VB.NET? If you try the statement SomeEvent != Nothing, you get the error: 'Public Event SomeEvent(sender As Object, e As System.EventArgs)' is an event, and cannot be called directly. Use a 'RaiseEvent' statement to raise an event. Not very useful!

It turns out that in VB.NET, to get at the delegate derived object to test if it is Nothing, you just add "Event" to the end of the event name. Don't go looking for it in intellisense though, because it won't be there! The resulting VB.NET code is:

if Not SomeEventEvent is Nothing Then
   SomeEventEvent(Me, args)
End If

Why have I used SomeEventEvent(Me, args) instead of RaiseEvent SomeEvent(Me, args)?

The default method of a delegate is the Invoke method, and RaiseEvent in VB.NET just calls this method on the delegate, and it makes my code less language specific. Personal preference really.

NB//The source code download includes the VB.NET port and the sub-class I created.

Sub-Classing the BackgroundWorker

This serves two purposes. Firstly, when Whidbey is released, changing over to calling the native BackgroundWorker will require just eight code changes in the sub-class, and not a swathe of changes across all my application. Secondly, it enables us to add functionality to the BackgroundWorker, in this case, the ability to pass multiple parameters to the asynchronous function.

Firstly, the three event argument classes, DoWorkEventArgs, ProgressChangedEventArgs, and RunWorkerCompletedEventArgs must all be sub-classed, along with their associated handler delegates. Then the BackgroundWorker class itself can be sub-classed.

We must handle the underlying events and throw our sub-classed versions.

Public Shadows Event DoWork As DoWorkEventHandler
Public Shadows Event ProgressChanged As ProgressChangedEventHandler
Public Shadows Event RunWorkerCompleted As RunWorkerCompletedEventHandler

The DoWork event is the easiest, although you need to be careful to reassign the sub-classed event arguments back to the original event arguments.

Private Sub BackgroundWorker_DoWork(ByVal sender As Object, _
    ByVal e As VS2005.DoWorkEventArgs) Handles MyBase.DoWork
    If Not DoWorkEvent Is Nothing Then
        Dim args As New DoWorkEventArgs(e.Argument)
        DoWorkEvent(sender, args)
        e.Cancel = args.Cancel
        If Not e.Cancel Then
            e.Result = args.Result
        End If
    End If
End Sub

The ProgressChanged event, however, must call a copy of the ProcessDelegate function from our port of the BackgroundWorker.

Private Sub BackgroundWorker_ProgressChanged(ByVal sender As Object, _
    ByVal e As VS2005.ProgressChangedEventArgs) Handles MyBase.ProgressChanged
    If Not ProgressChangedEvent Is Nothing Then
        Me.ProcessDelegate(ProgressChangedEvent, Me, _
            New ProgressChangedEventArgs(e.ProgressPercentage, Nothing))
    End If
End Sub

This is because the Whidbey version does not have this method, so if we call the base class function, then we will lose our ability to upgrade easily.

The worst one however is the RunWorkerCompleted event. The RunWorkerCompletedEventArgs class throws an exception if the Result property is accessed and would return nothing, and we must work around this in order to pass the event arguments into our sub-classed event arguments. Microsoft has changed some of the internal workings of delegates for Whidbey.

In .NET 1.1, the exception causes the callback delegate to be called twice, if we do not handle it. The second time, a new exception is thrown because you cannot call EndInvoke twice.

In Whidbey, the exception will bubble up the call stack until it is handled.

To deal with this, we only read the value of the Result property if we have not cancelled or erred.

Private Sub BackgroundWorker_RunWorkerCompleted(ByVal sender As Object, _
    ByVal e As VS2005.RunWorkerCompletedEventArgs) _
    Handles MyBase.RunWorkerCompleted
    If Not RunWorkerCompletedEvent Is Nothing Then
        Dim result As Object = Nothing
        If e.Cancelled = False AndAlso e.Error Is Nothing Then
            result = e.Result
        End If
        Me.ProcessDelegate(RunWorkerCompletedEvent, Me, _
            New RunWorkerCompletedEventArgs(result, e.Error, e.Cancelled))
    End If
End Sub

Finally, we wanted to extend the class. We do this by simply overloading the RunWorkerAsync method and passing it a ParamArray of type Object().

Public Overloads Sub RunWorkerAsync(ByVal ParamArray arguments As Object())
End Sub

Using the code

Making basic asynchronous calls with this class is a simple matter of hooking up the DoWork, ProgressChanged, and RunWorkerCompleted events, then calling the RunWorkerAsync method as I show in my demo app.

Making asynchronous webservice calls is a little odd however. You hook up the events in the same manner as normal, and in the DoWork event handler, call the webservice synchronously. This is exactly what happens under the wraps when you call the BeginWebMethod/EndWebMethod methods produced automatically in the proxy class. It spawns a new thread and makes a normal synchronous call from there.

Of course, the asynchronous thread will be locked up for the duration of the call, so you need to check if there is a cancellation pending when it returns. Your RunWorkerCompleted handler must ensure that it only does anything if cancelled is False.

It is impossible to monitor the progress of a webservice call until the data begins to return, at which point you can monitor the progress of receiving the data stream. However, for the purposes of the demo, I just set a timer running which updates the progress bar ten times per second. It's good enough to make users think something is happening, which it is, just not on their machines!


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


About the Author

The Man from U.N.C.L.E.
Software Developer
United Kingdom United Kingdom
Unfortunately my real name was already in use as a code project login. For those of you who are wondering I am really Napoleon Solo. Sorry, I mean, Mark Jackson. Well, I do look a bit like him I think.

You may also be interested in...


Comments and Discussions

Questionfew questions Pin
doran_doran12-Jun-08 7:55
memberdoran_doran12-Jun-08 7:55 
AnswerRe: few questions Pin
kiquenet.com11-Aug-08 1:25
professionalkiquenet.com11-Aug-08 1:25 
QuestionCancelling Pin
Kim Bilida26-Dec-05 23:07
memberKim Bilida26-Dec-05 23:07 
AnswerRe: Cancelling Pin
luke72720-Jan-06 7:53
memberluke72720-Jan-06 7:53 
GeneralRe: Cancelling Pin
Malcolm Hall26-Oct-06 14:37
memberMalcolm Hall26-Oct-06 14:37 
QuestionIf the user quits the appilcation? Pin
Kim Bilida9-Dec-05 7:18
memberKim Bilida9-Dec-05 7:18 
AnswerRe: If the user quits the appilcation? Pin
The Man from U.N.C.L.E.11-Dec-05 21:54
memberThe Man from U.N.C.L.E.11-Dec-05 21:54 
GeneralRedesign Pin
Tim McCurdy6-Oct-05 8:14
memberTim McCurdy6-Oct-05 8:14 
GeneralRe: Redesign Pin
The Man from U.N.C.L.E.6-Oct-05 8:35
memberThe Man from U.N.C.L.E.6-Oct-05 8:35 
GeneralRe: Redesign Pin
Tim McCurdy10-Oct-05 4:30
memberTim McCurdy10-Oct-05 4:30 
GeneralRe: Redesign Pin
The Man from U.N.C.L.E.10-Oct-05 5:29
memberThe Man from U.N.C.L.E.10-Oct-05 5:29 
GeneralRe: Redesign Pin
Tim McCurdy10-Oct-05 6:10
memberTim McCurdy10-Oct-05 6:10 
GeneralEventEvent Pin
Oktay115-Feb-05 14:48
memberOktay115-Feb-05 14:48 

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.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web06 | 2.8.190114.1 | Last Updated 15 Jul 2004
Article Copyright 2004 by The Man from U.N.C.L.E.
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid