通过Bytes加载程序集会丢失该位置

我想通过以下方式加载程序集

var loadedAssembly = Assembly.Load(File.ContentsAsBytes);

File.ContentAsBytes通过以下方式将dll作为byte[]返回

System.IO.File.ReadAllBytes("dll location");

问题是加载的程序集( loadedAssembly )失去了它的物理位置

  • loadedAssembly.CodeBase – 设置为正在加载它的程序集(这是不正确的)
  • loadedAssembly.Location – 为空

有没有办法从byte[]加载并获得类似的结果到Assembly.LoadFile因为我需要结果与AppDomain.CurrentDomain.AssemblyResolve

字节数组byte[]只是内存中的字节流。 它与任何文件都没有任何关联。 该字节数组可以从文件中读取,从Web服务器下载,或由随机数生成器自发创建。 没有额外的数据“随之而来”。

如果要维护最初从中读取字节数组的文件位置,则必须在另一个变量中单独维护该数据。 没有办法将额外数据“附加”到byte[]变量。

当您使用Assembly.Load将字节数组作为程序集加载时,它无法知道该字节数组的来源,因为该额外数据未提供给Load函数。

作为一种解决方法,有没有办法可以将字节数组保存到临时文件中,使用Assembly.LoadFile为您提供所需的数据并将Location链接回原始字节数组?

当然,你认为 Location会有一个你可以访问的设置方法或其他一些方法来调整它。 它没有。 发生了什么(我将mscorlib.dll丢弃到IL DASM中)是当你从文件加载时,有一个本机句柄与RuntimeAssembly类中的程序集相关联。 当你调用Location getter时,它会抓取这个句柄并从原生句柄中获取位置,但前提是它只有一个。 没有把手,没有位置。

这是IL:

 .method public hidebysig specialname virtual instance string get_Location() cil managed { .custom instance void System.Security.SecuritySafeCriticalAttribute::.ctor() = ( 01 00 00 00 ) // Code size 37 (0x25) .maxstack 3 .locals init (string V_0) IL_0000: ldnull IL_0001: stloc.0 IL_0002: ldarg.0 IL_0003: call instance class System.Reflection.RuntimeAssembly System.Reflection.RuntimeAssembly::GetNativeHandle() IL_0008: ldloca.s V_0 IL_000a: call valuetype System.Runtime.CompilerServices.StringHandleOnStack System.Runtime.CompilerServices.JitHelpers::GetStringHandleOnStack(string&) IL_000f: call void System.Reflection.RuntimeAssembly::GetLocation(class System.Reflection.RuntimeAssembly, valuetype System.Runtime.CompilerServices.StringHandleOnStack) IL_0014: ldloc.0 IL_0015: brfalse.s IL_0023 IL_0017: ldc.i4.8 IL_0018: ldloc.0 IL_0019: newobj instance void System.Security.Permissions.FileIOPermission::.ctor(valuetype System.Security.Permissions.FileIOPermissionAccess, string) IL_001e: call instance void System.Security.CodeAccessPermission::Demand() IL_0023: ldloc.0 IL_0024: ret } // end of method RuntimeAssembly::get_Location 

一旦你将byte[]传递给Assembly.Load方法,该字节数组完全没有信息甚至提示它来自的Load方法 – 它只是一堆字节。 如果将文件复制到单独的位置,则同样适用:

 File.Copy(dllLocation, anotherLocation); var asm = Assembly.LoadFile(anotherLocation); 

即使程序集最初位于dllLocation上,程序集位置也将指向dllLocation 。 类似地,当您加载程序集字节(基本上将程序集从磁盘复制到内存)时,这些字节的“位置”现在是内存。

我遇到了类似的情况。 我很少需要这个位置,但对于我所做的情况,我创建了一个帮助类(AssemblyUtilities),我用它来加载程序集的字节,并将位置存储在静态字典中。 另一个辅助方法(GetLocation)可以查看程序集的实际位置,如果没有,则查询字典。 这样可以正常工作,因为无论如何我负责加载原始字节,并且以“传统”方式加载peek-through处理程序集。 像这样……

 public static class AssemblyUtilities { private static Dictionary locationByAssembly = new Dictionary(); private static Dictionary assemblyByLocation = new Dictionary(StringComparer.OrdinalIgnoreCase); public static Assembly LoadFile(string location) { Assembly assembly; lock (locationByAssembly) { if (!assemblyByLocation.TryGetValue(location, out assembly)) { byte[] bytes = ReadAllBytes(location); if (bytes == null) return null; byte[] pdb = ReadAllBytes(Path.ChangeExtension(location, ".pdb")); assembly = ((pdb == null)? Assembly.Load(bytes): Assembly.Load(bytes, pdb)); locationByAssembly[assembly] = location; assemblyByLocation[location] = assembly; } return assembly; } } public static string GetLocation(Assembly assembly) { if (assembly == null) return null; string location = assembly.Location; if (location == null) locationByAssembly.TryGetValue(assembly, out location); return location; } private static byte[] ReadAllBytes(string path) { try { return File.ReadAllBytes(path); } catch { return null; } } } // And if you prefer extensions... public static class AssemblyExtensions { public static string GetLocation(this Assembly self) { return AssemblyUtilities.GetLocation(self); } }