PC Review


Reply
Thread Tools Rate Thread

BackgroundWorker does not fire the RunWorkerCompleted event

 
 
=?Utf-8?B?Sm9obiBIYW5u?=
Guest
Posts: n/a
 
      5th Feb 2007
I am trying to unit test a control that uses a background worker component
and have discovered that the RunWorkerCompleted event does not fire in this
scenario. The documentation doesn't say anything about this behavior, and I
was wondering if anyone here could tell me why the RunWorkerCompleted event
does no fire in the following sample code:

using System;
using System.Threading;
using System.Windows.Forms;
using System.ComponentModel;

namespace BackgroundWorkerPrototype
{
class Program
{
static void Main( string[] args )
{
BackgroundWorkerUserControl control = new
BackgroundWorkerUserControl();
control.StartBackgroundWorker();
Thread.Sleep( 3000 );
}
}

class BackgroundWorkerUserControl : UserControl
{
private BackgroundWorker worker;

public BackgroundWorkerUserControl()
{
worker = new BackgroundWorker();
SuspendLayout();
worker.DoWork += new DoWorkEventHandler( worker_DoWork );
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
worker_RunWorkerCompleted );
}

public void StartBackgroundWorker()
{
worker.RunWorkerAsync();
}

private void worker_DoWork( object sender, DoWorkEventArgs e )
{
Console.WriteLine( "DoWork started." );
Thread.Sleep( 1000 );
Console.WriteLine( "DoWork completed." );
}

private void worker_RunWorkerCompleted( object sender,
RunWorkerCompletedEventArgs e )
{
Console.WriteLine( "RunWorkerCompleted called." );
}
}
}

Thanks,
John
 
Reply With Quote
 
 
 
 
=?Utf-8?B?Sm9obiBWIEhhbm4=?=
Guest
Posts: n/a
 
      6th Feb 2007
Ok, I think I know what is going wrong here. To troubleshoot this, I ended up
implementing my own code to handle the long running thread asyncronously. I
did this by defining a custom delegate and invoking it asyncronously. Here's
the method that I'm calling asyncronously:

private void LoginUser( User user )
{
LoginUserCompletedDelegate completedDelegate =
new LoginUserCompletedDelegate( LoginUserCompleted );
AuthenticationResult result = AuthenticationResult.BadCredentials;

try
{
result = _authenticationGateway.Authenticate( user );

// initial exception here
this.Invoke( completedDelegate, null, result, user );
}
catch( Exception e )
{
// another exception here
this.Invoke( completedDelegate, e, result, null );
}
}

In this method, I'm trying to ensure that if any exceptions occur, they get
passed back to the UI thread. The BackgroundWorker component also provides
this functionality, and is very likely using Control.Invoke or
Control.InvokeAsync to do it. Trouble is, those methods are not supported if
you instance a control directly, as I was attempting to do in my tests.

So, in the code above, the first Invoke call results in an exception, which
then get handled my the catch block. Then, the Invoke call in the catch block
results in a new exception. Finally, in my EndInvoke method (listed below), I
try to call Invoke again, with similar results:

private void LoginUserEndInvoke( IAsyncResult result )
{
LoginUserCompletedDelegate completedDelegate =
new LoginUserCompletedDelegate( LoginUserCompleted );
try
{
LoginUserDelegate loginUserDelegate =
(LoginUserDelegate)result.AsyncState;

// yet another new exception here
loginUserDelegate.EndInvoke( result );
}
catch( Exception e )
{
// yet another new exception here
this.Invoke( completedDelegate, e,
AuthenticationResult.BadCredentials, null );
}
}

The resulting exception looks like this:

Thread Name: <No Name>
System.InvalidOperationException: Invoke or BeginInvoke cannot be called on
a control until the window handle has been created.
at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate
method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
at MyProject.Controls.LoginControl.LoginUserEndInvoke(IAsyncResult
result) in C:\MyProject.Controls\LoginControl.cs:line 139
at
System.Runtime.Remoting.Messaging.AsyncResult.SyncProcessMessage(IMessage msg)
at
System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(IMessage msg, IMessageSink replySink)
at
System.Runtime.Remoting.Proxies.AgileAsyncWorkerItem.ThreadPoolCallBack(Object o)
at System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(Object
state)
at System.Threading.ExecutionContext.Run(ExecutionContext
executionContext, ContextCallback callback, Object state)
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object
state)

I suspect that the BackgroundWorker component is using Control.Invoke to
implement it's RunWorkerCompleted event, and it silently absorbs the
exception above. This isn't great behavior, and if they would have simply let
the exception bubble up, it would have saved me a day and a half of work.
Perhaps someone will change this behavior in a later version of the
framework. Now to figure out how to unit test winforms ui code...
 
Reply With Quote
Reply

Thread Tools
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Fire event in the same thread as the UI like BackgroundWorker Sin Jeong-hun Microsoft C# .NET 4 25th Oct 2007 04:58 PM
BackgroundWorker Does Not Fire RunWorkerCompleted VAADADMIN Microsoft C# .NET 1 7th Sep 2007 03:44 PM
BackgroundWorker Does Not Fire RunWorkerCompleted VAADADMIN Microsoft C# .NET 1 7th Sep 2007 02:28 PM
BackgroundWorker RunWorkerCompleted does not fire Jeff Microsoft Dot NET Framework Forms 0 21st Mar 2007 01:26 PM
BackgroundWorker and RunWorkerCompleted event =?Utf-8?B?S2V2aW4gQnVydG9u?= Microsoft Dot NET Framework Forms 2 6th Sep 2006 01:21 AM


Features
 

Advertising
 

Newsgroups
 


All times are GMT +1. The time now is 06:00 AM.