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

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

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

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

Согласно MSDN:

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

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

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


person Andy    schedule 25.06.2009    source источник


Ответы (5)


То, что вы описываете, не является определенным поведением 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's 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, вы не получите исключений во время выполнения.

person Judah Gabriel Himango    schedule 25.06.2009
comment
+1 Хорошая точка зрения. В моем примере показаны особенности обработки ошибки, но мой код на самом деле вызовет другое исключение, если исключение никогда не попадет в метод DoWork. - person Bobby Cannon; 25.06.2009
comment
Я прошу не согласиться ... Я также изо всех сил пытаюсь понять, как класс BGW съедает экспы ... поскольку иногда мое приложение предоставляет мне доступ к моей пользовательской области на Win7, а иногда нет. При дальнейшем исследовании я обнаружил, что в доступе отказано из-за того, что папка не существует. Я обнаружил, что иногда в моем приложении возникает ошибка, а иногда нет. - person IbrarMumtaz; 18.11.2009
comment
@Ibrar, вы всегда проверяете e.Error внутри обработчика событий RunWorkerCompleted? Убедитесь, что вы проверили это, прежде чем что-либо делать. Все исключения, которые можно уловить, будут там указаны. - person Judah Gabriel Himango; 18.11.2009
comment
ты за ответ. Я последовал твоему примеру и внес поправки ... пока все хорошо. Не упал, я добавил оператор else, который вызывает настраиваемое событие и регистрирует ошибку в моем классе регистратора. Разрешить исключение обрабатываться, а не необработаться. Надеюсь, это последнее, что я вижу. - person IbrarMumtaz; 18.11.2009
comment
@ Джуда: спасибо за это. Вы сэкономили мне много времени. Это пахнет магией, и я действительно хочу, чтобы .NET генерировал более явное исключение (свойство Result может быть недоступно, если исключение было сгенерировано BackgroundWorker DoWork), вместо того, чтобы пытаться делать правильные вещи и просто сбивать с толку. - person Michael Petrotta; 26.08.2010
comment
Есть ли способ передать исключение, возникающее в фоновом потоке, обратно в основной вызывающий поток? - person user20358; 09.03.2015
comment
У меня есть вопрос по этому поводу: стоит ли вам вообще использовать операторы try - catch в подпрограмме обработчика DoWork Handler фонового рабочего? Разве вы не должны всегда просто ловить любую ошибку в RunWorkerCompleted Handler? - person Neal Davis; 14.11.2017
comment
Если это имеет смысл для вашей модели, конечно. Могут быть моменты, когда вы хотите попробовать / поймать в обработчике DoWork (например, повторить какое-то действие), но в большинстве случаев у вас не будет никаких попыток / улова в обработчике DoWork, и вместо этого вы будете иметь дело с ошибками в RunWorkerCompleted . - person Judah Gabriel Himango; 16.11.2017

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

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

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

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

person Mark Cranness    schedule 09.03.2011
comment
Спасибо за это. Я действительно хотел бы знать, почему он так себя ведет. Делает невозможным отладку обработки исключения RunWorkerCompleted события. - person yu_ominae; 03.09.2013
comment
Если бы они просто упомянули об отладчике в документации ... Так и есть. Спасибо. - person andersop; 03.05.2014
comment
@yu_ominae: Не исключено. Каждый раз, когда отладчик прерывает работу исключения, вы можете фактически нажать F5 (Продолжить), чтобы позволить BackgroundWorker перехватить исключение, и выполнение продолжится, как ожидалось в RunWorkerCompleted. - person Anders Carstensen; 29.12.2014
comment
Марк - вам действительно стоит подумать об обновлении своего ответа, чтобы включить комментарий Андерса. - person DavidRR; 04.03.2016

[Редактировать]

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

[/Редактировать]

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

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 в BackgroundException.Form1.worker_DoWork (отправитель объекта, DoWorkEventArgs e) в D: \ Workspaces \ Sandbox \ BackgroundException \ BackgroundException \ Form1.cs: строка 43 в System.ComponentModel.BackgroundWorker.OnDoWork (DoWorkEventArgs e) в System .ComponentModel.BackgroundWorker.WorkerThreadStart (аргумент объекта)

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

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

person Bobby Cannon    schedule 25.06.2009

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

Ответ Джуды правильный, но это не единственная причина, по которой может появиться диалоговое окно «Необработанное исключение в пользовательском коде». Если исключение генерируется из конструктора в фоновом потоке, то это исключение немедленно вызовет диалог и не будет передано в событие RunWorkerCompleted. Если вы переместите вредоносный код за пределы каких-либо конструкторов (в любой другой метод), он будет работать должным образом.

person Rich    schedule 20.03.2012
comment
Я попытался выбросить исключение в конструкторе, и RunWorkerCompleted его поймал. Возможно, вы пробовали это с консольным приложением? Согласно этому ответу, консольные приложения ведут себя иначе. - person ispiro; 29.08.2014
comment
Возможно. Конкретные исключения, с которыми у меня возникла эта проблема, давали мне Инициализатор типа для xyz выдавал ошибки исключения, я не уверен, какие конкретные обстоятельства могут вызвать это и т. Д. - person Rich; 03.09.2014
comment
Старый поток, но вспомнил остальную часть этой истории - это исключение TypeInitializationException, о котором я думал, и оно выдается, если исключение возникает внутри конструктора static. - person Rich; 19.09.2014

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

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

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

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

person Cavaleiro    schedule 26.04.2015
comment
Этот предыдущий ответ касается того, что происходит в отладчике Visual Studio. - person DavidRR; 04.03.2016