Необработанные исключения в 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?
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, вы не будете получать исключения во время выполнения.
Я бы добавил к тексту MSDN:
Если операция вызывает исключение, которое ваш код не обрабатывает, BackgroundWorker перехватывает исключение и передает его в обработчик события RunWorkerCompleted, где оно отображается как свойство Error System.ComponentModel..::.RunWorkerCompletedEventArgs. Если вы работаете в отладчике Visual Studio, он прервется в той точке обработчика событий DoWork, где возникло необработанное исключение.
... И отладчик сообщит об исключении как "~ Исключение не было обработано кодом пользователя"
Решение: не запускайте под отладчиком, и он работает, как и ожидалось: исключительная ситуация в e.Error.
Это старый вопрос, но я нашел его, пока гуглил те же симптомы. Размещать это на тот случай, если кто-то другой найдет его по той же причине.
Ответ Иуды верен, но это не единственная причина, по которой может появиться диалоговое окно "необработанное исключение в коде пользователя". Если исключение выдается из конструктора в фоновом потоке, то это исключение немедленно вызовет диалог и не будет передано событию RunWorkerCompleted. Если вы переместите нарушающий код за пределы каких-либо конструкторов (в любой другой метод), он будет работать как положено.
[Редактировать]
Иуда имеет большое значение. В моем примере указана специфика обработки ошибки, но мой код фактически вызовет другое исключение, если исключение никогда не было достигнуто в методе DoWork. Этот пример в порядке из-за того, что мы специально показываем возможности обработки ошибок в BackgroundWorker. Однако, если вы не проверяете параметр ошибки на ноль, то это может быть вашей проблемой.
[/Редактировать]
Я не вижу таких же результатов. Можете ли вы опубликовать небольшой код? Вот мой код
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(аргумент объекта)
Интересная статья, похожая на ваш вопрос. Есть раздел по обработке исключений.
У меня была та же самая проблема, и я уже применял ответ Иуды прежде, чем я нашел эту тему после некоторого поиска в Google.
Ну, я думаю, что ответ Иуды частично верен. Я нашел лучший ответ здесь
Отладчик хорошо работает, если вы запускаете приложение в "реальных условиях", RunWorkerCompleted обрабатывает исключение, как и ожидалось, и поведение приложения также ожидается.
Я надеюсь, что этот ответ поможет.