【问题标题】:Loading Byte Array Assembly加载字节数组程序集
【发布时间】:2013-07-24 13:57:21
【问题描述】:

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

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_”,这样系统就无法找到物理文件。 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);
    }
}

所以看来AppDomain.LoadAssembly.Load 之间存在差异。

【问题讨论】:

  • 依赖 DLL 是否有任何尚未复制的依赖项?
  • 主要依赖依赖。依赖关系虽然没有(非 CLR)依赖关系。似乎运行时不应该开始搜索文件。

标签: c# appdomain


【解决方案1】:

这是正常的,当 CLR 搜索“主要”需要的程序集时,它不会将您加载的“依赖项”视为合适的程序集。与“加载上下文”相关的问题,没有像这样加载的程序集。这是故意的,CLR 无法确保 DLL Hell 不会成为问题,因为它不知道程序集来自何处。既然打开了DLL Hell之门,你也得自己避开地狱。

您需要实现 AppDomain.AssemblyResolve 事件。当 CLR 找不到“依赖项”时它将触发,您可以返回从 Assembly.Load(byte[]) 获得的程序集。但是,当它为同一个程序集多次触发时,您必须始终如一地这样做,换句话说,返回完全相同的程序集,否则您将遇到更多由 .NET 类型标识引起的问题。产生难以理解的转换异常,“不能将 Foo 转换为 Foo”样式。

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

最好不要这样做。

【讨论】:

  • 为什么Assembly.Load 似乎允许这样做,而AppDomain.Load 不允许?另外,关于提交大小的要点。
  • 它与 Assembly.Load 没有任何关系。当您使用 AssemblyResolve 时,CLR 会说“我需要这个”,而您会说“就在这里”。所以 CLR 知道它得到了什么。您最初的方法是“这里有东西”,CLR 说“不知道那是什么”。
  • 我按照你的意思说,但只是通过切换方法似乎有效。我从来不需要使用AssemblyResolve,因为当它来自Assembly.Load 时,CLR 接受了它。
  • 注意到您对分页文件的建议,我决定在磁盘上创建一个临时文件夹/程序集副本,然后使用friendlyDomain.Load(传入完整的程序集名称)并从磁盘本身。感谢您的建议。
  • 实际上我发现内存中的程序集 (byte[]) 被加载到独立的 AppDomain 中(为 isolatedDomain.AssemblyLoad 添加处理程序)。但由于缺少文件,Manifest 无法加载。如果我提供相同的文件名但不同的程序集文件,则会出现清单不匹配错误。我猜MS实现不能很好地处理内存组装的情况。我知道的唯一工作方案是将程序集字节 [] 写入相同的文件名,然后从字节 [] 调用加载。但这很荒谬,从内存中加载就是从内存中加载。
【解决方案2】:

这两种方法没有区别(如果需要,可以查看official source code)。

AppDomain.Load Method (Byte[]) 的 MSDN 页面中指出,此方法正在加载当前应用程序域中的程序集:

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

行:

friendlyDomain.Load(dependency);

行为完全相同:

Assembly.Load(dependency);

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

注意:此答案补充了 Hans Passant 和 colinsmith 的答案。

【讨论】:

  • 我最终决定将程序集保存到磁盘上的临时文件夹中,然后将 AppDomain 基础设置为该文件夹。我可能疯了,但我发誓AppDomain.Load 似乎仍会检查磁盘是否存在程序集,而Assembly.Load 不会。事实上,在我当前的实现中,我只是将程序集的全名传递给friendlyDomain.Load,然后它从ApplicationBase 中检索它(如预期的那样)。但是为什么它不经过验证就不会使用字节数组是我无法理解的。
  • 这是正确的。我检查了来源。
【解决方案3】:

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

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

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

【讨论】:

  • It can show you which locations it's trying to probe。就是这样,它不应该像探索它一样。我已经指定了二进制文件,所以没有理由让它再次从文件系统中加载。
  • Primary 依赖于 Dependency。加载Dependency 应该不会导致任何问题,事实上我在上面发现使用Assembly.Load 似乎工作正常,而AppDomain.Load 则不行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-27
  • 1970-01-01
  • 2015-08-23
  • 1970-01-01
  • 2017-10-23
相关资源
最近更新 更多