Question Charger un assembly par Bytes perd l'emplacement


Je veux charger l'assemblage via le suivant

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

File.ContentAsBytes renvoie le dll en tant que byte[], via le suivant

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

Le problème est l'assembly chargé (loadedAssembly) perd son emplacement physique

  • loadedAssembly.CodeBase - est défini sur l'assembly qui le charge (ce qui n'est pas correct)
  • loadedAssembly.Location - est vide

Y a-t-il un moyen de charger à partir d'un byte[] et obtenir un résultat similaire à Assembly.LoadFile comme j'ai besoin du résultat pour travailler avec le AppDomain.CurrentDomain.AssemblyResolve


14
2018-05-16 20:45


origine


Réponses:


Un tableau d'octets byte[] est simplement un flux d'octets en mémoire. Il n'a aucune corrélation avec aucun fichier. Ce tableau d'octets pourrait avoir été lu à partir d'un fichier, téléchargé à partir d'un serveur Web ou créé spontanément par un générateur de nombres aléatoires. Il n'y a pas de données supplémentaires qui "vont avec".

Si vous souhaitez conserver l'emplacement du fichier à partir duquel le tableau d'octets a été lu, vous devez gérer ces données séparément dans une autre variable. Il n'y a aucun moyen de "joindre" les données supplémentaires au byte[] variable.

Lorsque vous utilisez Assembly.Load pour charger le tableau d'octets en tant qu'assemblage, il est impossible de savoir d'où vient ce tableau d'octets, car les données supplémentaires ne sont pas fournies au serveur. Load fonction.

Pour contourner le problème, existe-t-il un moyen de sauvegarder votre tableau d'octets dans un fichier temporaire, utilisez Assembly.LoadFile pour vous donner les données dont vous avez besoin et lier le Location Retour à votre tableau d'octets d'origine?


5
2018-05-23 03:43



Bien sûr, vous pense cet emplacement aurait une méthode définie quelque part à laquelle vous pourriez accéder ou une autre manière de le modifier. Ce n'est pas Ce qui se passe (j'ai déposé mscorlib.dll dans IL DASM) est que lorsque vous chargez depuis un fichier, il existe un handle natif qu'il associe à l'assembly dans la classe RuntimeAssembly. Lorsque vous appelez le getter Location, il saisit ce handle et vous donne l'emplacement depuis le handle natif, mais seulement s'il y en avait un. Pas de manche, pas d'emplacement.

Voici le 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
2018-05-23 13:28



Une fois que vous passez le byte[] au Assembly.Load méthode, ce tableau d’octets n’a absolument aucune information pour Load méthode d'où il vient - ce n'est qu'un tas d'octets. La même chose s'appliquerait si vous copiiez le fichier dans un emplacement distinct:

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

Le lieu de rassemblement indiquera anotherLocation, même si l'assemblée était à l'origine dllLocation. De même, lorsque vous chargez les octets d'assemblage (en copiant essentiellement l'assembly du disque vers la mémoire), l'emplacement de ces octets est désormais la mémoire.


2
2018-05-16 20:55



J'ai fait face à une situation similaire. Il est rare que j'ai réellement besoin de l'emplacement, mais pour les cas que je fais, j'ai créé une classe d'assistance (AssemblyUtilities) que j'utilise pour charger les octets de l'assemblage et simplement stocker l'emplacement dans un dictionnaire statique. Une méthode auxiliaire supplémentaire (GetLocation) parcourt l'emplacement réel de l'assembly et, s'il n'y en a pas, consulte le dictionnaire. Cela fonctionne bien puisque je suis responsable du chargement des octets bruts de toute façon, et que le peek-through gère les assemblages chargés de manière "traditionnelle". Ainsi...

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);
    }
}

2
2018-04-11 17:15