加载字节数组assembly
我正在尝试使用字节数组加载程序集,但我无法弄清楚如何让它正常工作。 这是设置:
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(); }
我创建了两个模拟dll,并故意将其扩展名重命名为“.dll_”,因此系统无法找到物理文件。 primary
和dependency
正确填充,但是当我尝试使用二进制数据调用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.
为什么要在系统中搜索文件?
UPDATE
另一方面,这似乎工作:
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); } }
所以看来AppDomain.Load
和Assembly.Load
之间存在差异。
这是正常的,CLR在搜索“主要”需要的程序集时不会将您加载的“依赖项”视为合适的程序集。 与“加载上下文”相关的问题,没有一个像这样加载的程序集。 这是故意的,CLR无法确保DLL Hell不会成为问题,因为它不知道程序集的来源。 既然你打开了DLL Hell的大门,你也必须自己避开地狱。
您需要实现AppDomain.AssemblyResolve事件。 它会在CLR找不到“依赖”时触发,你可以返回从Assembly.Load(byte [])得到的程序集。 但是,当它为同一个程序集多次触发时,你必须这样做,换句话说,返回完全相同的程序集,否则你会遇到更多由.NET类型标识引起的问题。 产生难以理解的铸造exception,“无法铸造Foo to Foo”的风格。
还有其他问题,效率很低。 程序集的虚拟内存不能由磁盘上的文件支持,因此它由页面文件支持。 这会增加进程的提交大小。
不这样做肯定会更好。
这两种方法没有区别(如果需要,可以查看官方源代码 )。
在AppDomain.Load方法(Byte [])的MSDN页面中,注意到此方法正在当前应用程序域中加载程序集:
此方法仅应用于将程序集加载到当前应用程序域中。 提供此方法是为了方便无法调用静态Assembly.Load方法的互操作性调用者。 要将程序集加载到其他应用程序域,请使用CreateInstanceAndUnwrap等方法。
这条线:
friendlyDomain.Load(dependency);
表现完全相同:
Assembly.Load(dependency);
它在更新的示例代码中工作的原因是因为Stage
对象实际上是在子 AppDomain中调用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事件,以跟踪序列。
这是一个方便的示例,它使用自定义MSBuild步骤将任何项目相关程序集作为Resources嵌入到EXE程序中,然后使用AssemblyResolve
从ResourceStream
加载它们Assembly.Load()
在byte []数组上执行Assembly.Load()
) 。