Application.Current is null, but new Application() still fails?
Asked Answered
C

3

6

I have an unmanaged application which uses a WPF assembly for some of its user interface. Because of this arrangement Application.Current is not created automatically. So when the first WPF window is loaded, my code does this:

        if (System.Windows.Application.Current == null)
        {
            new System.Windows.Application();
        }

This works the first time and is the approach I've seen recommended.

But if the user closes the (only) WPF window, and later loads it again, even though Current == null again an exception is thrown when the Application ctor is called.

It is clear in the documentation that you can only have one Application per AppDomain - but why then is Current null and yet I can't create it?


The exception that is thrown is of type InvalidOperationException and has the message:

Cannot create more than one System.Windows.Application instance in the same AppDomain.

Its InnerException is null.


To work around this I have tried to:

  • Construct Application using ShutdownMode = ShutdownMode.OnLastWindowClose

  • Explicitly call Current.Shutdown() when the WPF window is closed

but neither has made any difference.

Is there some other way to manually manage the lifetime of the Current object? Or should I instead attempt to create it when the unmanaged application starts, and then rely on it always being set for the lifetime of the process?

Coupling answered 11/2, 2020 at 14:31 Comment(6)
What is the Exception, and any InnerException?Herrenvolk
@Herrenvolk good point, I will check & add that...Coupling
@Herrenvolk addedCoupling
Can you create a small working example? There are may variables such as bitness/apartment threading etc...which may have to be considered.Pettit
@ΩmegaMan Probably yes, I will see if I can strip it down. Its a 32bit unmanaged calling application, and there are no threads being manually created.Coupling
I digged into a similar situation and i had to create a new AppDomain for each Application i had to start.Antilepton
T
6

The documentation you linked states the following in its remarks section:

Only one instance of the Application class can be created per AppDomain, to ensure shared access to a single set of application-scope window, property, and resource data. Consequently, the parameterless constructor of the Application class detects whether the instance being initialized is the first instance in an AppDomain; if it is not, an InvalidOperationException is thrown.

The part I highlighted implies that it is not checking if it is the only / single application currently running, but rather that it checks if any another Application instance has been initialized before (regardless of whether or not it has been closed yet).

Taking a look at the source code of the Application class confirms this: The Application class internally uses a static flag (_appCreatedInThisAppDomain) that is set only once when initializing the first Application instance. But apparently this flag is never reset, which prevents you from creating any more Application instances within the same AppDomain.

Two answered 11/2, 2020 at 15:8 Comment(2)
Thanks for digging that up, I didn't know the internals. So it seems odd to me then that the .Current property gets reset to null...Coupling
Yup, .Current returns _appInstance which is set to null in DoShutdown().Coupling
H
3

This is easy in WinForms, not so much in WPF.

Obviously, we don't have an Application problem, we have an AppDomain problem.

I put a reasonable amount of effort into this, but couldn't get it to behave as I wanted it to, that is to destroy the old then recreate an AppDomain on a new Thread when the spacebar is pressed, I suppose that makes sense though given the scope.

It's a work around at best, and may not even be an option in your situation.

Is there some other way to manually manage the lifetime of the Current object?

As best I can tell, the simple answer is to just maintain a WPF message loop Thread for the life of the program (via ShutdownMode.OnExplicitShutdown), and use the Application.Current.Dispatcher to display WPF objects as needed.

Here's an example of what I mean, as implemented in a managed console application:

class Program
{
    static void Main(string[] args)
    {
        Thread t = CreateThread();

        t.Start();

        bool quit = false;
        while (!quit)
        {
            switch(Console.ReadKey().Key)
            {
                case ConsoleKey.Escape:
                    Application.Current.Dispatcher.Invoke(() => Application.Current.Shutdown());
                    quit = true;
                    break;
                case ConsoleKey.W:
                    Application.Current.Dispatcher.Invoke(() =>
                    {
                        var w = new Window() { Width = 500, Height = 500, Title = "WPF Window" };
                        w.Show();
                    });
                    break;
                case ConsoleKey.D:
                    Application.Current.Dispatcher.Invoke(() =>
                    {
                        var d = new Window() { Width = 500, Height = 500, Title = "WPF Dialog" };
                        d.ShowDialog();
                    });
                    break;
                case ConsoleKey.Spacebar:
                    //// Nope!
                    //Application.Current.Dispatcher.Invoke(() => Application.Current.Shutdown());
                    //t = CreateThread();
                    //t.Start();
                    break;
            }
        };
    }

    static Thread CreateThread()
    {
        var t = new Thread(() =>
        {
            if (System.Windows.Application.Current == null)
            {
                new System.Windows.Application();

                Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
            }

            Application.Current.Run();
        });
        t.SetApartmentState(ApartmentState.STA);

        return t;
    }
}

You'll need references to PresentationCore, PresentationFramework and WindowsBase to build this example.

I hope it at least inspires someone.

EDIT: FYI, this may not work anymore... It worked when I posted it, now two days later it does not. There was a cumulative update for .NET Framework (kb4538122) installed yesterday, but I'm not sure if this was the breaking change or not.

EDIT: I updated the code, it now works again.

Herrenvolk answered 11/2, 2020 at 16:58 Comment(0)
S
0

I had the same problem when I wanted to instantiate new Application for testing purposes. In code I wanted to cover by unit tests, some methods where using Application.Current and in some test cases I expect it to be null, in some other not.

Since .NET6 creating domains is obsolete.

My workaround for it is to set guard's flag to false.

// Create Application
_ = new Application();

// Delete Application
if (Application.Current is not null)
{
    Application.Current.Shutdown();
    // make sure Dispatcher's queue is empty,
    // otherwise Application.Current will not be set to null
    FieldInfo appCreatedInThisAppDomain = typeof(Application)
        .GetField("_appCreatedInThisAppDomain",
                  BindingFlags.Static | Bindingflags.NonPublic)!;
    appCreatedInThisAppDomain.SetValue(null, false);
}

// Create new Application
Assert.That(Application.Current, Is.Null);
_ = new Application(); // should not throw

Please note that it is hacky solution and should not be used somewhere else then in tests projects!

Styria answered 9/9, 2024 at 11:19 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.