Необработанные исключения в BackgroundWorker

У меня есть небольшое приложение WinForms, которое использует объект BackgroundWorker для выполнения долговременной операции.

Операция фона вызывает случайные исключения, обычно, когда кто-то имеет открытый файл, который воссоздается.

Независимо от того, запускается ли код из среды IDE или нет, появляется диалоговое окно с сообщением об ошибке, информирующее пользователя о том, что произошло исключение Unhandled. Компиляция кода с использованием конфигурации Release также не изменяет это.

Согласно MSDN:

Если операция вызывает исключение, которое ваш код не обрабатывает, BackgroundWorker ловит исключение и передает его в обработчик события RunWorkerCompleted, где он отображается как свойство Error для System.ComponentModel..::. RunWorkerCompletedEventArgs. Если вы работаете под отладчиком Visual Studio, отладчик будет разбиваться в точке обработчика событий DoWork, где было обработано необработанное исключение.

Я ожидаю, что эти исключения будут исключены и захотят обработать их в событии RunWorkerCompleted, а не в DoWork. Мой код работает правильно, и ошибка обрабатывается правильно в событии RunWorkerCompleted, но я не могу на всю жизнь понять, как остановить диалог ошибок .NET, жалующийся на возникновение "необработанного исключения".

Разве BackgroundWorker не должен ломать эту ошибку автоматически? Не то, что говорится в документации MSDN? Что мне нужно сделать, чтобы сообщить .NET, что эта ошибка обрабатывается, но все же позволяет исключению просачиваться в свойство Error RunWorkerCompletedEventArgs?

Ответ 1

То, что вы описываете, - это не определенное поведение BackgroundWorker. Я подозреваю, что вы делаете что-то не так.

Вот небольшой пример, который подтверждает, что BackgroundWorker ест исключения в DoWork и делает их доступными для вас в 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();

Мои психические навыки отладки раскрывают вашу проблему: вы получаете доступ к e.Result в обработчике RunWorkerCompleted - если есть e.Error, вы должны обращаться с ним, не обращаясь к e.Result. Например, следующий код является плохим, плохим, плохим и будет генерировать исключение во время выполнения:

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 an
        // error. You can check for errors using e.Error.
        var result = e.Result; 
    };
worker.RunWorkerAsync();

Здесь правильная реализация обработчика события RunWorkerCompleted:

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

VOILA, вы не получите исключений во время выполнения.

Ответ 2

Я бы добавил к текст MSDN:

Если операция вызывает исключение, которое ваш код не обрабатывает, BackgroundWorker ловит исключение и передает его в обработчик события RunWorkerCompleted, где он отображается как свойство Error для System.ComponentModel..::. RunWorkerCompletedEventArgs. Если вы работаете в отладчике Visual Studio, отладчик будет разбиваться в точке обработчика событий DoWork, где было обработано необработанное исключение.

... И отладчик сообщит об этом исключении, поскольку "~ Исключение было не обработано кодом пользователя"

Решение: не запускаться под отладчиком, и работает как ожидалось: исключение попало в e.Error.

Ответ 3

Это старый вопрос, но я обнаружил его, в то время как у Google такие же симптомы. Проводя это, если кто-то найдет его по той же причине.

Иуда ответ, но это не единственная причина, по которой может появиться диалоговое окно "необработанное исключение в пользовательском коде". Если исключение вызывается внутри конструктора в фоновом потоке, это исключение немедленно вызовет диалог и не будет передано в событие RunWorkerCompleted. Если вы перемещаете нарушающий код вне любых конструкторов (любым другим способом), он работает так, как ожидалось.

Ответ 4

[изменить]

У Иуды есть отличная точка. В моем примере указана специфика обработки ошибки, но мой код действительно вызовет другое исключение, если исключение никогда не попадало в методе DoWork. Этот пример в порядке из-за того, что мы специально показываем возможности обработки ошибок BackgroundWorker. Однако, если вы не проверяете параметр ошибки на значение null, это может быть вашей проблемой.

[/Edit]

Я не вижу одинаковых результатов. Можете ли вы разместить небольшой код? Вот мой код.

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");
        }   
    }
}

Выход программы:

Ошибка? Исправление System.Exception: BOOM at BackgroundException.Form1.worker_DoWork (Объект отправителя, DoWorkEventArgs e) в D:\Workspaces\Sandbox\BackgroundException\BackgroundException\Form1.cs: линия 43 на System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs есть System.ComponentModel.BackgroundWorker.WorkerThreadStart(Объект аргумент)

Интересная статья, похожая на ваш вопрос. В нем есть раздел об обработке исключений.

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

Ответ 5

У меня была такая же проблема, и я уже применял ответ Иуды, прежде чем я нашел эту тему после некоторого поиска.

Хорошо, Имо ответ Иуды частично правильный. Я нашел лучший ответ здесь

Отладчик делает работу хорошо, если вы запускаете приложение в "реальных условиях", RunWorkerCompleted имеет дело с исключением, как ожидалось, и поведение приложения также ожидается.

Надеюсь, этот ответ поможет.