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?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With