Ответы, связанные с событием AppDomain.UnhandledException
, вероятно, делают неявное предполагается, что любое необработанное исключение возникает в потоке пользовательского интерфейса WPF. Это означает, что хотя они часто работают, формально они небезопасны для использования с операциями в других потоках. Более надежным вариантом является Application.DispatcherUnhandledException
, который WPF позволяет приложениям реализовывать настраиваемые отчеты о необработанных исключениях в основном потоке пользовательского интерфейса, фоновых потоках пользовательского интерфейса и BackgroundWorker
экземпляров.
Вероятная точка отказа с AppDomain.UnhandledException
— диалоговое окно отчета, вероятно, требует однопоточного подразделения (STA), поскольку и WPF, и Windows Forms являются STA. Поскольку потоки пула потоков по умолчанию являются многопоточными подразделениями, необработанное исключение из асинхронной операции в разделе Task.Run()
, ThreadPool.QueueUserWorkItem()
, IProgress<T>.Report()
или многие подобные API приведут к сбою создания экземпляра диалогового окна отчета с чем-то вроде исключения ниже. Это вызывает сбой приложения без возможности предложить пользователю сообщить об основной проблеме.
System.InvalidOperationException: The calling thread must be STA, because many UI components require this.
at System.Windows.Input.InputManager..ctor()
at System.Windows.Input.InputManager.GetCurrentInputManagerImpl()
at System.Windows.Input.KeyboardNavigation..ctor()
at System.Windows.FrameworkElement.FrameworkServices..ctor()
at System.Windows.FrameworkElement.EnsureFrameworkServices()
at System.Windows.FrameworkElement..ctor()
at System.Windows.Controls.Control..ctor()
at System.Windows.Window..ctor()
По моему опыту, Application.DispatcherUnhandledException
хорошо сочетается с TPL as Task
и связанные с ним классы облегчают передачу исключений обратно вызывающей стороне. Подводя итог обработка исключений TPL, Wait()
и await
перебрасываются автоматически, и вызывающие абоненты, использующие другие методы синхронизации, должны проверить Task.Exception
.
Однако, как указано в документации Application.DispatcherUnhandledException
, в других случаях требуется, чтобы вызывающая сторона WPF реализовывала распространение исключений. Возможно, наиболее распространенным из них является Progress<T>
WPF, который странно отсутствует поддержка распространения исключений из реализации IProgress<T>.Report()
, хотя его единственной целью является перемещение информации о ходе выполнения из рабочих потоков обратно в пользовательский интерфейс. Один из способов обхода — обернуть обработчик обновления хода выполнения, используя подход, аналогичный приведенному ниже примеру. Это всего лишь набросок; более частый опрос свойства Exception
может быть полезен для остановки при ошибках, более строгая семантика IDisposable
может быть предпочтительнее End()
, и может быть полезно по-разному обрабатывать случаи, когда невыполненные обновления одновременно терпят неудачу.
public class ExceptionPropagatingProgress<TProgress>
{
private readonly Action<TProgress> onProgressUpdateCore;
private readonly IProgress<TProgress> progress;
public Exception Exception { get; private set; }
public ExceptionPropagatingProgress(Action<TProgress> handler)
{
this.Exception = null;
this.onProgressUpdateCore = handler ?? throw new ArgumentNullException(nameof(handler));
this.progress = new Progress<TProgress>(this.OnProgressUpdate);
}
public void End()
{
if (this.Exception != null)
{
throw new AggregateException(this.Exception);
}
}
private void OnProgressUpdate(TProgress value)
{
try
{
this.onProgressUpdateCore(value);
}
catch (Exception exception)
{
lock (this.onProgressUpdateCore)
{
if (this.Exception == null)
{
this.Exception = exception;
}
else
{
this.Exception = new AggregateException(this.Exception, exception);
}
}
}
}
public void QueueProgressUpdate(TProgress value)
{
if (this.Exception != null)
{
throw new AggregateException(this.Exception);
}
this.progress.Report(value);
}
}
person
Todd West
schedule
27.02.2019