[英]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
(申請和thair來源)
現在,我想將LOADED.exe
[及其資源,如圖像dll和...)作為Byte array
加載到內存並執行它,然后從硬盤中刪除LOADED.exe
及其資源。
在第一步中,我試圖just load the LOADED.exe file to memory and execute it
(所以我在這一步中使用了一個simple EXE without any resource
的simple EXE without any resource
)。
好的,我在這里找到了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里面的按鈕下試了一下然后RUN ...處理異常的結果:
無法在同一AppDomain中創建多個System.Windows.Application實例。
好!
然后,我搜索了一個解決方案,並且已經說過你必須在一個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);
}
}
現在我的錯誤在這一行:
好!
我再次搜索並找到了這個( 動態加載程序集未加載到新的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**
:
Luncher.exe中發生了未處理的“System.TypeLoadException”類型異常
附加信息: 無法從程序集Loaded,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null'加載類型'Main' 。
另外我在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中刪除它的文件它在內存中運行。
創建共享程序集。 這將加載到AppDomains(“Launcher”域,“已加載”域)中,並作為我們的“已加載”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); } } }
用它。 從Launcher應用程序中調用Runner.RunInOtherDomain
。
var assemblyPath = "path to your loaded.exe"; ChildDomainLoader.Runner.RunInOtherDomain(assemblyPath); File.Delete(assemblyPath);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.