Reputation: 548
When I await
on a method that throws an exception, try/catch do not save application from crashing.
There is a throwing method
void CurrentStep.Process(CancellationToken cancellationToken)
{
throw new Exception();
}
It is called from the UI thread by the means of ICommand.Execute()
ProcessCurrentStepCommand = AsyncCommandFactory.Create(async cancellationToken =>
{
try
{
await Task.Run(() => CurrentStep.Process(cancellationToken));
}
catch {}
CurrentStep = CurrentStep.NextStepViewModel;
});
ProcessCurrentStepCommand
is bound to the button on UI. When I click the button, my application breaks.
I feel that there is a general problem with throwing an exception on UI thread, but at the same time I don't understand why catch block isn't saving me from exception.
I have found the only way that works for me now:
await Task.Factory.StartNew(
action: () => CurrentStep.Process(cancellationToken),
creationOptions: TaskCreationOptions.LongRunning);
But it looks ugly. If I forget in the future what I wanted to do with this code I might think that I need to clean it and get into trouble with some exception knocking out the whole application.
When in debug mode, everything behaves nicely.
Call stack:
UI.exe!UI.Steps.ViewModels.SvmConnectionViewModel.Process(System.Threading.CancellationToken cancellationToken)
UI.exe!UI.MainViewModel..ctor.AnonymousMethod__1() Line 18 mscorlib.dll!System.Threading.Tasks.Task.InnerInvoke() Line 2911 mscorlib.dll!System.Threading.Tasks.Task.Execute() Line 2523 mscorlib.dll!System.Threading.Tasks.Task.ExecutionContextCallback(object obj) Line 2888 mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 581
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 531
mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot) Line 2853 mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution) Line 2792 mscorlib.dll!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() Line 2729
mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() Line 830 mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() Line 1171
NotImplementedException occured: A first chance exception of type 'System.NotImplementedException' occurred in mscorlib.dll
.
Call stack:
mscorlib.dll!System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task task) Line 180
mscorlib.dll!System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task task) Line 170
mscorlib.dll!System.Runtime.CompilerServices.TaskAwaiter.GetResult() Line 125
UI.exe!UI.MainViewModel..ctor(System.Threading.CancellationToken cancellationToken) Line 18
[Resuming Async Method]
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine) Line 1065
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 581
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 531
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run() Line 1045
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.OutputAsyncCausalityEvents>.AnonymousMethod__0() Line 973
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.ContinuationWrapper.Invoke() Line 1085
mscorlib.dll!System.Runtime.CompilerServices.TaskAwaiter.OutputWaitEtwEvents.AnonymousMethod__0() Line 301
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.ContinuationWrapper.Invoke() Line 1085
mscorlib.dll!System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation.GetActionLogDelegate.AnonymousMethod__3() Line 470
mscorlib.dll!System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation..cctor.AnonymousMethod__6(object state) Line 393
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) Line 118 Unknown
WindowsBase.dll!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(object source, System.Delegate method, object args, int numArgs, System.Delegate catchHandler) Line 41 Unknown
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl() Line 583 Unknown
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(object state) Line 528 Unknown
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 581
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 531
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Line 520
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke() Line 441 Unknown
WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue() Line 2227 Unknown
WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) Line 2480 Unknown
WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) Line 345 Unknown
WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) Line 494 Unknown WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) Line 111 Unknown WindowsBase.dll!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(object source, System.Delegate method, object args, int numArgs, System.Delegate catchHandler) Line 41 Unknown
WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs) Line 1447 Unknown WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam) Line 398 Unknown
[Native to Managed Transition]
[Managed to Native Transition]
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame) Line 2281 Unknown
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame) Line 369 Unknown
WindowsBase.dll!System.Windows.Threading.Dispatcher.Run() Line 328 Unknown
PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore) Line 2745
PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window) Line 1841 PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window window) Line 261 PresentationFramework.dll!System.Windows.Application.Run() Line 222 UI.exe!UI.App.Main() [Native to Managed Transition]
mscorlib.dll!System.AppDomain.ExecuteAssembly(string assemblyFile, System.Security.Policy.Evidence assemblySecurity, string[] args) Line 2031
Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() Unknown mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(object state) Line 74
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 581
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 531
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Line 520
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() Line 111
[Async Call]
UI.exe!UI.Commands.AsyncCommandFactory.Create(System.Threading.CancellationToken token) Line 27
[Async Call]
UI.exe!UI.NotifyTaskCompletion.WatchTaskAsync(System.Threading.Tasks.Task task) Line 66
[Async Call]
UI.exe!UI.Commands.AsyncCommand.ExecuteAsync(object parameter) Line 55
[Async Call]
UI.exe!UI.Commands.AsyncCommandBase.Execute(object parameter) Line 15
Call stack:
mscorlib.dll!System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task task) Line 180
mscorlib.dll!System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task task) Line 170
mscorlib.dll!System.Runtime.CompilerServices.TaskAwaiter.GetResult() Line 125
UI.exe!UI.Commands.AsyncCommandFactory.Create(System.Threading.CancellationToken token) Line 27
[Resuming Async Method]
...
Call stack:
mscorlib.dll!System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task task) Line 180
mscorlib.dll!System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task task) Line 170
mscorlib.dll!System.Runtime.CompilerServices.TaskAwaiter.GetResult() Line 125
UI.exe!UI.NotifyTaskCompletion.WatchTaskAsync(System.Threading.Tasks.Task task) Line 66
[Resuming Async Method]
...
<Label Content="{Binding ProcessCurrentStepCommand.Execution.ErrorMessage}" Visibility="{Binding ProcessCurrentStepCommand.Execution.IsFaulted, Converter={StaticResource BooleanToVisibilityConverter}}" />
To understand the context of the question and the accepted answer, you will need to look at these articles:
Upvotes: 3
Views: 2301
Reputation: 456322
I don’t want to propagate exceptions directly back to the main UI loop; I want to capture any exceptions and set properties so that the error handling is done via data binding.
In that case, what you really need is a synchronous command that just starts the asynchronous operation, where the asynchronous operation is represented using NotifyTaskCompletion
(or some similar type you write). Breaking apart the operation like this (into a synchronous "start" and asynchronous data binding) is easier than trying to do it all in one (which is also possible - it's just that the code isn't as short or reusable):
// Represents the execution of the current step.
NotifyTaskCompletion ProcessCurrentStepCommandExecution
{
get { return _processCurrentStepCommandExecution; }
set { _processCurrentStepCommandExecution = value; PropertyChanged(); }
}
...
var cancellationToken = ...; // Wherever you get this from.
ProcessCurrentStepCommand = new DelegateCommand(() =>
{
ProcessCurrentStepCommandExecution = new NotifyTaskCompletion(async () =>
{
await Task.Run(() => CurrentStep.Process(cancellationToken));
// I'm assuming here you only want to move to the next step if there are no errors.
// Otherwise, this should be in a finally block.
CurrentStep = CurrentStep.NextStepViewModel;
});
});
Edit:
I believe you may be running into a bug in the example code for that article (there used to be updated code in the comments before MSDNMag decided to delete them all, and I'm working to get the code sample updated, a surprisingly long process). The bug occurs if the task completes synchronously (with exception or success); in that case, NotifyTaskCompletion<T>.TaskCompleted
will be null
.
To fix this, change the constructor for NotifyTaskCompletion<T>
from this:
{
Task = task;
if (!task.IsCompleted)
TaskCompletion = WatchTaskAsync(task);
}
to this:
{
Task = task;
TaskCompletion = WatchTaskAsync(task);
}
Upvotes: 5