So lets say I happen to have an unhandled exception in my application or it crashes for some reason. Is there some way for me to capture the output and show an error report dial
The answers involving the AppDomain.UnhandledException event probably make an implicit assumption any unhandled exception is raised on WPF's UI thread. This means that, while they'll often work, they're not formally safe for use with operations on other threads. A more reliable option is Application.DispatcherUnhandledException, which WPF provides for applications to implement custom reporting of unhandled exceptions across the main UI thread, background UI threads, and BackgroundWorker instances.
A likely failure point with AppDomain.UnhandledException
is the report dialog probably requires a single-threaded apartment (STA) since both WPF and Windows Forms are STA. As thread pool threads default to multithreaded apartments, an unhandled exception from an asynchronous operation under Task.Run(), ThreadPool.QueueUserWorkItem(), IProgress
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()
My experience of Application.DispatcherUnhandledException
is it's robust in combination with the TPL as Task
and its associated classes facilitate propagation of exceptions back to the caller. To summarize the TPL's exception handling, Wait()
and await
rethrow automatically and callers using other synchronization methods should check Task.Exception.
However, as Application.DispatcherUnhandledException
's documentation points out, other cases require WPF's caller to implement exception propagation. Perhaps the most common of these is WPF's ProgressIProgress
even though its sole purpose is moving progress information from worker threads back to the UI. One workaround is to wrap the progress update handler using an approach similar to the example below. This is just a sketch; more frequent polling of the Exception
property may be valuable in halting on errors, the stronger semantics of IDisposable may be preferable to End()
, and it might be useful to handle cases where a backlog of updates fail concurrently differently.
public class ExceptionPropagatingProgress
{
private readonly Action onProgressUpdateCore;
private readonly IProgress progress;
public Exception Exception { get; private set; }
public ExceptionPropagatingProgress(Action handler)
{
this.Exception = null;
this.onProgressUpdateCore = handler ?? throw new ArgumentNullException(nameof(handler));
this.progress = new Progress(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);
}
}