none
RunWorkerCompletedEventArgs.Result is always "Operation has been cancelled." RRS feed

  • Question

  • No matter what I do, I can't prevent RunWorkerCompletedEventArgs.Result from returning "Operation has been cancelled." when the BackgroundWorker is cancelled using the .CancelAsynch() method.

    Even if I specifically set the .Result property of the BackgroundWorker in the DoWork event handler after cancelling, it will only return "Operation has been cancelled."

    Is this the intended behavior? How can I return, well... anything, when the BackgroundWorker is cancelled?

        Private Sub bw_LoadFile_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles bw_LoadFile.DoWork
            Dim args As LoadFileArgs = e.Argument
    
            For i = 0 To 100
                If bw_LoadFile.CancellationPending = True Then
                    e.Cancel = True
                    Exit For
    
                Else
                    System.Threading.Thread.Sleep(100)
                    bw_LoadFile.ReportProgress(i)
    
                End If
    
            Next
    
            e.Result = args
    
        End Sub
    Thursday, June 7, 2012 3:25 PM

Answers

  • So here's a really ugly workaround; I added a boolean value to my argument class (see below) and when there is a cancellation pending I set that boolean to True. I then exit the DoWork event handler and check that value in the RunWorkerCompleted event handler to determine that the DoWork handler was indeed cancelled. I then call the .CancelAsync() method to actually cancel the thread.

    What a hack!

    Anyway, it works.

    'The argument class
    Public Class LoadFileArgs
        Public f As FileInfo
        Public t As String
        Public c As Boolean
    End Class
    
    'Running the worker
    ...
                            Dim args As LoadFileArgs = New LoadFileArgs()
                            With args
                                .f = New FileInfo(dialog.FileName)
                                .t = b.Tag
                                .c = False
                            End With
    
                            With bw_LoadFile
                                .WorkerSupportsCancellation = True
                                .WorkerReportsProgress = True
                                .RunWorkerAsync(args)
                            End With
    ...
    
    'The DoWork handler
    Private Sub bw_LoadFile_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles bw_LoadFile.DoWork
            Dim args As LoadFileArgs = e.Argument
    
            For i = 0 To 100
                If bw_LoadFile.CancellationPending = True Then
                    args.c = True
                    Exit For
    
                Else
                    System.Threading.Thread.Sleep(100)
                    bw_LoadFile.ReportProgress(i)
    
                End If
    
            Next
    
            e.Result = args
    
    End Sub
    
    'The RunWorkerCompleted handler
    Private Sub bw_LoadFile_RunWorkerCompleted(sender As System.Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bw_LoadFile.RunWorkerCompleted
            Dim args As LoadFileArgs = e.Result
    
            If args.c Then
                tssl_Status.Text = String.Concat("Cancelled opening ", LCase(args.t), " file.")
                tspb_Reading.Visible = False
    ...

    Thursday, June 7, 2012 6:15 PM

All replies

  • So here's a really ugly workaround; I added a boolean value to my argument class (see below) and when there is a cancellation pending I set that boolean to True. I then exit the DoWork event handler and check that value in the RunWorkerCompleted event handler to determine that the DoWork handler was indeed cancelled. I then call the .CancelAsync() method to actually cancel the thread.

    What a hack!

    Anyway, it works.

    'The argument class
    Public Class LoadFileArgs
        Public f As FileInfo
        Public t As String
        Public c As Boolean
    End Class
    
    'Running the worker
    ...
                            Dim args As LoadFileArgs = New LoadFileArgs()
                            With args
                                .f = New FileInfo(dialog.FileName)
                                .t = b.Tag
                                .c = False
                            End With
    
                            With bw_LoadFile
                                .WorkerSupportsCancellation = True
                                .WorkerReportsProgress = True
                                .RunWorkerAsync(args)
                            End With
    ...
    
    'The DoWork handler
    Private Sub bw_LoadFile_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles bw_LoadFile.DoWork
            Dim args As LoadFileArgs = e.Argument
    
            For i = 0 To 100
                If bw_LoadFile.CancellationPending = True Then
                    args.c = True
                    Exit For
    
                Else
                    System.Threading.Thread.Sleep(100)
                    bw_LoadFile.ReportProgress(i)
    
                End If
    
            Next
    
            e.Result = args
    
    End Sub
    
    'The RunWorkerCompleted handler
    Private Sub bw_LoadFile_RunWorkerCompleted(sender As System.Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bw_LoadFile.RunWorkerCompleted
            Dim args As LoadFileArgs = e.Result
    
            If args.c Then
                tssl_Status.Text = String.Concat("Cancelled opening ", LCase(args.t), " file.")
                tspb_Reading.Visible = False
    ...

    Thursday, June 7, 2012 6:15 PM
  • Hi ,

    Thank you for sharing your solution here.

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, June 8, 2012 9:55 AM
    Moderator
  • RunWorkerCompletedEventArgs already has a way to tell if operation is canceled.  You don't need to invent your own.

    void worker_DoWork(object sender, DoWorkEventArgs e) { var bw = (BackgroundWorker)sender; while (workToDo) { if (bw.CancellationPending) { e.Cancel = true; return; }     // do some work
    }
        e.Result = "Done";
    } void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (null != e.Error) MessageBox.Show(this, e.Error.Message, "Exception"); else if (e.Cancelled) MessageBox.Show(this, "Operation Cancelled", "Canceled"); else if (null != e.Result) MessageBox.Show(this, e.Result.ToString(), "Completed"); }


    This signature unintentionally left blank.



    • Edited by Nick F. _ Thursday, June 14, 2012 10:57 AM just because
    Thursday, June 14, 2012 10:24 AM
  • Nick,

    Within my DoWork Event Handler, I may cancel the worker for an error during execution. I need a way to determine whether the BackgroundWorker was cancelled as a result of the user requesting it (Calling the CancelAsync() method) or if it was cancelled as a result of an error in the DoWork Event Handler.

    Since the argument is always just the string I mentioned, it's impossible to tell why the CancelAsync() method was called and by what operation.

    Using my method above, I can set an exception or a string in the argument that's returned by the BackgroundWorker when it exits gracefully and handle the exception in the RunWorkerCompleted Event in either case.

    Tuesday, June 19, 2012 4:08 PM
  • You can do anything you wish in response to CancellationPending = True.  Unhandled errors will cause the DoWork event to terminate with the completed event error property set to the exception.

    Tuesday, June 19, 2012 5:17 PM
  • I guess I'm not being clear enough...

    During the initialization of the BackgroundWorker, I'm passing an object in the argument.

    The DoWork Event Handler manipulates this object. Regardless of how (or why) the DoWork Event Handler terminates, that object MUST be passed back to the initiating thread.

    This doesn't happen when the BackgroundWorker is cancelled because the object is overwritten by the string.

    Tuesday, June 19, 2012 5:29 PM
  • "This doesn't happen when the BackgroundWorker is cancelled because the object is overwritten by the string"

    You're the one doing the overwriting.  If you don't want it overwritten, don't overwrite it. 

    That is, don't set e.Cancel = true.  Indicate cancellation in the result object.  Example:

    Imports System.ComponentModel
    Imports System.Threading
    Public Class Form1
      
    Dim TestBGW As New BGW
    End Class
    Class BGW
      
    Inherits BackgroundWorker
      
    Sub New()
        
    Me.WorkerSupportsCancellation = True
        
    Me.RunWorkerAsync("MyObject")
        
    Thread.Sleep(100)
        
    Me.CancelAsync()
      
    End Sub
      
    Protected Overrides Sub OnDoWork(e As DoWorkEventArgs)
        
    MyBase.OnDoWork(e)
        
    While Not Me.CancellationPending
          
    Thread.Sleep(1000)
        
    End While
        e.Result = 
    New String() {e.Argument.ToString, "Cancelled"}
      
    End Sub
      
    Protected Overrides Sub OnRunWorkerCompleted(e As RunWorkerCompletedEventArgs)
        
    MyBase.OnRunWorkerCompleted(e)
        
    Dim Strings() As String = DirectCast(e.Result, String())
        
    MessageBox.Show(String.Join(", ", Strings))
      
    End Sub
    End Class

    • Edited by JohnWein Tuesday, June 19, 2012 5:52 PM
    Tuesday, June 19, 2012 5:35 PM
  • I guess I'm not being clear enough...

    During the initialization of the BackgroundWorker, I'm passing an object in the argument.

    The DoWork Event Handler manipulates this object. Regardless of how (or why) the DoWork Event Handler terminates, that object MUST be passed back to the initiating thread.

    This doesn't happen when the BackgroundWorker is cancelled because the object is overwritten by the string.

     I understand now. Typically when I use a BackgroundWorker I pass 'this' or keep my own reference to the initial argument.  This lets me leverage the stock functionality already built into the component.

    With your requirement, you will need to manage it yourself.  This includes trapping and storing any/all exceptions, since these will also invalidate the Result property of the completion event.


    This signature unintentionally left blank.

    Wednesday, June 20, 2012 12:22 PM