I'm trying to create a simple global exception handler in a WPF application that is build with the MVVM Light Toolkit, but I'm having a hard time making it work.
The thing is that an exception risen in a view model will not be caught in the App's UnhandledException handler, even though I register a listener for both the Dispatcher and the AppDomain like this:
private void Application_Startup(object sender, StartupEventArgs e)
{
AppDomain.CurrentDomain.UnhandledException += DomainUnhandledException;
DispatcherUnhandledException += App_DispatcherUnhandledException;
}
private void DomainUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs)
{
var exception = unhandledExceptionEventArgs.ExceptionObject as Exception;
ShowExceptionMessage(exception);
}
private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
ShowExceptionMessage(e.Exception);
e.Handled = true;
}
I found this blog post that describes the problem spot on, with the solution described with this code snipped for view models:
// Throw the exception in the UI thread.
App.Current.RootVisual.Dispatcher.BeginInvoke(() => { throw new MyException(); });
However, I would like all exceptions to bubble up to the global exception handler, not only the ones that I throw myself in the VM.
So the question is: Is it somehow possible to re-throw exceptions from other threads into the UI thread in one place?
Update: Added more detailed code for the App's event handler setup.
I think I figure this out now.
The problem is due to the fact that WPF suppresses exceptions thrown in a view's databinding, and because my view model is databound to the view's DataContext (through a property in my ViewModelLocator utilizing unity dependency injector) any exceptions in the construction of the view model will be swallowed.
See this SO question for more info.
So I suppose I just have to make sure that nothing important for the application's ability to function correctly should happen in the constructor.
The “global” exception handling events for WPF and Windows Forms applications (Application.DispatcherUnhandledException and Application.ThreadException) fire only for exceptions thrown on the main UI thread. You still must handle exceptions on worker threads manually.
AppDomain.CurrentDomain.UnhandledException fires on any unhandled exception, but provides no means of preventing the application from shutting down afterward.
Maybe check also Thread Pooling “divide-and-conquer” style.
TPL
How do I invoke a method on the UI thread when using the TPL?
Related
OK - so I have a legacy MC++ app that calls WPF views like they are dialogs. I have an abstract ViewModel parent class. Is there a way that I can add UnhandledException handling to my ViewModel so that any exceptions thrown within the ViewModel or it's child implementations can be handled there before propagating out to the MC++ app?
I don't really have an architectural framework to work with. All the ViewModels and Views implemented to this point have been one-offs :(
You should be able to add exception handling to the method that opens and shows the View as a dialog (ie: wrap the Window.ShowDialog() call). This should catch any exceptions thrown from within your View or ViewModel, as they're all "launched" from that point.
If you have access to a Dispatcher, you can tie into the Dispatcher.UnhandledException event, otherwise you can tie into the AppDomain.UnhandledException event:
Dispatcher.CurrentDispatcher.UnhandledException += HandleDispatcherException;
someDispatcher.UnhandledException += HandleDispatcherException;
AppDomain.CurrentDomain.UnhandledException += HandleAppDomainException;
How may I ensure that my childwindow is unloaded when it's closed?
I am opening the childwindow from my viewmodel, but after it's been closed it still fires of events like selectionchanged on comboboxes.
The childwindow is using the same viewmodel as it's been called from, so I guess that explains why the events are being fired. The itemssources are still valid.
But when it's closed, I would like to "dispose" the childwindow for good.
I've tried to add a Closed handler like this (Default view code behind):
private void OnLaunchEditItem(ItemMessage msg)
{
var editWnd = new EditItemWindow();
editWnd.Closed += new EventHandler(editWnd_Closed);
editWnd.Show();
}
void editWnd_Closed(object sender, EventArgs e)
{
sender = null;
}
No sucesss..
So what I'm doing now is to remove the itemssource from the childwindow controls, which seems to me... not the ideal solution to the problem. It must be possible to dispose it all from memory on closing? (Childwindow "view" code-behind)
private void OKButton_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
combobox1.ItemsSource = null;
combobox2.ItemsSource = null;
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = false;
combobox1.ItemsSource = null;
combobox2.ItemsSource = null;
}
The messaging has a known problem that it introduces a hard link between the messenger and the recipient of a message. So if you use messaging you havee to ensure that the Messenger.Unregister method is called. In other words, when you call Register to handle a messsage make sure you call Unregister as well!
So in your view you have to register for the Unloaded event; there you then call Messenger.Unregiser(this); where this is your view.
In ViewModels you have to make sure that the Cleanup method is called to deregister the ViewModel as a message recipient.
Also see:
MVVM Light Listener not releasing / deterministic finalization for registered object? and MVVM Light Messenger executing multiple times.
Laurent is aware of this Problem but - as of now - has no solution.
Sharing ViewModels between views can lead to problems like this. That's why it's rarely done.
A ViewModel should generally not be concerned with navigation because in an ideal world it shouldn't even know what kind of view it is bound to. This includes spawing child views (ChildWindows).
I would recommend two changes to you. The first one is to create a dedicated viewmodel for your dialog. And second to decouple the navigation from the viewmodel by delegating navigation to a Controller. A controller in MVVM is usually a singleton object who's whole purpose is opening windows, dialogs etc. This can be implemented using the Event Aggregator pattern in a quite elegant fashion.
A WPF window dialog is shown using the ShowDialog method in the Window class like when a button is pressed on the main window, like this.
private void button1_Click(object sender, RoutedEventArgs e)
{
try
{
var window = new Window1();
window.ShowDialog();
}
catch (ApplicationException ex)
{
MessageBox.Show("I am not shown.");
}
}
The window has a Loaded event subscribed in the xaml like this:
<Window x:Class="Stackoverflow.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Loaded="Window_Loaded">
<Grid />
</Window>
An exception is thrown in the Window_Loaded event
private void Window_Loaded(object sender, RoutedEventArgs e)
{
throw new ApplicationException();
}
However the exception is not catched by the catch around the ShowDialog call, nor does the call return. The exception is swallowed and the window still displayed.
Why does this happen and how would I go about handling an exception in the Window_Loaded event of a WPF window? Do I have to catch it in the event-handler and Dispose the window manually?
In WinForms you need to call Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException)
in order to let exceptions bubble through ShowDialog calls. Is there a similar switch that needs to be set on WPF?
I've only seen this issue on x64 machines, with code compiled with Any Cpu.
Changing your program to compile as x84 may fix it, but I've had problems there myself depending on our assemblies.
My only code suggestion is the following, and even then it's not guaranteed to pick it up.
Catch the exception, and re-throw it in a Background worker.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
/// your code here...
throw new ApplicationException();
/// your code here...
}
catch (Exception ex)
{
if (IntPtr.Size == 8) // 64bit machines are unable to properly throw the errors during a Page_Loaded event.
{
BackgroundWorker loaderExceptionWorker = new BackgroundWorker();
loaderExceptionWorker.DoWork += ((exceptionWorkerSender, runWorkerCompletedEventArgs) => { runWorkerCompletedEventArgs.Result = runWorkerCompletedEventArgs.Argument; });
loaderExceptionWorker.RunWorkerCompleted += ((exceptionWorkerSender, runWorkerCompletedEventArgs) => { throw (Exception)runWorkerCompletedEventArgs.Result; });
loaderExceptionWorker.RunWorkerAsync(ex);
}
else
throw;
}
}
The "Why" is explained here: http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/
In short, the exception cannot be propagated in 64-bit operating systems because there is a transition between user and kernel mode.
The test of IntPtr.Size in #midspace answer is not adequate because IntPtr.Size will be equal to 4 in an x86 process running on a x64 os (you need to use Environment.Is64BitOperatingSystem instead on .NET 4 and greater).
The solution now: use another event like ContentRendered which is called after the Loaded one or put your code in the window constructor.
Never use Loaded (or OnLoad in Winforms), because if there is an exception there, you don't know what can happens.
Have a look also at this answer: https://stackoverflow.com/a/4934010/200443
I also reconstructed your answer in Visual Studio 2010 in a blank WPF 3.5 project.
The project behaved as expected, i.e. the Window_Loaded threw the exception, and it was caught by the button click event.
So I'm not sure why yours isn't working, maybe try posting your App.xml.cs and any other code you haven't shown here?
In the meantime, I thought I would point out a few things:
WPF does indeed handle "uncaught" exceptions a little differently from WinForms. Try looking at this for a starter:
http://msdn2.microsoft.com/en-us/library/system.windows.application.dispatcherunhandledexception.aspx
It looks as if there isn't an exact equivalent in WPF for the SetUnhandledExceptionMode method (See http://social.msdn.microsoft.com/forums/en-US/wpf/thread/955c75f8-9cd9-4158-bed9-544bd7946413). Try their advice about registering a handler and see if that helps you?
I would recommend stepping through your code - set a breakpoint in the Window_Loaded, and see what happens - pay careful attention to the call stack.
Good luck!
Researching this a bit more I found this peculiar blogg entry describing a similar problem.
http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/
As it turns out it might be a 64-bit processor architecture problem. Who would have guessed?! It might explain why some people could not reproduce the issue my simple example. I tried to compile my example in "Any CPU", x64 and x86 but to no avail. On x64 the whole thing actually blew up completely crashing with a windows crash dialog.
So I guess this is the answer without verifying it on a 32-bit machine.
I've had similar problem, and trying to understand where it's coming is frustrating. There's a couple of things that could be causing a problem.
Are you calling anything before the method call InitializeComponent(); ? Previously I've had a method calling xaml elements, which haven't been initialised yet.
Have you tried pressing F10 to start the application, using Step Over will let you see the exact process start up?
Have you checked your naming? XAML can swallow errors such as methods being mis-spelt, and then an exception is thrown on run time, this particularly true DataSet providers. You'd be surprised what you can get away with.
Catching an exception for opening a form is pretty fundamental, I'd rather look at any dependents (Data or XAML bindings) and deal with those first.
Hope those points help.......
I was wondering if there's a way to watch all RoutedEvents that are raised in a WPF application. A way to write some info about the events fired to the console would be prefect to see what's going on.
I've found another way:
I've added this to the loaded handler of my UserControl.
var events = EventManager.GetRoutedEvents();
foreach (var routedEvent in events)
{
EventManager.RegisterClassHandler(typeof(myUserControl),
routedEvent,
new RoutedEventHandler(handler));
}
and this is the handler method:
internal static void handler(object sender, RoutedEventArgs e)
{
if (e.RoutedEvent.ToString() != "CommandManager.PreviewCanExecute" &&
e.RoutedEvent.ToString() != "CommandManager.CanExecute")
Console.WriteLine(e.OriginalSource+"=>"+e.RoutedEvent);
}
The CanExecute events are a bit too much in my case. If you would like to see these too, just remove the if statement.
Yes, but it requires some reflection. You're better off using a tool like Snoop that already does the hard lifting for you.
In the tab Events you can see list of events, and the element that handled it.
Issue:
Our application crashes when a user type some text into the WPF TextBox.
Please help! The easy 3 minutes reproduction appears below
Reproduction:
Register a WPF TextBox (m_textBox) TextChanged event to the method below
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
// This line of code throws Unhandled exception when typing '^' char in
// the text box (in United "States-International" keyboard)
m_textBox.Dispatcher.Invoke(DispatcherPriority.Normal, new SendOrPostCallback(Foo), null);
}
private void Foo(object state)
{
//Do nothing
}
Change your keyboard to "United States - International"
Run the application and type '^' twice in the TextBox
Aplication will crash!!!!
The problem is the composition code doesn't expect to get additional input events until your TextChanged handler returns. When you call Dispatcher.Invoke, it causes the Windows message queue to be processed causing additional input events.
In my opinion this is a bug because the composition engine should be robust against re-entrancy when firing user-specified events.
I could reproduce this error. Typing ^q or ~~ will also cause the crash.
The exception that is thrown states that composition has already been completed. I managed to work around it by using the async BeginInvoke instead of Invoke. Will that work for you?
m_textBox.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new SendOrPostCallback(Foo), null);