2013-05-16 9 views
14

ich die Baugruppe über die folgendeLaden eine Anordnung von Bytes verliert den Standort

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

die File.ContentAsBytes gibt die DLL als byte[] über die folgenden

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

laden möchten

Das Problem ist, dass die geladene Baugruppe (loadedAssembly) ihren physikalischen Standort verliert

  • loadedAssembly.CodeBase - wird die Baugruppe eingestellt, die es geladen ist (was nicht korrekt ist)
  • loadedAssembly.Location - ist leer

Gibt es eine Möglichkeit von einem byte[] und bekommt ein laden ähnliches Ergebnis zu Assembly.LoadFile als ich das das Ergebnis müssen mit dem AppDomain.CurrentDomain.AssemblyResolve

+0

Das ist der springende Punkt. Dies hat tiefe Auswirkungen auf die Sicherheit. Eine Assemblyidentität wird nicht nur durch ihre Bytes dargestellt. Wo es geladen wurde (Disk, Internet, etc.) ist entscheidend. Sie können nicht vorgeben, dass eine bestimmte Menge von Byte ist, sagen wir, mscorlib.dll. Der einzige Weg wäre, es in einer temporären Datei zu speichern und von dort zu laden, was mit AssemblyResolve funktionieren könnte, aber das wirft die Frage auf, warum Sie das überhaupt machen wollen? –

+0

Ich weiß nicht, was Ihr Kontext ist, aber ist es nicht eine Option, ein Wörterbuch selbst zu verwalten? –

+0

@SimonMourier die Arbeit herum ist bereits gedacht, und kann in meinem Szenario verwendet werden, aber die Anwendung wäre viel flexibler, wenn ich das obige erreichen könnte. – dbones

Antwort

2

arbeiten Nachdem Sie die byte[] zum Assembly.Load-Methode übergeben, hat das Byte-Array absolut keine Informationen auch die Load Methode Hinweis wHE Es kam von - es ist nur ein paar Bytes. Das gleiche gilt, wenn Sie die Datei in einen anderen Ort kopiert:

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

Der Montageort wird anotherLocation gerichtet sein, auch wenn die Montage ursprünglich auf dllLocation war. Wenn Sie die Assembly-Bytes laden (im Wesentlichen die Assembly von der Festplatte in den Arbeitsspeicher kopieren), ist die "Position" dieser Bytes jetzt der Speicher.

+0

danke, das ist, wie ich das Problem verstanden, aber wenn Sie es aus dem Byte [] laden, ist ihre eine Möglichkeit, irgendwelche Meta-Daten bereitzustellen. Als loadFile muss die Assembly im Speicher geladen werden und irgendwann Metadaten liefern. – dbones

5

Ein Byte-Array byte[] ist einfach ein Stream von Bytes im Speicher. Es hat überhaupt keine Korrelation mit irgendeiner Datei. Dieses Byte-Array könnte aus einer Datei gelesen, von einem Webserver heruntergeladen oder spontan von einem Zufallsgenerator erzeugt worden sein. Es gibt keine zusätzlichen Daten, die "damit einhergehen".

Wenn Sie die Dateiposition beibehalten möchten, von der das Byte-Array ursprünglich gelesen wurde, müssen Sie diese Daten getrennt in einer anderen Variablen verwalten. Es gibt keine Möglichkeit, die zusätzlichen Daten an die Variable byte[] anzuhängen.

Wenn Sie Assembly.Load verwenden, um den Byte-Array als eine Baugruppe zu laden, hat es keine Möglichkeit, zu wissen, wo der Byte-Array herkam, weil die zusätzlichen Daten an die Load Funktion nicht vorgesehen ist.

Als Workaround gibt es eine Möglichkeit, Sie können Ihre Byte-Array in eine temporäre Datei speichern, verwenden Sie Assembly.LoadFile, um Ihnen die Daten, die Sie benötigen, und verknüpfen Sie die Location zurück zu Ihrem ursprünglichen Byte-Array?

+0

danke, bereits bewusst dieser Lösung, auch wurde es von @ Carlosfigueira und in den Kommentaren vorgeschlagen. Beachten Sie, dass das LoadFile dasselbe unter den Abdeckungen ausführen muss. Ich muss nur einen Weg finden, der mit AssemblyResolve funktioniert, außer es gibt eine Alternative zu AssemblyResolve. – dbones

3

Sicher, Sie würden denken, dieser Speicherort hätte eine Methode irgendwo, dass Sie zugreifen könnten oder eine andere Möglichkeit, es zu zwicken. Es tut es nicht. Was passiert (ich habe mscorlib.dll in IL DASM gelöscht) ist, dass beim Laden aus der Datei ein natives Handle vorhanden ist, das mit der Assembly in der Klasse RuntimeAssembly verknüpft ist. Wenn Sie den Location-Getter aufrufen, wird dieser Handle abgerufen und Sie erhalten den Speicherort vom nativen Handle, jedoch nur, wenn es einen gab. Kein Griff, kein Ort.

Hier ist der 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 
2

ich eine ähnliche Situation konfrontiert. Es ist selten, dass ich den Speicherort tatsächlich benötige, aber für die Fälle, die ich tue, habe ich eine Hilfsklasse (AssemblyUtilities) erstellt, mit der ich die Bytes für die Assembly lade und den Speicherort einfach in einem statischen Dictionary ablege. Eine zusätzliche Hilfsmethode (GetLocation) durchsucht den tatsächlichen Standort der Assembly, und wenn es keinen gibt, wird das Wörterbuch konsultiert. Das funktioniert gut, da ich sowieso dafür verantwortlich bin, die Rohbytes zu laden, und die Peek-Through-Handles-Assemblys auf die "traditionelle" Weise geladen werden. Wie 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); 
    } 
} 
+0

Können Sie ein Beispiel von dem, worüber Sie sprechen, veröffentlichen? Diese Antwort ist etwas verwirrend. – BradleyDotNET

+0

Kurz, hier ist, was ich habe: 'öffentliche statische Klasse AssemblyUtilities { private statische Wörterbuch locationByAssembly = ...; public static Assembly LoadFile (Zeichenfolge loc) { byte [] bytes = ReadAllBytes (loc); byte [] pdb = ReadAllBytes (Pfad.ChangeExtension (loc, ".pdb")); Assembly a = ((pdb == null)? Assembly.Load (Byte): Assembly.Load (Byte, Pdb)); locationByAssembly [a] = loc; Rückgabe a; } öffentliche statische Zeichenfolge GetLocation (Assembly a) { Zeichenfolge loc = a.Location; if (loc == null) locationByAssembly.TryGetValue (a, out loc); Rückkehr loc; } } ' – the7thchakra

+0

Verzeihen Sie die beschissene Formatierung. Ich habe das Rückwärtsticket benutzt, aber es hat die Zeilenumbrüche aufgefressen. Meh. – the7thchakra