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.
Related
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.
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);
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.
I need to write a wrapper around a third party api that peruses message pumps and hence needs to be handled very differently depending on whether the wrapper is instantiated on a UI thread (such as within a wpf application) or not (such as a console application).
If it does not run on a UI thread then I need a dispatcher and implement my own message pump.
For that purpose I need to know whether the wrapper is instantiated within a wpf application or not. It is not enough to determine whether the instantiation happens on a UI thread or not (as even in a wpf application the thread on which the wrapper is instantiated might not be the UI thread).
Is there any way I can figure out whether I am in a wpf or windows form environment with message pump or a console application where I have to implement my own message pump?
Thanks
I think you might be best off to have three separate packages.
To make it auto-select, you would need to have references to both WPF and WinForms in your package. Assuming worstcase, your package just made me import both into my console application. Others might consider importing WinForms into an WPF application worst case and another group of people might do WinForms because they cannot access WPF (and therefor you just cut them of from using your package altogether).
If no other answer satisfies your needs - you can use reflection to check if Application.Current is null or not, without directly referencing WPF assemblies (and the same works with WinForms):
private static bool IsWpfApplication() {
var type = Type.GetType("System.Windows.Application, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
if (type == null)
return false;
var currentProp = type.GetProperty("Current", BindingFlags.Public | BindingFlags.Static);
if (currentProp == null)
return false;
return currentProp.GetValue(null, new object[0]) != null;
}
Note that this might load PresentationFramework dll into current app domain, even in console application. If that is a problem for you - you can do the same by inspecting assemblies already loaded in app domain:
private static bool IsWpfApplication2() {
var wpfAsm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(c => c.GetName().Name == "PresentationFramework");
if (wpfAsm == null)
return false;
var type = wpfAsm.GetType("System.Windows.Application");
if (type == null)
return false;
var currentProp = type.GetProperty("Current", BindingFlags.Public | BindingFlags.Static);
if (currentProp == null)
return false;
return currentProp.GetValue(null, new object[0]) != null;
}
Is there any way I can figure out whether I am in a wpf or windows form environment with message pump or a console application where I have to implement my own message pump?
You could check whether there is a top-level window available:
if (Process.GetCurrentProcess().MainWindowHandle != IntPtr.Zero)
//WPF
MainWindowHandle should return a handle provided that the WPF application has a main window.
You should also be able to use the native GetConsoleWindow function to determine whether you're in the context of a console application.
This might work:
public static void Main(string[] args)
{
var application = Application.Current;
Console.WriteLine($"Application is {(application == null ? "null": "not-null")}");
Console.ReadKey();
}
Requires references to PresentationFramework and, according to Resharper, WindowsBase and System.Xaml
I'm currently trying to use a WPF component that makes use of Application.Current from a WPF application, however due to several reasons I never call Application.Run (nor is that an option). The result is a NullReferenceException.
I'm basically trying to display multiple instances of the same WPF window from what would be a console application.
Any advice (and code samples in C#/F#) would be welcome!
Thanks in advance
Just to offer an alternative solution.
It is possible to keep an application running without any windows open. To me this feels less 'hackish'. :) http://msdn.microsoft.com/en-us/library/system.windows.application.shutdownmode.aspx
public class AppCode : Application
{
// Entry point method
[STAThread]
public static void Main()
{
AppCode app = new AppCode();
app.ShutdownMode = ShutdownMode.OnExplicitShutdown;
app.Run();
...
app.Shutdown();
}
}
EDIT:
Ok this got a bit cumbersome. Application.Run will block, so it needs to run in its own thread.
When it does run in its own thread, any interaction between your main thread and your ui thread had best be done by Application.Current.Dispatcher.Invoke. Here is some working code, that assumes you have a class that inherits from Application. I'm using a modified App.xaml/App.xaml.cs that a WPF project template creates for you, to get nice handling of ResourceDictionaries for free.
public class Program
{
// Entry point method
[STAThread]
public static void Main()
{
var thread = new System.Threading.Thread(CreateApp);
thread.SetApartmentState(System.Threading.ApartmentState.STA);
thread.Start();
// This is kinda shoddy, but the thread needs some time
// before we can invoke anything on the dispatcher
System.Threading.Thread.Sleep(100);
// In order to get input from the user, display a
// dialog and return the result on the dispatcher
var result = (int)Application.Current.Dispatcher.Invoke(new Func<int>(() =>
{
var win = new MainWindow();
win.ShowDialog();
return 10;
}), null);
// Show something to the user without waiting for a result
Application.Current.Dispatcher.Invoke(new Action(() =>
{
var win = new MainWindow();
win.ShowDialog();
}), null);
System.Console.WriteLine("result" + result);
System.Console.ReadLine();
// This doesn't really seem necessary
Application.Current.Dispatcher.InvokeShutdown();
}
private static void CreateApp()
{
App app = new App();
app.ShutdownMode = ShutdownMode.OnExplicitShutdown;
app.Run();
}
}
The following is the intended behavior of Application class:
The first open window is the MainWindow.
The only window in the list becomes the MainWindow (if others are to
be removed).
Application Class is designed to exit if no windows are present in
windows list.
Check this link.
So basically you cannot run an Application, without any window open. Keep a window open but hidden.
If I have misunderstood your problem, then perhaps the following similar cases might help:
Managing Application Resources when WPF is Hosted.
When running unit tests in Visual Studio 2008
I have a console app, where I want to show wpf window:
ths = new ThreadStart(() =>
{
window = new wpf_lib.Container();
System.Windows.Application app = new Application();
app.Run(window);
});
th = new Thread(ths);
th.TrySetApartmentState(ApartmentState.STA);
th.Start();
But, how I can get access for wpf window methods and properties from any method in the console app?
You've two options, first and in your case easiest:
Use the System.Windows.Application.MainWindow method to get access to the Window's properties and methods.
Second: Add a property/method to the Container class that returns the Window object you want. This may or may not be possible, mostly depending on wether you've access to the source code of that class.
If you are encountering exceptions regarding thread access, use the Dispatcher of the appropriate WPF object. See this question for details.
When attempting to Resume a Workflow with the following code:
public WorkflowApplication LoadInstance(Guid instanceId)
{
if (this.instances.ContainsKey(instanceId))
return this.instances[instanceId];
WorkflowApplication instance = new WorkflowApplication(new Tarpon.Workflows.CreateContact());
// Create Persistable Workflow
SqlWorkflowInstanceStore store = new SqlWorkflowInstanceStore(ConfigurationManager.ConnectionStrings["WorkflowPersistance"].ConnectionString);
store.HostLockRenewalPeriod = new TimeSpan(0, 0, 5);
instance.InstanceStore = store;
// Load Instance
instance.Completed += OnWorkflowCompleted;
instance.Idle += OnIdle;
instance.PersistableIdle += OnIdleAndPersistable;
instance.Aborted += OnAborted;
instance.Load(instanceId);
// Save instance in list of running instances
this.instances.Add(instance.Id, instance); // ERROR IS THROWN HERE
return instance;
}
I get and error on the line "this.instances.Add(instance.Id, instance)":
The execution of an InstancePersistenceCommand was interrupted because the instance '9b9430b6-f182-469d-bcae-0886d546f7ea' is locked by a different instance owner.
This error usually occurs because a different host has the instance loaded. The instance owner ID of the owner or host with a lock on the instance is '30411662-b9b3-4250-9e2c-5aaa9895b740'.
I have attempted to lower the HostLockRenewalPeriod in the above code, and also added the below code to hopefully disable the lock on the Instance but to no avail. It also never seems to break into the below code. Every time I go past the Load() method, I get th above error.
public PersistableIdleAction OnIdleAndPersistable(WorkflowApplicationIdleEventArgs e)
{
instances.Remove(e.InstanceId);
return PersistableIdleAction.Unload;
}
It seems this code works half the time, but the other half it does not resume it's workflows correctly.
Does anyone have any clue to what I can do to remove the lock properly, without having to re-write all of this functionality?
Please take a look at this blog post describing persistance and instaneStore confiuration.
This code is copied from the post and I think it might help you: "
var instanceStore = new SqlWorkflowInstanceStore(connStr);
var instanceHandle = instanceStore.CreateInstanceHandle();
var createOwnerCmd = new CreateWorkflowOwnerCommand();
var view = instanceStore.Execute(instanceHandle, createOwnerCmd, TimeSpan.FromSecond(30));
instanceStore.DefaultInstanceOwner = view.InstanceOwner;
// Do whatever needs to be dome with multiple WorkflowApplications
var deleteOwnerCmd = new DeleteWorkflowOwnerCommand();
instanceStore.Execute(instanceHandle, deleteOwnerCmd, TimeSpan.FromSeconds(30));
The key is the CreateWorkflowOwnerCommand that needs to be executed at the start. And when you use the CreateWorkflowOwnerCommand just make sure not to forget the DeleteWorkflowOwnerCommand otherwise all workflow will remain locked by the owner and can’t be reloaded by another SqlWorkflowInstanceStore
I don't see instance.Run or instance.ResumeBookmark here, and you'll need that for any execution-related events like PersistableIdle to be fired.
Actually, when I've seen this error in development, it just means I need to clean out my persistence database. You can do that with a stored procedure that already exists to delete a persisted workflow.
Try making a WorkflowIdleBehavior object and setting it's TimeToUnload to zero. See here for more details.