简体   繁体   中英

WPF cannot close Application instance for running it a second time

I have an Console Application started as [STAThread] .

That application should open a seperate Wpf UI for entering some settings.

The functions for that:

 private static void openUI()
    {
        var application = new System.Windows.Application();

        //referenced project in the same solution
        var ui = new ManagerUI.MainWindow();

        //blocks execution
        application.Run(ui);

        application.Shutdown();
    }

Opening the UI for the first time works as expected. The problem occurs when opening the UI for the second time.

I get an System.InvalidOperationException , saying that I cannot run more than one Application-Instance in the same AppDomain.

For saving ram, it must be closed between the operations.

I also tried to create the System.Windows.Application in the constructor. But as soon as I run the application the second time, I get a very similiar exception. The InitializeComponents() method of the UI throws an System.InvalidOperationException , saying that the Object is going to be terminated.

The StackTraces shows that the error appears when the xaml is parsed, so I conclude it cannot open it, because it is still opened by the first execution.

Neither calling ui.Close() nor calling application.Shutdown() solves the problem ( Environment.Exit() closes everything, including my Console Application).

The ram profiler indicates, not everything was closed correctly because it shows an higher use after the Window was closed, than before it was opened in the firts place.

How do I properly close the Application instance, or how do I re-use it to run an Wpf Application multiple times?

Having looked at the source code for the Application class , it doesn't look like you will be able to work around this, as various static fields are initialized by the class constructor:

public Application()
{
    ...

    lock(_globalLock)
    {
        if (_appCreatedInThisAppDomain == false)
        {
            ...
            _appInstance = this;
            ...
            _appCreatedInThisAppDomain = true;
        }
        else
        {
            throw new InvalidOperationException(...);
        }
    }
}

...

static private object                           _globalLock;
static private bool                             _appCreatedInThisAppDomain;
static private Application                      _appInstance;

...

Basically the constructor sets _appCreatedInThisAppDomain to true, and as that field is private you have no way of setting it back*.

I think the only way of achieving something similar to what you want is to write a separate WPF application, then use the Process class to launch that from your console application. Alternatively, you could theoretically create a separate AppDomain to host your WPF stuff but that would be a lot more complicated.


[*] other than using Reflection, but let's not go there!

You may create a class that derives from MarshalByRefObject :

public class AppDomainWrapper : MarshalByRefObject
{
    public void openUI()
    {
        var application = new System.Windows.Application();
        var ui = new Window();
        application.Run(ui);
        application.Shutdown();
    }
}

...and execute its openUI() method in its own application domain:

[STAThread]
static void Main(string[] args)
{
    const int n = 2;
    for (int i = 0; i < n; ++i)
    {
        AppDomain appDomain = AppDomain.CreateDomain("AppDomain");
        AppDomainWrapper application = appDomain.CreateInstanceAndUnwrap(typeof(AppDomainWrapper).Assembly.FullName, typeof(AppDomainWrapper).FullName) as AppDomainWrapper;
        application.openUI();
        AppDomain.Unload(appDomain);
    }
}

Have a look at this question: Does a WPF Application Actually Need Application.Run? .

Basically it says, that you can open windows using window.ShowDialog() method without Application instance

The think is that Application.Run does not do anything important but run Dispatcher loop. ShowDialog have its own Dispatcher. You can create Application singleton instance however, since it contains some shared resources.

Steven Rands shows the problem. I have the same problem in an external add-in. But I need an application object for xaml resources and a valid Application.Current. In my eyes this is a bug. If you call Shutdown() this member should also be reset to false.

Hack(run it after application.Shutdown() ). I use this in tests:

var field = typeof(Application).GetField(
    "_appCreatedInThisAppDomain",
    BindingFlags.Static | BindingFlags.NonPublic) ??
    throw new InvalidOperationException(
        "Field is not found: _appCreatedInThisAppDomain.");
field.SetValue(null, false);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM