web-dev-qa-db-fra.com

Exceptions non gérées dans BackgroundWorker

J'ai une petite application WinForms qui utilise un objet BackgroundWorker pour effectuer une opération de longue durée.

L'opération en arrière-plan lève des exceptions occasionnelles, généralement lorsque quelqu'un a un fichier ouvert en cours de recréation.

Que le code soit exécuté à partir de IDE ou non .NET ouvre une boîte de dialogue d'erreur informant l'utilisateur qu'une exception non gérée s'est produite. La compilation du code à l'aide de la configuration Release ne change rien à cela Soit.

Selon MSDN :

Si l'opération déclenche une exception que votre code ne gère pas, BackgroundWorker intercepte l'exception et la transmet au gestionnaire d'événements RunWorkerCompleted, où elle est exposée en tant que propriété Error de System.ComponentModel .. ::. RunWorkerCompletedEventArgs. Si vous exécutez sous le débogueur Visual Studio, le débogueur s'arrêtera au point dans le gestionnaire d'événements DoWork où l'exception non gérée a été levée.

Je m'attends à ce que ces exceptions soient levées à l'occasion et je voudrais les gérer dans l'événement RunWorkerCompleted plutôt que dans DoWork. Mon code fonctionne correctement et l'erreur est gérée correctement dans l'événement RunWorkerCompleted mais je ne peux pas pour la vie de moi-même comprendre comment arrêter la boîte de dialogue d'erreur .NET se plaignant de "l'exception non gérée" de se produire.

Le BackgroundWorker n'est-il pas censé détecter cette erreur automatiquement? N'est-ce pas ce que dit la documentation MSDN? Que dois-je faire pour informer .NET que cette erreur est gérée tout en permettant à l'exception de se propager dans la propriété Error de RunWorkerCompletedEventArgs?

65
Andy

Ce que vous décrivez n'est pas le comportement défini de BackgroundWorker. Vous faites quelque chose de mal, je suppose.

Voici un petit exemple qui prouve que BackgroundWorker mange des exceptions dans DoWork, et les met à votre disposition dans RunWorkerCompleted:

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();

Mes compétences de débogage psychique me révèlent votre problème: vous accédez à e.Result dans votre gestionnaire RunWorkerCompleted - s'il y a une e.Error, vous devez le gérer sans accéder à e.Result. Par exemple, le code suivant est mauvais, mauvais, mauvais et lèvera une exception lors de l'exécution:

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();

Voici une implémentation appropriée du gestionnaire d'événements RunWorkerCompleted:

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

VOILA, vous ne recevrez pas d'exceptions d'exécution.

121

J'ajouterais au texte MSDN :

Si l'opération déclenche une exception que votre code ne gère pas, BackgroundWorker intercepte l'exception et la transmet au gestionnaire d'événements RunWorkerCompleted, où elle est exposée en tant que propriété Error de System.ComponentModel .. ::. RunWorkerCompletedEventArgs. Si vous exécutez sous le débogueur Visual Studio, le débogueur s'arrêtera au point du gestionnaire d'événements DoWork où l'exception non gérée a été levée.

... ET le débogueur signalera l'exception comme "~ L'exception n'a pas été gérée par le code utilisateur"

Solution: ne pas exécuter sous le débogueur et cela fonctionne comme prévu: exception interceptée dans e.Error.

36
Mark Cranness

C'est une vieille question, mais je l'ai trouvée en cherchant sur Google les mêmes symptômes. Publier ceci au cas où quelqu'un d'autre le trouverait pour la même raison.

La réponse de Juda est juste, mais ce n'est pas la seule raison pour laquelle la boîte de dialogue "exception non gérée dans le code utilisateur" peut apparaître. Si une exception est levée depuis l'intérieur d'un constructeur sur le thread d'arrière-plan, cette exception provoquera immédiatement la boîte de dialogue et ne sera pas transmise à l'événement RunWorkerCompleted. Si vous déplacez le code incriminé en dehors de tout constructeur (vers toute autre méthode), cela fonctionne comme prévu.

2
Rich

[Éditer]

Juda a un grand point. Mon exemple a souligné les détails de la gestion de l'erreur, mais mon code provoquerait en fait une autre exception si une exception n'était jamais rencontrée dans la méthode DoWork. Cet exemple est correct car nous montrons spécifiquement les capacités de gestion des erreurs de BackgroundWorker. Cependant, si vous ne comparez pas le paramètre d'erreur avec null, cela pourrait être votre problème.

[/Éditer]

Je ne vois pas les mêmes résultats. Pouvez-vous poster un petit code? Voici mon 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");
        }   
    }
}

Sortie du programme:

Erreur? System.Exception: BOOM à BackgroundException.Form1.worker_DoWork (expéditeur d'objet, DoWorkEventArgs e) dans D:\Workspaces\Sandbox\BackgroundException\BackgroundException\Form1.cs: ligne 43 à System.ComponentModel.BackgroundWorker.OnDoWork (DoWorkEventArgs e) sur System .ComponentModel.BackgroundWorker.WorkerThreadStart (argument Object)

Un article intéressant qui ressemble à votre question. Il contient une section sur la gestion des exceptions.

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

1
Bobby Cannon

J'ai eu le même problème et j'appliquais déjà la réponse de Juda avant de trouver ce sujet après quelques recherches sur Google.

Eh bien, imo la réponse de Juda est partiellement correcte. J'ai trouvé une meilleure réponse ici

Le débogueur fait bien le travail, si vous exécutez l'application dans des "conditions réelles", RunWorkerCompleted traite l'exception comme prévu et le comportement de l'application est également attendu.

J'espère que cette réponse vous aidera.

0
Cavaleiro