The Scenario:
LUNCHER.exe: a WPF Application >> Build 32bit
, .Net 4.5.1
, location= D:\
LOADED.exe: another WPF Application >> Build 32bit
, .Net 4.5.1, location= D:\
I'm owner of both assembly
(both application and thair sources)
Now, i want load the LOADED.exe
[and its resources such as images dlls and...) as a Byte array
to the memory and execute it, then remove LOADED.exe
and its resources from hard disk.
In the first step i'm trying to just load the LOADED.exe file to memory and execute it
(so i used a simple EXE without any resource
in this step).
Ok, i found this way for WinForm programes here:
var filePath = @"D:\LOADED.EXE";
if (File.Exists(filePath))
{
try
{
// prepare to load the application into memory (using Assembly.Load)
// read the bytes from the application exe file
var fs = new FileStream(filePath, FileMode.Open);
var br = new BinaryReader(fs);
byte[] bin = br.ReadBytes(Convert.ToInt32(fs.Length));
fs.Close();
br.Close();
// load the bytes into Assembly
var asm = Assembly.Load(bin);
// search for the Entry Point
var method = asm.EntryPoint;
if (method != null)
{
// create an istance of the Startup form Main method
object o = asm.CreateInstance(method.Name);
// invoke the application starting point
Application.Current.ShutdownMode = System.Windows.ShutdownMode.OnExplicitShutdown;
method.Invoke(o, null);
}
else
{
//show message: Impossible to launch the application
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message + "\n\r\n\r" + ex.InnerException + "\n\r\n\r" + "\n\r\n\r" + ex.Source);
// exception throws .. something to do?
}
}
I tried it inside LUNCHER.exe under a button then RUN... The result of handled exception:
Cannot create more than one System.Windows.Application instance in the same AppDomain.
OK!
Then, i searched for a solution and some body has been said you must execute it in a new [different] AppDomain
.
For example here is an answer: Dynamically Loaded Assembly - Settings & Communication
I tried it by the following codes under another button in the LUNCHER.exe
:
private void Button_Click_1(object sender, RoutedEventArgs e)
{
try
{
var filePath = string.Format("{0}{1}", Utility.ExePath, PART_PATH);
AppDomain newappdomain = getAppDomainForAssembly(filePath, "LOADED.exe.domain");
object loadedexe_object = getInstanceFromAppDomain(ref newappdomain, filePath);
//If you know the method name to call...
executeMethod(loadedexe_object.GetType(), "methodname", ref loadedexe_object, null);
//or get entry point...
executeMethod(loadedexe_object.GetType(),
_asm_resolve(filePath).EntryPoint.Name, ref loadedexe_object, null);
}
catch (Exception ex)
{
var type = "";
if (ex is ArgumentNullException)
{
type = "ArgumentNullException";
}
else if (ex is NotSupportedException)
{
type = "NotSupportedException";
}
else if (ex is AppDomainUnloadedException)
{
type = "AppDomainUnloadedException";
}
else if (ex is TypeLoadException)
{
type = "TypeLoadException";
}
else if (ex is MissingMethodException)
{
type = "MissingMethodException";
}
else if (ex is MethodAccessException)
{
type = "MethodAccessException";
}
else if (ex is BadImageFormatException)
{
type = "BadImageFormatException";
}
else if (ex is FileLoadException)
{
type = "FileLoadException";
}
MessageBox.Show(type + "\n\r\n\r" + ex.Message + "\n\r\n\r" + ex.InnerException + "\n\r\n\r" + ex.Source);
}
}
private AppDomain getAppDomainForAssembly(string assemblypath, string appdomainname)
{
//this._assembly_file = AssemblyFile;
string _assembly_file_name = System.IO.Path.GetFileName(assemblypath);
string _rootpath = System.IO.Path.GetDirectoryName(assemblypath);
//this._assembly_class_name = AssemblyClassNameToInstance;
AppDomainSetup _app_domain_info = new AppDomainSetup();
_app_domain_info.ApplicationBase = _rootpath;
_app_domain_info.PrivateBinPath = _rootpath;
_app_domain_info.PrivateBinPathProbe = _rootpath;
_app_domain_info.ConfigurationFile = _rootpath + @"LOADED.exe.config"; //Here put the path to the correct .assembly .config file
AppDomain _app_domain = AppDomain.CreateDomain(appdomainname, null, _app_domain_info);
return _app_domain;
}
protected System.Reflection.Assembly _asm_resolve(string assemblyFile)
{
return System.Reflection.Assembly.LoadFrom(assemblyFile);
}
private object getInstanceFromAppDomain(ref AppDomain appDomain,
string assemblyPath, string className = null)
{
if (string.IsNullOrEmpty(className))
{
System.Reflection.Assembly assembly = _asm_resolve(assemblyPath);
System.Reflection.MethodInfo method = assembly.EntryPoint;
// Now my ERROR is in this line>>
return appDomain.CreateInstanceFromAndUnwrap(assemblyPath, method.Name);
}
else
{
return appDomain.CreateInstanceFromAndUnwrap(assemblyPath, className);
}
}
Now my error is in this line:
OK!
I searched again and found this one (Dynamically loaded Assembly not loading in new AppDomain):
// Provides a means of invoking an assembly in an isolated appdomain
public static class IsolatedInvoker
{
// main Invoke method
public static void Invoke(string assemblyFile, string typeName, string methodName, object[] parameters)
{
// resolve path
assemblyFile = Path.Combine(Environment.CurrentDirectory, assemblyFile);
Debug.Assert(assemblyFile != null);
// get base path
var appBasePath = Path.GetDirectoryName(assemblyFile);
Debug.Assert(appBasePath != null);
// change current directory
var oldDirectory = Environment.CurrentDirectory;
Environment.CurrentDirectory = appBasePath;
try
{
// create new app domain
var domain = AppDomain.CreateDomain(Guid.NewGuid().ToString(), null, appBasePath, null, false);
try
{
// create instance
var invoker = (InvokerHelper) domain.CreateInstanceFromAndUnwrap(Assembly.GetExecutingAssembly().Location, typeof(InvokerHelper).FullName);
// invoke method
var result = invoker.InvokeHelper(assemblyFile, typeName, methodName, parameters);
// process result
Debug.WriteLine(result);
}
finally
{
// unload app domain
AppDomain.Unload(domain);
}
}
finally
{
// revert current directory
Environment.CurrentDirectory = oldDirectory;
}
}
// This helper class is instantiated in an isolated app domain
private class InvokerHelper : MarshalByRefObject
{
// This helper function is executed in an isolated app domain
public object InvokeHelper(string assemblyFile, string typeName, string methodName, object[] parameters)
{
// create an instance of the target object
var handle = Activator.CreateInstanceFrom(assemblyFile, typeName);
// get the instance of the target object
var instance = handle.Unwrap();
// get the type of the target object
var type = instance.GetType();
// invoke the method
var result = type.InvokeMember(methodName, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, instance, parameters);
// success
return result;
}
}
}
Then i called it by the following codes under another button in the LUNCHER.exe
:
private void Button_Click_2(object sender, RoutedEventArgs e)
{
var filePath = string.Format("{0}{1}", Utility.ExePath, PART_PATH);
IsolatedInvoker.Invoke(filePath, "Main", "Main", new object[] {});
}
But i get same error like previous way **B**
:
An unhandled exception of type 'System.TypeLoadException' occurred in Luncher.exe
Additional information: Could not load type 'Main' from assembly Loaded, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Also i tested this way under another button in the LUNCHER.EXE
:
private void Button_Click_3(object sender, RoutedEventArgs e)
{
var filePath = @"D:\LOADED.exe";
var dll = File.ReadAllBytes(filePath);
var assembly = Assembly.Load(dll);
var app = typeof (Application);
var field = app.GetField("_resourceAssembly", BindingFlags.NonPublic | BindingFlags.Static);
field.SetValue(null, assembly);
//fix urihelper
var helper = typeof (BaseUriHelper);
var property = helper.GetProperty("ResourceAssembly", BindingFlags.NonPublic | BindingFlags.Static);
property.SetValue(null, assembly, null);
//---- Now my ERROR is in this line >>
assembly.EntryPoint.Invoke(null, new object[] {});
}
And error in last line of code in Run Time:
An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll
Additional information: Exception has been thrown by the target of an invocation.
I'm confused!
Please help me with simple descriptions and some codes because I'm beginner in this scenarios (loading assembly, create AppDomain
and...) but i need to load a WPF application to the memory and show its window and then delete it's file from H.D.D while it is running in the memory.
Create a shared assembly. This will be loaded into both AppDomains ("Launcher" Domain, "Loaded" Domain) and serve as an entry point of our "Loaded" AppDomain:
Add a new project > Class library > Name: ChildDomainLoader
Add the following references to the new project: System.Xaml
, WindowsBase
, PresentationFramework
Add a project reference for ChildDomainLoader
in your Launcher
project. The Loaded
project doesn't have to be modified.
Add some code to the shared assembly. We need a MarshalByRefObject that can be called cross-domain and loads our child assembly. Let's call it Runner
:
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
namespace ChildDomainLoader
{
public class Runner : MarshalByRefObject
{
public static AppDomain RunInOtherDomain(string assemblyPath)
{
var ownType = typeof (Runner);
string ownAssemblyName = ownType.Assembly.FullName;
// Create a new AppDomain and load our assembly in there.
var childDomain = AppDomain.CreateDomain(Guid.NewGuid().ToString());
childDomain.Load(ownAssemblyName);
// Call Run() in other AppDomain.
var runner = (Runner) childDomain.CreateInstanceAndUnwrap(ownAssemblyName, ownType.FullName);
runner.Run(assemblyPath);
return childDomain;
}
public void Run(string assemblyPath)
{
// We load the assembly as byte array.
var otherAssemblyBytes = File.ReadAllBytes(assemblyPath);
var assembly = AppDomain.CurrentDomain.Load(otherAssemblyBytes);
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
throw new NotImplementedException("Probably need to do some work here if you depend on other assemblies.");
};
// Set the assembly as ResourceAssembly, as WPF will be confused otherwise.
Application.ResourceAssembly = assembly;
// Search for the App class.
var app = assembly
.GetExportedTypes()
.Single(t => typeof(Application).IsAssignableFrom(t));
// Invoke its Main method.
MethodInfo main = app.GetMethod("Main", BindingFlags.Static | BindingFlags.Public);
main.Invoke(null, null);
}
}
}
Use it. Call Runner.RunInOtherDomain
from your Launcher application.
var assemblyPath = "path to your loaded.exe";
ChildDomainLoader.Runner.RunInOtherDomain(assemblyPath);
File.Delete(assemblyPath);
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