none
Abbruch in BackgroundWorker DoWork RRS feed

  • Frage

  • Hallo

    Kann mir jemand auf die Sprünge helfen.

    In BackgroundWorker_DoWork rufe ich eine Function in einer Schleife auf, die einen String zurückgibt.
    Dieser String zeigt dieser einen Fehler an.

    Wenn dieser String <> "", will ich den BackgrounWorker beenden.
    Ich denke das das mit e.Cancel richtig ist.
    Ebenso übergebe ich in e.Result meinen String.
    Dieser kommt leider nicht im  BackgrounWorker_RunWorkerCompleted an.

    Meine Frage:
    Wie beende ich in BackgroundWorker_DoWork  richtig?
    Wie kann ich dann noch einen String zurückgeben?

    Gruss Peter

    Freitag, 13. März 2015 15:50

Antworten

  • Hallo Peter,

    um DoWork zu beenden, verlasse die Methode - also Return oder zum Ende der Methode kommen. Das RunWorkerCompleted Ereignis wird danach ausgeführt. Das Setzen von e.Cancel bzw. e.Result ist optional.

    Ein Beispiel, das ich irgendwo anders mal gegeben hatte:

    Imports System
    Imports System.ComponentModel
    Imports System.Diagnostics
    Imports System.Data.SqlClient
    Imports System.Windows.Forms
    
    Public Class BackgroundPollingForm
    
        Dim WithEvents pollingBackgroundWorker As New BackgroundWorker
    
        Public Sub New()
            InitializeComponent()
    
            pollingBackgroundWorker.WorkerSupportsCancellation = True
            pollingBackgroundWorker.WorkerReportsProgress = True
        End Sub
    
        Private Sub Button1_Click(sender As Object, e As System.EventArgs) Handles Button1.Click
            If pollingBackgroundWorker.IsBusy Then
                Debug.WriteLine("Worker stopped")
                pollingBackgroundWorker.CancelAsync()
            Else
                Debug.WriteLine("Worker started")
                pollingBackgroundWorker.RunWorkerAsync()
            End If
        End Sub
    
        Private Sub BackgroundPollingForm_Shown(sender As Object, e As System.EventArgs) Handles MyBase.Shown
            pollingBackgroundWorker.RunWorkerAsync()
        End Sub
    
        Private Sub BackgroundPollingForm_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
            If e.Cancel = False AndAlso Me.pollingBackgroundWorker.IsBusy Then
                Me.pollingBackgroundWorker.CancelAsync()
            End If
        End Sub
    
        Private Sub pollingBackgroundWorker_DoWork(sender As Object, e As DoWorkEventArgs) _
            Handles pollingBackgroundWorker.DoWork
            Do While Not pollingBackgroundWorker.CancellationPending
                Try
                    Using connection As New SqlConnection(My.Settings.AdventureWorksConnectionString)
                        connection.Open()
                        Dim command As New SqlCommand("SELECT COUNT(*) FROM Sales.SalesOrderHeader;", connection)
                        Dim value = command.ExecuteScalar()
    
                        pollingBackgroundWorker.ReportProgress(0, value)
                    End Using
                Catch ex As Exception
                    Debug.WriteLine("Worker exception " & ex.Message)
                    e.Cancel = True
                    Return
                End Try
                System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5))
            Loop
        End Sub
    
        Private Sub pollingBackgroundWorker_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) _
            Handles pollingBackgroundWorker.ProgressChanged
            If Not Me.IsDisposed Then
                Debug.WriteLine("Worker ProgressChanged {0}", e.UserState)
                Me.InfoTextBox.Text = e.UserState.ToString()
            End If
        End Sub
    
        Private Sub pollingBackgroundWorker_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) _
            Handles pollingBackgroundWorker.RunWorkerCompleted
            If e.Error IsNot Nothing Then
                Debug.WriteLine("Worker exception " & e.Error.Message)
            ElseIf e.Cancelled Then
                Debug.WriteLine("Worker cancelled")
            Else
                Debug.WriteLine("Worker completed")
            End If
        End Sub
    End Class
    

    Wobei die SQL Abfrage sinnfrei ist und durch Deine eigene Funktion / Schleife ersetzt würde.

    Gruß Elmar

    • Als Antwort markiert peter haus Freitag, 13. März 2015 19:18
    Freitag, 13. März 2015 16:02
    Beantworter

Alle Antworten

  • Hallo Peter,

    um DoWork zu beenden, verlasse die Methode - also Return oder zum Ende der Methode kommen. Das RunWorkerCompleted Ereignis wird danach ausgeführt. Das Setzen von e.Cancel bzw. e.Result ist optional.

    Ein Beispiel, das ich irgendwo anders mal gegeben hatte:

    Imports System
    Imports System.ComponentModel
    Imports System.Diagnostics
    Imports System.Data.SqlClient
    Imports System.Windows.Forms
    
    Public Class BackgroundPollingForm
    
        Dim WithEvents pollingBackgroundWorker As New BackgroundWorker
    
        Public Sub New()
            InitializeComponent()
    
            pollingBackgroundWorker.WorkerSupportsCancellation = True
            pollingBackgroundWorker.WorkerReportsProgress = True
        End Sub
    
        Private Sub Button1_Click(sender As Object, e As System.EventArgs) Handles Button1.Click
            If pollingBackgroundWorker.IsBusy Then
                Debug.WriteLine("Worker stopped")
                pollingBackgroundWorker.CancelAsync()
            Else
                Debug.WriteLine("Worker started")
                pollingBackgroundWorker.RunWorkerAsync()
            End If
        End Sub
    
        Private Sub BackgroundPollingForm_Shown(sender As Object, e As System.EventArgs) Handles MyBase.Shown
            pollingBackgroundWorker.RunWorkerAsync()
        End Sub
    
        Private Sub BackgroundPollingForm_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
            If e.Cancel = False AndAlso Me.pollingBackgroundWorker.IsBusy Then
                Me.pollingBackgroundWorker.CancelAsync()
            End If
        End Sub
    
        Private Sub pollingBackgroundWorker_DoWork(sender As Object, e As DoWorkEventArgs) _
            Handles pollingBackgroundWorker.DoWork
            Do While Not pollingBackgroundWorker.CancellationPending
                Try
                    Using connection As New SqlConnection(My.Settings.AdventureWorksConnectionString)
                        connection.Open()
                        Dim command As New SqlCommand("SELECT COUNT(*) FROM Sales.SalesOrderHeader;", connection)
                        Dim value = command.ExecuteScalar()
    
                        pollingBackgroundWorker.ReportProgress(0, value)
                    End Using
                Catch ex As Exception
                    Debug.WriteLine("Worker exception " & ex.Message)
                    e.Cancel = True
                    Return
                End Try
                System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5))
            Loop
        End Sub
    
        Private Sub pollingBackgroundWorker_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) _
            Handles pollingBackgroundWorker.ProgressChanged
            If Not Me.IsDisposed Then
                Debug.WriteLine("Worker ProgressChanged {0}", e.UserState)
                Me.InfoTextBox.Text = e.UserState.ToString()
            End If
        End Sub
    
        Private Sub pollingBackgroundWorker_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) _
            Handles pollingBackgroundWorker.RunWorkerCompleted
            If e.Error IsNot Nothing Then
                Debug.WriteLine("Worker exception " & e.Error.Message)
            ElseIf e.Cancelled Then
                Debug.WriteLine("Worker cancelled")
            Else
                Debug.WriteLine("Worker completed")
            End If
        End Sub
    End Class
    

    Wobei die SQL Abfrage sinnfrei ist und durch Deine eigene Funktion / Schleife ersetzt würde.

    Gruß Elmar

    • Als Antwort markiert peter haus Freitag, 13. März 2015 19:18
    Freitag, 13. März 2015 16:02
    Beantworter
  • Hallo Elmar,

    wieder mal vielen Dank.
    Ich werde dein Codeschnipsel gleich mal vornehmen.

    Ich verstehe gerade immer weniger.

    Ich teste mit dem Stückchen Code unten.
    Bei der Zeile     BackgroundWorker1.ReportProgress(i, DateTime.Now)    kein Problem.

    Bei den Zeilen
       s = "Mein String " & DateTime.Now
       BackgroundWorker1.ReportProgress(i, s)
    und           
       BackgroundWorker1.ReportProgress(i, "Working---")
    geht nichts mehr.    Meldung: Ein Aufrufziel hat einen Ausnahmefehler verursacht.

    gibt es da eine Erklärung?

    Vielen Dank.
    Peter

    Private Sub backgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
           
            Dim s As String
            Dim start As DateTime = DateTime.Now
            e.Result = ""

            For i As Integer = 0 To 30
                System.Threading.Thread.Sleep(50)
               
                BackgroundWorker1.ReportProgress(i, DateTime.Now)
               
                's = "Mein String " & DateTime.Now
                'BackgroundWorker1.ReportProgress(i, s)

                'BackgroundWorker1.ReportProgress(i, "Working---")
            
                If i >= 10 Then
                    e.Result = "MyCancel"
                    e.Cancel = True
                    Return
                End If

                If BackgroundWorker1.CancellationPending Then
                    e.Cancel = True
                    Return
                End If
            Next

            Dim duration As TimeSpan = DateTime.Now - start      
            e.Result = "Duration: " + duration.TotalMilliseconds.ToString() + " ms."
        End Sub

    Freitag, 13. März 2015 16:11
  • Hallo Elmar.

    Dein Beispiel Funktioniert.
    Wieso meines nicht ???

    Gruss Peter

    Freitag, 13. März 2015 16:31
  • Hallo Peter,

    da Du den EventHandler nicht zeigst, kann ich nur vermuten...

    In UserState kann man alles reinpacken, weil es vom Type System.Object ist, aber bei der Verarbeitung muss man es richtig auspacken, sonst knallt es. Erweitert man in meinem Beispiel die Übergabe zu:

    	pollingBackgroundWorker.ReportProgress(0, value)
    	' Weitere...
    	pollingBackgroundWorker.ReportProgress(CType(value, Integer), "Hallo")
    	pollingBackgroundWorker.ReportProgress(CType(value, Integer), DateTime.Now)
    	pollingBackgroundWorker.ReportProgress(CType(value, Integer), value)
    

    so könnte man das z. B. so tun:

        Private Sub pollingBackgroundWorker_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) _
            Handles pollingBackgroundWorker.ProgressChanged
            If Not Me.IsDisposed Then
                If TypeOf e.UserState Is Integer Then
                    Debug.WriteLine("UserState is an integer.")
                ElseIf TypeOf e.UserState Is DateTime Then
                    Debug.WriteLine("UserState is a date.")
                ElseIf TypeOf e.UserState Is String Then
                    Debug.WriteLine("UserState is a string.")
                End If
                Debug.WriteLine("Worker ProgressChanged {0}", e.UserState)
                Me.InfoTextBox.Text = e.UserState.ToString()
            End If
        End Sub
    

    Meine Ausgabe funktioniert unabhängig davon, da sie via ToString() aus allem einen String macht. In den Fällen, in denen es auf den Typ ankommt, muss man wie oben testen und via CType umwandeln oder aber TryCast verwenden. Am besten definiert man jedoch eine Klasse, die alle notwendigen Elemente als Eigenschaften hat, so dass man UserState direkt via DirectCast umwandeln kann.

    Gruß Elmar

    Freitag, 13. März 2015 20:34
    Beantworter