Я столкнулся со странным поведением BackgroundWorker
или .NET Framework при сбросе исключения в событии RunWorkerCompleted
.
Исключения, возникающие в фоновом потоке, передаются в RunWorkerCompleted
. Там я делаю object temp = e.Result
, чтобы отбросить исключение. Поскольку это должно происходить в основном потоке пользовательского интерфейса, я ожидаю, что исключение будет распространено до Application.Run(...)
, которое я окружил блоком try-catch.
При запуске приложения в Visual Studio это работает нормально. Тем не менее, когда я выполняю exe-файл вне Visual Studio, исключение не обрабатывается и вызывает сбой приложения.
Возможно ли, что RunWorkerCompleted
событие не выполняется в основном потоке пользовательского интерфейса?
Эти сообщения не являются дубликатами, а скорее подтверждают, что моя ситуация странная:
Это очень упрощенный пример кода, который воспроизводит ошибку: создайте проект WinForms и добавьте:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
try
{
Form form = new Form();
form.Load += delegate
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (sender, e) =>
{
// do nothing
};
bw.RunWorkerCompleted += (sender, e) =>
{
throw new Exception("Booo!");
};
bw.RunWorkerAsync();
};
Application.Run(form);
}
catch (Exception ex)
{
while (ex is TargetInvocationException && ex.InnerException != null)
ex = ex.InnerException;
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Тогда попробуйте:
- Запустите его в Visual Studio. Перед закрытием приложения вы получите дружественное окно с сообщением.
- Запустите скомпилированный исполняемый файл вне Visual Studio. Вы получите сообщение .NET Framework «необработанное исключение».
Интересная часть заключается в том, что если я нажму «продолжить» в 2, приложение будет сохранено, форма будет отображаться и принимать пользовательский ввод. Это должно означать, что вместо основного потока пользовательского интерфейса произошел сбой фонового потока. Основной поток остается живым.
(Почему я хочу делать все эти вещи? Я отображаю заставку, и хотя некоторые запуска приложения выполняются в фоновом режиме, заставка отображает прогресс с помощью метки и индикатора выполнения. Итак, у меня есть создать и отобразить заставку, а затем захватить основной поток пользовательского интерфейса для запуска BackgroundWorker
. В исходном коде он сообщает о ходе выполнения обратно в основной поток, который обновляет форму заставки. Если во время запуска возникает исключение, я хочу поймать это. В некоторых случаях они являются «бизнес-исключениями», которые имеют конкретное значение, например «вы не авторизованы для использования этого приложения». В этих случаях я отображаю дружественное окно сообщения, прежде чем позволить приложению умереть. Блок 1045 * для очистки ресурсов. Я не уверен, выполняет ли диалоговое окно «Необработанное исключение» .NET Framework блок finally, прежде чем убить приложение.)