Nicht behandelte Ausnahmen in BackgroundWorker

70

Ich habe eine kleine WinForms-App, die ein BackgroundWorker-Objekt verwendet, um einen lang laufenden Vorgang auszuführen.

Die Hintergrundoperation löst gelegentlich Ausnahmen aus, normalerweise wenn jemand eine Datei geöffnet hat, die neu erstellt wird.

Unabhängig davon, ob der Code von der IDE ausgeführt wird oder nicht. .NET öffnet ein Fehlerdialogfeld, in dem der Benutzer darüber informiert wird, dass eine nicht behandelte Ausnahme aufgetreten ist. Das Kompilieren des Codes mithilfe der Release-Konfiguration ändert dies ebenfalls nicht.

Laut MSDN :

Wenn die Operation eine Ausnahme auslöst, die Ihr Code nicht behandelt, fängt der BackgroundWorker die Ausnahme ab und übergibt sie an den RunWorkerCompleted-Ereignishandler, wo sie als Error-Eigenschaft von System.ComponentModel .. ::. RunWorkerCompletedEventArgs verfügbar gemacht wird. Wenn Sie unter dem Visual Studio-Debugger ausgeführt werden, wird der Debugger an der Stelle im DoWork-Ereignishandler unterbrochen, an der die nicht behandelte Ausnahme ausgelöst wurde.

Ich erwarte, dass diese Ausnahmen gelegentlich ausgelöst werden, und möchte sie eher im RunWorkerCompleted-Ereignis als in DoWork behandeln. Mein Code funktioniert ordnungsgemäß und der Fehler wird im RunWorkerCompleted-Ereignis korrekt behandelt, aber ich kann für mein ganzes Leben nicht herausfinden, wie ich das Auftreten des .NET-Fehlerdialogs verhindern kann, in dem über die "Nicht behandelte Ausnahme" geklagt wird.

Soll der BackgroundWorker diesen Fehler nicht automatisch abfangen? Ist das nicht das, was in der MSDN-Dokumentation steht? Was muss ich tun, um .NET darüber zu informieren, dass dieser Fehler behandelt wird , während die Ausnahme weiterhin in die Error-Eigenschaft von RunWorkerCompletedEventArgs übertragen werden kann?

Andy
quelle

Antworten:

133

Was Sie beschreiben, ist nicht das definierte Verhalten von BackgroundWorker. Du machst etwas falsch, vermute ich.

Hier ist ein kleines Beispiel, das beweist, dass BackgroundWorker Ausnahmen in DoWork isst und Ihnen in RunWorkerCompleted zur Verfügung stellt :

var worker = new BackgroundWorker();
worker.DoWork += (sender, e) => 
    { 
        throw new InvalidOperationException("oh shiznit!"); 
    };
worker.RunWorkerCompleted += (sender, e) =>
    {
        if(e.Error != null)
        {
            MessageBox.Show("There was an error! " + e.Error.ToString());
        }
    };
worker.RunWorkerAsync();

Meine psychischen Debugging-Fähigkeiten offenbaren mir Ihr Problem: Sie greifen in Ihrem RunWorkerCompleted-Handler auf e.Result zu. Wenn ein e.Error vorhanden ist, müssen Sie damit umgehen, ohne auf e.Result zuzugreifen. Der folgende Code ist beispielsweise schlecht, schlecht, schlecht und löst zur Laufzeit eine Ausnahme aus:

var worker = new BackgroundWorker();
worker.DoWork += (sender, e) => 
    { 
        throw new InvalidOperationException("oh shiznit!"); 
    };
worker.RunWorkerCompleted += (sender, e) =>
    {
        // OH NOOOOOOOES! Runtime exception, you can't access e.Result if there's an
        // error. You can check for errors using e.Error.
        var result = e.Result; 
    };
worker.RunWorkerAsync();

Hier ist eine ordnungsgemäße Implementierung des RunWorkerCompleted-Ereignishandlers:

private void RunWorkerCompletedHandler(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Error == null)
    {
       DoSomethingWith(e.Result); // Access e.Result only if no error occurred.
    }
}

VOILA, Sie erhalten keine Laufzeitausnahmen.

Judah Gabriel Himango
quelle
+1 Guter Punkt. In meinem Beispiel wurde auf die Besonderheiten der Fehlerbehandlung hingewiesen, aber mein Code würde tatsächlich eine weitere Ausnahme verursachen, wenn in der DoWork-Methode niemals eine Ausnahme getroffen würde.
Bobby Cannon
Ich bin anderer Meinung ... Ich habe auch Probleme zu sehen, wie die BGW-Klasse die Kosten auffrisst ... da mir meine App manchmal Zugriff auf meinen Benutzerbereich unter Win7 gewährt und manchmal nicht. Als ich weiter nachforschte, stellte ich fest, dass der Zugriff verweigert wurde, weil der Ordner nicht existiert. Ich finde, dass der Fehler manchmal in meiner App ausgelöst wird und manchmal nicht.
IbrarMumtaz
@Ibrar, suchen Sie in Ihrem RunWorkerCompleted-Ereignishandler immer nach e.Error? Stellen Sie sicher, dass Sie dies überprüfen, bevor Sie etwas tun. Alle fangbaren Ausnahmen werden dort gemeldet.
Judah Gabriel Himango
für die Beantwortung. Ich bin deinem Beispiel gefolgt und habe es geändert ... soweit so gut. Ist nicht umgefallen, habe ich eine else-Anweisung hinzugefügt, die ein benutzerdefiniertes Ereignis auslöst und den Fehler in meiner Logger-Klasse protokolliert. Lassen Sie die Ausnahme behandelt und nicht unbehandelt bleiben. Hoffentlich ist dies das Letzte, was ich davon sehe.
IbrarMumtaz
4
@ Judah: Danke dafür. Du hast mir viel Zeit gespart. Dies riecht nach Magie, und ich wünschte wirklich, .NET würde eine Ausnahme auf die Nase werfen ("Auf die Ergebniseigenschaft kann möglicherweise nicht zugegriffen werden, wenn eine Ausnahme von ausgelöst wurde BackgroundWorker DoWork"), anstatt zu versuchen, das Richtige zu tun und nur die Dinge zu verwirren .
Michael Petrotta
40

Ich würde dem MSDN-Text hinzufügen :

Wenn die Operation eine Ausnahme auslöst, die Ihr Code nicht behandelt, fängt der BackgroundWorker die Ausnahme ab und übergibt sie an den Ereignishandler RunWorkerCompleted, wo sie als Error-Eigenschaft von System.ComponentModel .. ::. RunWorkerCompletedEventArgs verfügbar gemacht wird. Wenn Sie unter dem Visual Studio-Debugger ausgeführt werden, wird der Debugger an der Stelle im DoWork-Ereignishandler unterbrochen, an der die nicht behandelte Ausnahme ausgelöst wurde.

... UND der Debugger meldet die Ausnahme als "~ Ausnahme wurde vom Benutzercode nicht behandelt"

Lösung: Führen Sie das Programm nicht unter dem Debugger aus und es funktioniert wie erwartet: Ausnahme in e.Error abgefangen.

Mark Cranness
quelle
5
Dank dafür. Was ich wirklich gerne wissen würde, ist, warum um alles in der Welt es sich so verhält. Macht es unmöglich, die RunWorkerCompletedAusnahmebehandlung des Ereignisses zu debuggen .
Yu_ominae
Wenn sie nur das bisschen über den Debugger in die Dokumentation aufgenommen hätten ... Also wahr. Vielen Dank.
andersop
1
@yu_ominae: Nicht unmöglich. Immer wenn der Debugger die Ausnahme bricht, können Sie F5 (Weiter) drücken, damit der BackgroundWorker die Ausnahme abfängt und die Ausführung wie erwartet fortgesetzt wird RunWorkerCompleted.
Anders Carstensen
Mark - Sie sollten wirklich in Betracht ziehen, Ihre Antwort zu aktualisieren, um Anders Kommentar aufzunehmen.
DavidRR
2

Dies ist eine alte Frage, aber ich habe sie gefunden, als ich die gleichen Symptome gegoogelt habe. Veröffentlichen Sie dies, falls jemand anderes es aus demselben Grund findet.

Judahs Antwort ist richtig, aber es ist nicht der einzige Grund, warum das Dialogfeld "Nicht behandelte Ausnahme im Benutzercode" angezeigt werden kann. Wenn eine Ausnahme aus einem Konstruktor im Hintergrundthread ausgelöst wird , führt diese Ausnahme sofort zum Dialogfeld und wird nicht an das Ereignis RunWorkerCompleted übergeben. Wenn Sie den fehlerhaften Code außerhalb eines Konstruktors (auf eine andere Methode) verschieben, funktioniert er wie erwartet.

Reich
quelle
Ich habe versucht, eine Ausnahme in einen Konstruktor zu werfen, und der RunWorkerCompletedhat sie abgefangen. Vielleicht haben Sie es mit einer Konsolenanwendung versucht? Nach dieser Antwort verhalten sich Konsolen-Apps anders.
Ispiro
Es ist möglich. Die spezifischen Ausnahmen, mit denen ich dieses Problem hatte, gaben mir "Der Typinitialisierer für xyz hat eine Ausnahme ausgelöst" -Fehler, ich bin nicht sicher, welche spezifischen Umstände das usw. hervorrufen würden.
Rich
Alter Thread, aber ich erinnere mich an den Rest dieser Geschichte - die TypeInitializationException ist die, an die ich gedacht habe, und sie wird ausgelöst, wenn eine Ausnahme in einem statischen Konstruktor auftritt .
Rich
1

[Bearbeiten]

Juda hat einen großen Punkt. In meinem Beispiel wurde auf die Besonderheiten der Fehlerbehandlung hingewiesen, aber mein Code würde tatsächlich eine weitere Ausnahme verursachen, wenn in der DoWork-Methode niemals eine Ausnahme getroffen würde. Dieses Beispiel ist in Ordnung, da wir speziell die Fehlerbehandlungsfunktionen des BackgroundWorker zeigen. Wenn Sie den Fehlerparameter jedoch nicht gegen null prüfen, kann dies Ihr Problem sein.

[/Bearbeiten]

Ich sehe nicht die gleichen Ergebnisse. Kannst du einen kleinen Code posten? Hier ist mein Code.

private void Form1_Load(object sender, EventArgs e)
{
    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += new DoWorkEventHandler(worker_DoWork);
    worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
    worker.RunWorkerAsync();
}

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // Will cause another exception if an exception didn't occur.
    // We should be checking to see if e.Error is not "null".
    textBox1.Text = "Error? " + e.Error;
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    for (int i = 0; i < 10; i++)
    {
        if (i < 5)
        {
            Thread.Sleep(100);
        }
        else
        {
            throw new Exception("BOOM");
        }   
    }
}

Programmausgabe:

Error? System.Exception: BOOM bei BackgroundException.Form1.worker_DoWork (Objektabsender, DoWorkEventArgs e) in D: \ Workspaces \ Sandbox \ BackgroundException \ BackgroundException \ Form1.cs: Zeile 43 bei System.ComponentModel.BackgroundWorker.OnDoWork (DoWorkEventArgs) bei .ComponentModel.BackgroundWorker.WorkerThreadStart (Objektargument)

Ein interessanter Artikel, der Ihrer Frage ähnelt. Es enthält einen Abschnitt zum Umgang mit Ausnahmen.

http://www.developerdotstar.com/community/node/671

Bobby Cannon
quelle
0

Ich hatte das gleiche Problem und habe bereits die Antwort von Juda angewendet, bevor ich dieses Thema nach einigem googeln gefunden habe.

Nun, imo ist die Antwort von Juda teilweise richtig. Ich habe hier eine bessere Antwort gefunden

Der Debugger macht die Arbeit gut. Wenn Sie die Anwendung unter "realen Bedingungen" ausführen, behandelt RunWorkerCompleted die Ausnahme wie erwartet und das Anwendungsverhalten ist ebenfalls das erwartete.

Ich hoffe diese Antwort hilft.

Cavaleiro
quelle
Diese frühere Antwort befasst sich mit den Vorgängen im Visual Studio-Debugger.
DavidRR