Udontknow Udontknow - 2 days ago 6
C# Question

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

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!

Answer

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)
{
}

Comments