从另一个WPF应用程序加载WPF应用程序程序集,出现错误:无法在同一AppDomain中创建多个System.Windows.Application实例 [英] Load a WPF application Assembly from another WPF app, get error: Cannot create more than one System.Windows.Application instance in the same AppDomain
问题描述
场景:
LUNCHER.exe :一个WPF应用程序>> Build 32bit
,.Net 4.5.1
,location= D:\
LOADED.exe :另一个WPF应用程序>> Build 32bit
、. Net 4.5.1,location= D:\
我是owner of both assembly
(应用程序和其他来源)
现在,我想将LOADED.exe
[及其图像dll和...之类的资源)作为Byte array
加载到内存并执行它,然后从硬盘上删除LOADED.exe
及其资源./p>
在第一步中,我尝试just load the LOADED.exe file to memory and execute it
(因此在此步骤中使用了simple EXE without any resource
).
A)
好吧,我发现WinForm程序使用这种方式此处:
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?
}
}
我在LUNCHER.exe里面的一个按钮下尝试了一下,然后运行...处理异常的结果:
不能在同一AppDomain中创建多个System.Windows.Application实例.
好!
B)
然后,我搜索了一个解决方案,有人说必须在new [different] AppDomain
中执行它.
例如,这是一个答案:动态加载的程序集-设置&交流
我通过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);
}
}
现在我的错误在这一行:
好!
C)
我再次搜索并找到了这个(动态加载的程序集不在新的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;
}
}
}
然后我通过以下代码在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[] {});
}
但是我遇到了与以前的方式**B**
相同的错误:
"System.TypeLoadException"类型的未处理异常发生在 Luncher.exe
其他信息:无法从程序集中加载类型"Main" 已加载,版本= 1.0.0.0,文化=中性, PublicKeyToken = null'.
D)
我也在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[] {});
}
运行时代码的最后一行错误:
类型的未处理异常 mscorlib.dll中发生了" System.Reflection.TargetInvocationException "
其他信息:目标服务器已抛出异常 调用.
最后:
我很困惑!
- 以上所有方法中我都犯什么错误?
- 为什么所有方法都以错误结尾!!!
请以简单的描述和一些代码帮助我,因为在这种情况下我是新手(加载程序集,创建AppDomain
和...),但是我需要将WPF应用程序加载到内存中并显示其窗口,然后当它在内存中运行时,从HDD中删除它的文件.
-
创建共享程序集.这将同时加载到两个AppDomain("Launcher"域,"Loaded"域)中,并作为我们"Loaded" AppDomain的入口点:
添加新项目>类库>名称:
ChildDomainLoader
向新项目添加以下引用:
System.Xaml
,WindowsBase
,PresentationFramework
在您的
Launcher
项目中为ChildDomainLoader
添加项目参考.Loaded
项目无需修改. -
向共享程序集中添加一些代码.我们需要一个可以称为跨域的MarshalByRefObject并加载我们的子程序集.我们称之为
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); } } }
-
使用它.从启动器应用程序中调用
Runner.RunInOtherDomain
.var assemblyPath = "path to your loaded.exe"; ChildDomainLoader.Runner.RunInOtherDomain(assemblyPath); File.Delete(assemblyPath);
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).
A)
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!
B)
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!
C)
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'.
D)
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.
Finally:
I'm confused!
- what is my mistake in all above methods?
- why all the ways end with an ERROR!!!
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 yourLauncher
project. TheLoaded
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);
这篇关于从另一个WPF应用程序加载WPF应用程序程序集,出现错误:无法在同一AppDomain中创建多个System.Windows.Application实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!