从另一个WPF应用程序加载WPF应用程序程序集,获取错误:无法在同一AppDomain中创建多个System.Windows.Application实例

时间:2014-12-06 04:06:43

标签: c# wpf appdomain

情景:

LUNCHER.exe :WPF应用程序>> Build 32bit.Net 4.5.1location= D:\

LOADED.exe :另一个WPF应用程序>> Build 32bit,。Net 4.5.1,location= D:\

我是owner of both assembly(申请和来源)

现在,我想将LOADED.exe [及其资源(如图片dll和...)作为Byte array加载到内存中并执行它,然后删除LOADED.exe及其来自硬盘的资源。

在第一步中我尝试just load the LOADED.exe file to memory and execute it(所以我在这一步中使用了simple EXE without any resource)。

A)

好的,我发现这种方式适用于WinForm程序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?
    }
}

我在一个按钮下面的LUNCHER.exe中尝试了它然后运行...处理异常的结果:

  

无法在同一AppDomain中创建多个System.Windows.Application实例。

enter image description here

OK!


B)

然后,我搜索了一个解决方案,并且已经说过你必须在new [different] AppDomain中执行它。

例如,这里有一个答案:Dynamically Loaded Assembly - Settings & Communication

我在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);
    }
}

现在我的错误在这一行: enter image description here

OK!


C)

我再次搜索并找到了这个(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;
        }
    }
}

然后我通过以下代码在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,文化=中立,   公钥=空'


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中删除它的文件。

1 个答案:

答案 0 :(得分:2)

  1. 创建共享程序集。这将加载到AppDomains(“Launcher”域,“已加载”域)并作为“已加载”AppDomain的入口点:< / p>

    添加新项目&gt;课程表&gt;姓名:ChildDomainLoader

    将以下引用添加到新项目中:System.XamlWindowsBasePresentationFramework

    ChildDomainLoader项目中添加Launcher的项目参考。 <{1}}项目不需要修改。

  2. 将一些代码添加到共享程序集。我们需要一个可以称为跨域的MarshalByRefObject并加载我们的子程序集。我们称之为Loaded

    Runner
  3. 使用它。从Launcher应用程序中调用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