正在加载字节数组程序集
本文关键字:数组 程序集 字节数 字节 加载 | 更新日期: 2023-09-27 18:24:38
我正在尝试只使用字节数组加载程序集,但我不知道如何让它正常工作。设置如下:
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_",这样系统就无法找到物理文件。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.
为什么要在系统中搜索文件?
更新
另一方面,这似乎奏效了:
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程序中,然后使用AssemblyResolve
从ResourceStream
加载它们(对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);
}
}
}