Search code examples
c#windowpersistenceunhandled-exceptionwinui-3

InvalidOperationException when closing a WindowEx window in WinUI 3


Recently, I created a new WinUI 3 (v1.2) desktop app using Template Studio for WinUI. Because starting unpackaged apps in Debug is so much faster than packaged apps, I chose an unpackaged app in the setup wizard. The resulting barebones code created an app that always crashed when closed using the Close method or the Close button on the System Menu.

The app throws an unhandled exception which VS 2022 catches in App.g.i.cs in the following section:

#if DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
    UnhandledException += (sender, e) =>
    {
        if (global::System.Diagnostics.Debugger.IsAttached) global::System.Diagnostics.Debugger.Break();
    };
#endif

You can disable this by setting the DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION variable in the project Properties (under Build -> Conditional compilation symbols) but the program will still throw the error (this code simply gives you a convenient way to trap unexpected errors).

Can anyone explain why this is happening with an unmodified new project and how to correct it, please?


Solution

  • The template code generates a MainWindow that is of the class WindowEx. This nifty extension of the Window class adds quite a few helpful features (see: WinUIEx Wiki) but some of them aren't completely documented yet. The WindowEx class creates a WinUIEx.WindowManager to manage the WindowEx window. When the WindowEx is closed, WinUIEx.WindowManager.Window_Closed() is called which then calls WinUIEx.WindowManager.SavePersistence() which is the cause of the crash mentioned above.

    SavePersistence() tries to save some of the window characteristics so they can be restored when the app restarts. However, the persistence services are only enabled for packaged apps, not unpackaged (which I used). To turn this behavior off, set the PersistenceId property of the WinUIEx.WindowManager instance to either null, string.Empty, or "" (the template sets PersistenceId = "MainWindow" by default). Just add

        var manager = WinUIEx.WindowManager.Get(MainWindow);
        manager.PersistenceId = string.Empty;
    

    somewhere after MainWindow has been instantiated (I put it in App.OnLaunched). I hope this helps.