views:

460

answers:

1

I have a background worker wich can be cancelled.

The normal flows interrupt itself when the CancelPending variable turns to true (responding to user interaction on UI wich call worker.CancelAsynch() ) , exceptions are thrown because if that (since normal flow is interrupted, lots of null ref exception are thrown)

So when the worker returns, I want to be able to distinguish exception that have been thrown when the worker was canceled (to silently ignore them) from exceptions thrown when worker was not canceled (to report them to UI).

My code is as follow (sorry for the c#/vb mix ...) :

The worker class:

Public Class ClassBaseGetObjectsWorker
    Inherits System.ComponentModel.BackgroundWorker


 Protected Overrides Sub OnDoWork(ByVal e As System.ComponentModel.DoWorkEventArgs)
        Try

            Dim cpt As Int16 = 0
            While cpt < 5
                System.Threading.Thread.Sleep(1000)
                cpt = cpt + 1
                If CheckForCancellation() Then
        'Simulating exception due to cancel
                    Throw New Exception("Exception du to cancel !")
                End If
            End While

        Catch exc As Exception
            e.Cancel = Me.CancellationPending
            Throw exc
        End Try

    End Sub
End Class

The call back :

void setObjSetCollWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
    if (e.Cancelled) {
     resultLabel.Text += "Canceled";
     //e.Error is selently ignored
    }
    else {
     if (e.Error != null) {
      //Reporting errors arising during normal (uncanceled) flow
      throw e.Error.InnerException;
     }
     else {
      //Worker process finished without error or being canceled.
      updateUIAfterBackgroundJob();
     }
    }
}

Then, when I'm doing worker.CancelAsynch(), e.Cancelled is set to false (wich is not what I expected) in the Completed call back. If I comment out "Trow exc" in the worker, if I test again, e.Cancelled is correctly set to true.

So, what is the cleanest way to get the information I want, that is : I want to know if the exception poping out in the completed handler was thrown when the worker was in the cancellationPending state or not.

(Sorry for my poor english)

A: 

If the best way to structure the code in your OnDoWork()-implementation is to throw an exception when you detect cancellation, do the following:

Create a CancelException:

public class CancelException: Exception {}

Throw this CancelException when you detect that cancellation is pending:

if(CheckForCancellation()) throw new CancelException();

Add a try-catch around the code in your OnDoWork()-method:

protected override void OnDoWork(DoWorkEventArgs e){
  try{
    //...
  }
  catch(CancelException){
    // silently return
    return;
  }
}

That way your code will obey the BackgroundWorker-contract (which is to return from OnDoWork() when you detect cancellation pending, rather than to throw an exception), and the Cancelled property should now be as you expect

Rasmus Faber
A not so nice alternative would be adding a public member "WasCancelled" on my BackgroundWorker derived class that can be set to true in the OnDoWork catch block (Me.WasCancelled = Me.CancellationPending). This member could be then used in the completed call back.
jfburdet