Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF ShowDialog swallowing exceptions during window load

Tags:

exception

wpf

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?

like image 314
vidstige Avatar asked Jan 26 '11 16:01

vidstige


2 Answers

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;
    }
}
like image 181
midspace Avatar answered Sep 19 '22 21:09

midspace


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

like image 31
Maxence Avatar answered Sep 17 '22 21:09

Maxence