Search code examples
vb.netbackgroundworkerfunction-callcancellation

How to check for BackgroundWorker cancellation between each of multiple function calls


I have a BackgrounWorker that calls vairous methods from a custom class called ExcelOutput. The below function works, but it does not allow me to check for cancellation.

Private Sub bw_DoWork(ByVal sender As Object,
                      ByVal e As DoWorkEventArgs)

    Dim UserDump As New CurrentUserDumpInfo(Me.MainForm.ConfigForm) ' Create a new instance of the CurrentUserDumpInfo
    Dim Excel As New ExcelOutput(Me)                                ' Create a new instance of the ExcelOutput 
    Dim FirstRow As Integer = 4                                     ' The row on which the output should start
    Dim CurrentRow As Integer                                       ' The currnet row on which the output shoud continue

    '** Prepare the CurrentUserDumpInfo and the ExcelOutput *'
    Call UserDump.prepare()
    Call Excel.Prepare()

    CurrentRow = Excel.OutputGroup("General", UserDump.lstGeneralHeaders, UserDump.lstGeneralData, FirstRow)
    CurrentRow = Excel.OutputGroup("Address", UserDump.lstAddressHeaders, UserDump.lstAddressData, CurrentRow + 2)

    Call Excel.AutofitContents(4, CurrentRow)
    Call Excel.AlignCells(4, CurrentRow)
    Call Excel.StyleWorksheet()
    Call Excel.MakeSafeCopy()
    Call Excel.LockWorksheet()
    Call Excel.Finish(UserDump.CurrentUser.FullName)

End Sub

To do this, I've set each methods listed above to check for errors (using Try/Catch), and if there is an error, I set bw.WorkerSupportsCancellation = True call the bw.CancelAsync() method (you'll notice I pass Me when initiating the ExcelOutput instance, making this possible).

This method works, but to implement it fully I have to wrap every call in an If block like so, making the code very long and difficult to read (each call goes from 1 line to 6 lines) -

If bw.CancellationPending = True Then
    e.Cancel = True
    Exit Sub
Else
    CurrentRow = Excel.OutputGroup("General", UserDump.lstGeneralHeaders, UserDump.lstGeneralData, 4)
End If

Is there a better way to do this, that will both keep the code short and sweat but offer the functionality of the cancellation check? Thanks.

Update

Thanks to the answer below, here is the exact bw_DoWork() method that I am now using, which achieves exactly what I was looking for -

Private Sub bw_DoWork(ByVal sender As Object,
                      ByVal e As DoWorkEventArgs)

    Dim UserDump As New CurrentUserDumpInfo(Me.MainForm.ConfigForm) ' Create a new instance of the CurrentUserDumpInfo
    Dim Excel As New ExcelOutput(Me)                                ' Create a new instance of the ExcelOutput 
    Dim FirstRow As Integer = 4                                     ' The row on which the output should start
    Dim CurrentRow As Integer                                       ' The currnet row on which the output shoud continue

    Dim Counter As Integer = 0
    While Not bw.CancellationPending = True

        Select Case Counter

            Case 0 : Call UserDump.prepare() : Exit Select  ' Prepare the CurrentUserDumpInfo object ready for use
            Case 1 : Call Excel.Prepare() : Exit Select     ' Prepare the ExcelOutput object ready for use

            Case 2 : CurrentRow = Excel.OutputGroup("General", UserDump.lstGeneralHeaders, UserDump.lstGeneralData, FirstRow) : Exit Select
            Case 3 : CurrentRow = Excel.OutputGroup("Address", UserDump.lstAddressHeaders, UserDump.lstAddressData, CurrentRow + 2) : Exit Select
            Case 4 : CurrentRow = Excel.OutputGroup("Account", UserDump.lstAccountHeaders, UserDump.lstAccountData, CurrentRow + 2) : Exit Select
            Case 5 : CurrentRow = Excel.OutputGroup("Profile", UserDump.lstProfileHeaders, UserDump.lstProfileData, CurrentRow + 2) : Exit Select

            Case 6 : Call Excel.AutofitContents(4, CurrentRow) : Exit Select
            Case 7 : Call Excel.AlignCells(4, CurrentRow) : Exit Select
            Case 8 : Call Excel.StyleWorksheet() : Exit Select
            Case 9 : Call Excel.MakeSafeCopy() : Exit Select
            Case 10 : Call Excel.LockWorksheet() : Exit Select
            Case 11 : Call Excel.Finish(UserDump.CurrentUser.FullName) : Exit Select

            Case Else : Exit While

        End Select

        Counter += 1

    End While

    '** Check to see if the BackgroundWorker should be cancelled *'
    If bw.CancellationPending = True Then e.Cancel = True

End Sub

Solution

  • There's a "common" rule not to use try/catch in a DoWork event. The exception is if you want to dispose or clean up objects. If an error occurs it will be available in the RunWorkerCompleted event (e.Error).

    Private Sub bw_DoWork(sender As Object, e As DoWorkEventArgs) Handles bw.DoWork
    
        Dim obj As IDisposable = Nothing
        Dim [error] As Exception = Nothing
    
        Try
            obj = New Control()
            Throw New Exception("Simulated exception")
        Catch ex As Exception
            [error] = ex
        Finally
            If (Not obj Is Nothing) Then
                obj.Dispose()
                obj = Nothing
            End If
        End Try
    
        If (Not [error] Is Nothing) Then
            Throw [error]
        End If
    
    End Sub
    

    With that being said, you can try to do the work in a While Loop and check for cancellation after each cycle.

    Private Sub bw_DoWork(sender As Object, e As DoWorkEventArgs) Handles bw.DoWork
    
        Dim worker As BackgroundWorker = DirectCast(sender, BackgroundWorker)
        Dim num As Integer = Nothing
        Dim obj As IDisposable = Nothing
        Dim [error] As Exception = Nothing
    
        Try
            'TDOD: obj = New IDisposable ()
            num = 0
            While (Not e.Cancel)
                Select Case num
                    Case 0
                        'Do somthing
                        Exit Select
                    Case 1
                        'Do somthing
                        Exit Select
                    Case 2
                        'Do somthing
                        Exit Select
                    Case Else
                        Exit While
                End Select
                num += 1
                e.Cancel = worker.CancellationPending
            End While
        Catch ex As Exception
            [error] = ex
        Finally
            If (Not obj Is Nothing) Then
                obj.Dispose()
                obj = Nothing
            End If
        End Try
    
        If (Not [error] Is Nothing) Then
            Throw [error]
        ElseIf (Not e.Cancel) Then
            'TODO: e.Result = Nothing 
        End If
    
    End Sub