正在加载字节数组程序集



我正在尝试只使用字节数组加载程序集,但我不知道如何让它正常工作。设置如下:

public static void Main() 
{
    PermissionSet permissions = new PermissionSet(PermissionState.None);
    AppDomainSetup setup = new AppDomainSetup { ApplicationBase = Environment.CurrentDirectory };
    AppDomain friendlyDomain = AppDomain.CreateDomain("Friendly", null, setup, permissions);
    Byte[] primary = File.ReadAllBytes("Primary.dll_");
    Byte[] dependency = File.ReadAllBytes("Dependency.dll_");
    // Crashes here saying it can't find the file.
    friendlyDomain.Load(dependency);
    AppDomain.Unload(friendlyDomain);
    Console.WriteLine("Stand successful");
    Console.ReadLine();
}

我创建了两个mock dll,并有意将其扩展名重命名为".dll_",这样系统就无法找到物理文件。primarydependency都正确填充,但当我尝试用二进制数据调用AppDomain.Load方法时,它会返回:

Could not load file or assembly 'Dependency, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

为什么要在系统中搜索文件?

更新

另一方面,这似乎奏效了:

public class Program {
    public static void Main() {
        PermissionSet permissions = new PermissionSet(PermissionState.Unrestricted);
        AppDomainSetup setup = new AppDomainSetup { ApplicationBase = Environment.CurrentDirectory };
        AppDomain friendlyDomain = AppDomain.CreateDomain("Friendly", null, setup, permissions);
        Byte[] primary = File.ReadAllBytes("Primary.dll_");
        Byte[] dependency = File.ReadAllBytes("Dependency.dll_");
        // Crashes here saying it can't find the file.
        // friendlyDomain.Load(primary);
        Stage stage = (Stage)friendlyDomain.CreateInstanceAndUnwrap(typeof(Stage).Assembly.FullName, typeof(Stage).FullName);
        stage.LoadAssembly(dependency);
        Console.WriteLine("Stand successful");
        Console.ReadLine();
    }
}
public class Stage : MarshalByRefObject {
    public void LoadAssembly(Byte[] data) {
        Assembly.Load(data);
    }
}

因此CCD_ 5和CCD_。

这是正常的,CLR在搜索"主要"需要的程序集时,不认为您加载的"依赖项"是合适的程序集。一个与"加载上下文"相关的问题,没有这样加载的程序集的问题。这是故意的,CLR无法确保DLL地狱不会成为问题,因为它不知道程序集来自哪里。既然你打开了DLL地狱的大门,你也必须自己避开地狱。

您需要实现AppDomain.AssemblyResolve事件。当CLR找不到"依赖项"时,它将被激发,您可以返回从assembly.Load(byte[])获得的程序集。但是,当它为同一程序集多次激发时,也就是说,返回完全相同的程序集时,您必须始终这样做,否则.NET类型标识将导致更多问题。产生了难以理解的铸造异常,"不能铸造Foo到Foo"风格。

还有其他问题,效率相当低。程序集的虚拟内存不能由磁盘上的文件支持,因此它由分页文件支持。这会增加流程的提交大小。

当然最好不要这样做。

这两种方法没有区别(如果需要,可以查看官方源代码)。

在AppDomain.Load方法(Byte[])的MSDN页面中,注意到该方法正在当前应用程序域中加载程序集:

此方法应仅用于将程序集加载到当前应用程序域。提供此方法是为了方便无法调用静态程序集的互操作性调用程序。Load方法若要将程序集加载到其他应用程序域中,请使用方法,例如CreateInstanceAndUnwrap。

线路:

friendlyDomain.Load(dependency);

行为与完全相同:

Assembly.Load(dependency);

它在更新的示例代码中起作用的原因是,Stage对象实际上正在childAppDomain内部调用Assembly.Load

注:这个答案补充了Hans Passant和colinsmith的答案。

如果使用FusionLogViewer,您可以查看CLR在加载程序集时遇到的特定问题的更多详细信息。。。。它可以向你显示它试图探测的位置,以给你线索,等等。

  • http://msdn.microsoft.com/en-us/library/e74a18c4(v=vs.71).aspx
  • http://www.shujaat.net/2012/04/fusion-log-viewer-fuslogvw-for-assembly.html

您还可以在代码中处理AppDomain上的AssemblyLoad/AssemblyResolve/ResourceResolve事件,以跟踪整个序列。

  • http://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve.aspx

这是一个方便的示例,它使用自定义MSBuild步骤将任何依赖于项目的程序集作为资源嵌入到EXE程序中,然后使用AssemblyResolveResourceStream加载它们(对byte[]数组执行Assembly.Load())。

  • http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application

所以我在做一系列研究时,发现了一些关于stackoverflow的答案,我提出的解决方案如下:

using System;
using System.Windows.Forms;
using System.Reflection;
public partial class MainForm : Form
{
    private AppDomain = AppDomain.CreateDomain("asmDomain");
    public MainForm()
    {
        InitializeComponent();
    }
    /// <summary>
    /// Loads a Byte array as raw assmebly then loads and creates defined object from 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    [LoaderOptimizationAttribute(LoaderOptimization.MultiDomain)]
    private void loadAsmObject(object sender, TileItemEventArgs e)
    {
        Byte[] rawAssembly = getFileAsm(); // Load the bytes however you wish.
        try
        {
            AppDomain.Unload(appDomain);
            appDomain = AppDomain.CreateDomain("asmDomain");
            AppDomainBridge isolationDomainLoadContext = (AppDomainBridge)appDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof (AppDomainBridge).ToString());
            // Form is MarshalByRefObject type for the current AppDomain
            MyObject obj = isolationDomainLoadContext.ExecuteFromAssembly(rawAssembly, "MyNamespace.MyObject"/*, new object[] { "Arg1", "Arg2" } Optional args*/) as MyObject;
            obj.callMethod();
        }
        catch (Exception Ex)
        {
            MessageBox.Show("Failed to load Object!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    /// <summary>
    /// Acts as a shared app domain so we can use AppDomain.CurrentDomain.Load without errors.
    /// </summary>
    private class AppDomainBridge : MarshalByRefObject
    {
        public Object ExecuteFromAssembly(Byte[] rawAsm, string typeName, params object[] args)
        {
            Assembly assembly = AppDomain.CurrentDomain.Load(rawAssembly: rawAsm);
            return Activator.CreateInstance(assembly.GetType(typeName), args);
        }
    }
}

最新更新