Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Windows Forms / WinForm General / February 2007

Tip: Looking for answers? Try searching our database.

BackgroundWorker does not fire the RunWorkerCompleted event

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
John Hann - 05 Feb 2007 23:22 GMT
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
John V Hann - 06 Feb 2007 19:30 GMT
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...

Rate this thread:







Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.