Executing a c# WPF application inside an already running c# process - c#

I´m trying to build up an automated check if several 32 bit WPF applications can be opened without problems.
I do not want to use Process.Start as i cannot be sure if each program will return a non-zero exit code in case a problem occurs (and i would have to close those WPF application with further code).
My plan instead: Loading the assemblies at runtime and triggering their start method (connecting to some exception event sinks to get infos about problems and closing the windows opened later on).
This is what i got so far:
public void Check(string executablePath)
{
try
{
Assembly assembly;
try
{
assembly = Assembly.LoadFrom(executablePath);
}
catch (BadImageFormatException e)
{
Logger.InfoFormat("Not a 32 bit .NET application : {0}", Path.GetFileName(executablePath));
return;
}
assembly.EntryPoint.Invoke(null, new object[] { });
Logger.InfoFormat("OK : {0}", Path.GetFileName(executablePath));
}
catch (Exception e)
{
Logger.Error(e);
}
}
My problem: As soon as i invoke the EntryPoint method, an error screen from the application inside is presented telling me an IOExeption happened (it was not able to find the resource for the splash screen).
Do i have to preload those resources inside other assemblies somehow to get it working?
Update
With Dirks answer i was able to create a new application domain and delegating the call of the entry point to a MarshalByRefObject descendant created by this domain.
I was also able to change the value of Assembly.EntryAssembly thanks to this website (currently not online)
http://webcache.googleusercontent.com/search?q=cache:6POIVfrxbAcJ:dejanstojanovic.net/aspnet/2015/january/set-entry-assembly-in-unit-testing-methods/+&cd=8&hl=en&ct=clnk&gl=de
Code snippet doing the work:
private void ModifyEntryAssembly(Assembly assembly)
{
AppDomainManager manager = new AppDomainManager();
FieldInfo entryAssemblyfield = manager.GetType().GetField("m_entryAssembly", BindingFlags.Instance | BindingFlags.NonPublic);
if (entryAssemblyfield == null)
{
throw new Exception("Could not retrieve entryAssemblyField.");
}
entryAssemblyfield.SetValue(manager, assembly);
AppDomain domain = AppDomain.CurrentDomain;
FieldInfo domainManagerField = domain.GetType().GetField("_domainManager", BindingFlags.Instance | BindingFlags.NonPublic);
if (domainManagerField == null)
{
throw new Exception("Could not retrieve domainManagerField.");
}
domainManagerField.SetValue(domain, manager);
}
Now, im getting the splash screen and a login dialog from the called executable, much further now!
There is another problem with an EEntryPointException thrown, but that´s another story for another question... Thanks!

The WPF splash screen is searched in the entry assembly1, which, in your case, is your main executable and not the executable you are trying to check.
You can resolve this by starting the application under test in an own application domain, which will then get its own entry assembly:
class Test
{
public static void Main()
{
var otherDomain = AppDomain.CreateDomain("otherDomain");
otherDomain.ExecuteAssembly("MyExecutable.exe");
}
}
However, you should be aware that your approach is going to give you false positives, because the application under test runs in another environment. For example, calls Assembly.GetExecutingAssembly() will give different results in the application under test. And your approach won't be able to test 32-bit and 64-bit applications at the same time.
1
As you can see from the reference source, the below overload of the SplashScreen constructor calls Assembly.GetEntryAssembly():
public SplashScreen(string resourceName)
: this(Assembly.GetEntryAssembly(), resourceName)
{
}

Related

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.
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.

How to determine whether a class is instantiated within a console application or wpf?

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

C# WCF service implemented in a DLL

I have a WCF service written in C# what works fine. It's the usual self-hosted type in a Win32 console application. But now, for reasons of special updating (it's not a simple desktop computer where you simply update a program using the usual installers but a special embedded system) I'd like to move the actual type implementing the service to a DLL which is loaded by a very simple loader executable using reflection:
string DllFilename = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "myservice.dll");
var ServicesDll = Assembly.LoadFrom(DllFilename);
var ServicesType = ServicesDll.GetType("MyNamespace.MyServices");
var Instance = (ServiceBase)Activator.CreateInstance(ServicesType);
ServiceBase.Run(new ServiceBase[] { Instance });
I can deploy the service all three ways, either installing it normally via MSI, or using sc.exe, or even with a self-managing code like this:
ServicesType.GetMethod("InstallService").Invoke(null, null);
ServicesType.GetMethod("StartService").Invoke(null, null);
where
public class MyServices : ServiceBase {
//...
public static void InstallService() {
if (!IsInstalled())
using (AssemblyInstaller installer = GetInstaller()) {
var state = new Hashtable();
try {
installer.Install(state);
installer.Commit(state);
}
catch {
installer.Rollback(state);
throw;
}
}
}
public static void StartService() {
if (IsInstalled())
using (var controller = new ServiceController("MyService")) {
if (controller.Status != ServiceControllerStatus.Running) {
controller.Start(); // fails here
controller.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(10));
}
}
}
//...
}
As I mentioned in the beginning, everything works just fine in an EXE. But as soon as the MyService type is in the DLL, InstallService is still OK but StartService fails with System.ComponentModel.Win32Exception: %1 is not a valid Win32 application. It seems that Windows expects the service executable (which it stores itself not as the EXE I actually installed but as the DLL the service was implemented in) to be an executable to be called at its leisure.
How can I circumvent this limitation, maybe to signal somehow that it's my loader EXE that I want to be called, not the dependent DLL?
Split your MyServices into two classes - one a thin wrapper inherited from ServiceBase (to live in .EXE) and another doing real work including self-hosting to live in updatable .DLL. The first one can take an instance of the second as a .ctor argument and delegate start/stop calls to it.
You may also want to look at Topshelf.
I finally went with Rene's suggestion, the bulk of the service stays in an EXE and loaded as an assembly into the loader. Fortunately, loading an assembly can be done both from EXE and DLL.

Is it possible to reflect whole Windows Forms applications?

EDIT: The problem was a mismatch between the two programs' Framework versions: one was using 2.0 while two was using 4.0. The below code works perfectly.
I am trying to reflect a whole Windows Form application, but invoking the Entry Point does not work: either nothing happens or a crash shows up. Is there something I can do?
public static void Reflect(object program)
{
object[] objArray;
MethodInfo entryPoint = ((Assembly)program).EntryPoint;
MethodInfo methodInfo = entryPoint;
if ((int)entryPoint.GetParameters().Length == 0)
{
objArray = null;
}
else
{
objArray = new object[] { new string[0] };
}
methodInfo.Invoke(null, objArray);
}
If both your applications are WinForms then you can't run a second application within first one because it's a single threaded environment (and each one has its own message pump).
To workaround this issue you may start your second application from another thread. This way each application will run in its own thread (but honestly I don't know if this will always work or if there are other drawbacks with Windows).
Something like this (assuming your assembly has not been loaded for Reflection only):
ThreadPool.QueueUserWorkItem(delegate (object state)
{
var entryPoint = ((Assembly)program).EntryPoint;
entryPoint.Invoke(null, new string[0]);
});
Note that application entry point (if not null) will always have same signature, no need to check. Of course I wouldn't use a thread from pool but this is just an example.

AssemblyResolve Does not fire

I have an asp.net application. I want to load some assemblies dynamically.
this is my code on application start
protected void Application_Start(Object sender, EventArgs e)
{
LoadPrivateAssemblies();
}
private static void LoadPrivateAssemblies()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainAssemblyResolve;
Assembly.Load("MyDLL");
}
static Assembly CurrentDomainAssemblyResolve(object sender, ResolveEventArgs args)
{
//loads and returns assembly successfully.
}
this code works fine except when a nested c# code calls a class from my dynamic dll Inside an asp.net page (not code-behind)
sample :
<%if(MyDLL.TestObject.Value){%>white some ting<%}%>
what should I do now ?
I think If I know When a new AppDomain is created, it may solve my problem.
I really think you are barking up the wrong tree here.
Assembly.Load(byte[]) does "persist" the assembly in the app domain - otherwise, what else would it be doing?
To illustrate the fact it does, try this:
Create a solution with one console application, and one class library, named OtherAssembly.
In the class library, OtherAssembly, add a single class:
namespace OtherAssembly
{
public class Class1
{
public string HelloWorld()
{
return "Hello World";
}
}
}
In the console application, use this as your program:
public class Program
{
static void Main(string[] args)
{
try
{
using (var fs = new FileStream("OtherAssembly.dll", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
var buffer = new byte[fs.Length];
// load my assembly into a byte array from disk
fs.Read(buffer, 0, (int) fs.Length);
// load the assembly in the byte array into the current app domain
AppDomain.CurrentDomain.Load(buffer);
}
// get my type from the other assembly that we just loaded
var class1 = Type.GetType("OtherAssembly.Class1, OtherAssembly");
// create an instance of the type
var class1Instance = class1.GetConstructor(Type.EmptyTypes).Invoke(null);
// find and invoke the HelloWorld method.
var hellowWorldMethod = class1.GetMethod("HelloWorld");
Console.WriteLine(hellowWorldMethod.Invoke(class1Instance, null));
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
finally
{
Console.ReadLine();
}
}
}
Don't reference OtherAssembly from your main program assembly, instead, compile the solution and manually drop OtherAssembly.dll in the main program's bin folder.
Run the main program. It outputs "Hello World", which it can only have done if the assembly was loaded and retained in memory. You'll note I've been very careful not to give Visual Studio or C# any hint to load this OtherAssembly. No reference was added, the type is not explicitly referenced in C#.
You need to look again at your problem.
[EDIT: in response to you commenting on the fact this is not an ASP.NET application]
OK - I've moved my main program to an ASP.NET web page and tried accessing the assembly both from code behind and the markup - and it works in both situations. I'm sure you are missing something here - it just doesn't make sense that the behaviour of this method - whose job it is to load an assembly into the current app domain - is different in an ASP.NET scenario.
It seems to me there are at least two things to investigate:
If it really is a new app domain being created on each call, it would only be because of an unhandled exception in your application. The quickest way to diagnose this is to use SysInternals Process Explorer. Fire it up - add .Net -> Total AppDomains to the list of columns and watch the app domain count for your process (IIS worker process or web dev) as you run your app. If this increases on each call, you've got unhandled exceptions tearing down one AppDomain and forcing another to be created.
Are you sure this assembly is really being loaded? When you pass the byte array to Assembly.Load, try also writing it out to disk using FileStream.Write. Open the file in Reflector or some similar tool. Is this the assembly you expected it to be. Is it really being loaded at all, or is it an empty/corrupt array?
I'm not trying to be argumentative, but this really feels like you are looking in the wrong place for the cause of the problem.
I think If I know When a new AppDomain is created, it may solve my problem
You should use AssemblyLoad event. AssemblyResolved occurs when the resolution of assembly is fails
I found that the problem is because Assembly.Load(bytes); does not persist the assembly in appdomain. does any body know how to persist loaded assembly using Assembly.Load(bytes); in appdomain ?
finally I decided to switch to LoadFile method instead of load.
EDIT
Finally I switched from Assemly.Load(byte[]) to Assembly.LoadFile(string).
But it did not correct the problem itself. I have added <%#Assembly name="MyDLL"%> To All of my ASPX files that has markup C# code emebeded.
And This Solved my Problem.
Thanks to your answers. I have voted up answers that helped me, but I can not accept your solutions because no one is not complete enough.

Categories

Resources