Loading an assembly by Bytes loses the location

旧时模样 提交于 2019-12-23 06:58:40

问题


I want to load the assembly via the following

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

the File.ContentAsBytes returns the dll as a byte[], via the following

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

The issue is the loaded assembly (loadedAssembly) loses its phyisical location

  • loadedAssembly.CodeBase - is set to the assembly which is loading it (which is not correct)
  • loadedAssembly.Location - is empty

Is there a way to load from a byte[] and getting a similar result to Assembly.LoadFile as I need the the result to work with the AppDomain.CurrentDomain.AssemblyResolve


回答1:


A byte array byte[] is simply a stream of bytes in memory. It has no correlation to any file at all. That byte array could have been read from file, downloaded from a web server, or created spontaneously by a random number generator. There is no extra data that "goes with it".

If you want to maintain the file location where the byte array was originally read from, then you must maintain that data separately in another variable. There is no way to "attach" the extra data to the byte[] variable.

When you use Assembly.Load to load the byte array as an assembly, it has no way to know where that byte array came from, because that extra data is not provided to the Load function.

As a workaround, is there a way you can save your byte array to a temporary file, use Assembly.LoadFile to give you the data you need and link the Location back to your original byte array?




回答2:


Sure, you'd think that Location would have a set method somewhere that you could access or some other way to tweak it. It doesn't. What happens (I dropped mscorlib.dll into IL DASM) is that when you load from file, there is a native handle that it associated with the assembly in the class RuntimeAssembly. When you call the Location getter, it grabs this handle and gives you the location from the native handle, but only if there was one. No handle, no location.

Here's the 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



回答3:


Once you pass the byte[] to the Assembly.Load method, that byte array has absolutely no information to even hint the Load method where it came from - it's only a bunch of bytes. The same would apply if you copied the file to a separate location:

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

The assembly location will be pointing to anotherLocation, even though the assembly originally was on dllLocation. Similarly, when you load the assembly bytes (essentially copying the assembly from the disk to memory), the "location" of those bytes is now the memory.




回答4:


I faced a similar situation. It's rare that I actually need the location, but for the cases that I do, I created a helper class (AssemblyUtilities) that I use to load the bytes for the assembly and just store the location in a static Dictionary. An additional helper method (GetLocation) peeks through to the assembly's actual location, and if there isn't one, consults the dictionary. This works fine since I'm responsible for loading the raw bytes anyway, and the peek-through handles assemblies loaded in the "traditional" way. Like so...

public static class AssemblyUtilities {
    private static Dictionary<Assembly, string> locationByAssembly =
        new Dictionary<Assembly, string>();

    private static Dictionary<string, Assembly> assemblyByLocation =
        new Dictionary<string, Assembly>(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);
    }
}


来源:https://stackoverflow.com/questions/16597138/loading-an-assembly-by-bytes-loses-the-location

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!