2008-10-22 6 views
36

Ich schreibe ein Tool, um Informationen über .NET-Anwendungen zu berichten, die in Umgebungen und Regionen innerhalb der Systeme meines Kunden bereitgestellt werden.Wie wird eine .NET-Assembly für Reflektionsoperationen geladen und anschließend entladen?

Ich möchte die Werte der Assembly-Attribute in diesen Baugruppen lesen.

Dies kann mit Assembly.ReflectionOnlyLoad erreicht werden, aber auch diese Vorgehensweise hält die Baugruppe geladen. Das Problem hier ist, dass ich nicht zwei Assemblys laden kann, die den gleichen Namen von verschiedenen Pfaden haben, also kann ich natürlich nicht dieselbe Anwendung vergleichen, die in verschiedenen Systemen bereitgestellt wird.

An dieser Stelle gehe ich davon aus, dass die Lösung temporäre AppDomain s verwenden wird.

Kann jemand Detail, wie man eine Versammlung in eine andere AppDomain lädt, die Attribute von ihm liest und die AppDomain dann auslädt?

Dies muss sowohl für Assemblys im Dateisystem als auch für URL-Adressen funktionieren.

Antwort

47

Vom MSDN documentation of System.Reflection.Assembly.ReflectionOnlyLoad (String):

Die reflexions nur Kontext ist kein unterscheidet sich von anderen Kontexten. Baugruppen, die in den -Kontext geladen werden, können nur durch entladen werden, indem die Anwendungsdomäne entladen wird.

So, ich fürchte, die einzige Möglichkeit zum Entladen einer Baugruppe ist das Entladen der Anwendungsdomäne. Um eine neue AppDomain und Last-Baugruppen in sie zu schaffen:

public void TempLoadAssembly() 
{ 
    AppDomain tempDomain = AppDomain.CreateDomain("TemporaryAppDomain"); 
    tempDomain.DoCallBack(LoaderCallback); 
    AppDomain.Unload(tempDomain); 
} 

private void LoaderCallback() 
{ 
    Assembly.ReflectionOnlyLoad("YourAssembly"); 
    // Do your stuff here 
} 
+0

Danke, das wie ein nützliches Muster aussieht. –

+1

Wird die Assembly in Assembly.RefrectionOnlyLoad (...) immer noch in der aktuellen Domäne und nicht in der temporärenAppDomain geladen? – Anzurio

+3

AZ: Nein. AppDomain.DoCallback "speichert den Code in einer anderen Anwendungsdomäne, die vom angegebenen Delegaten identifiziert wird" (MSDN). Die Dokumentation von Assembly.ReflectionOnlyLoad gibt eindeutig an, dass "die Assembly in den reflection-only-Kontext der Anwendungsdomäne des Aufrufers geladen wird" (wiederum MSDN). Das bedeutet, dass die Assembly tatsächlich in die temporäre App-Domäne geladen wird. –

4

Sie können versuchen, Unmanaged Metadata API zu verwenden, die COM ist und leicht von .NET-Anwendung verwendet werden kann, mit irgendeiner Art von Verpackung.

+0

Hallo Ilya. Danke für diesen Link. Ich werde diese Arbeit in Kürze wiederholen und werde diese API definitiv ausprobieren. Ich hoffe, es ist effizienter als das Laden der Baugruppe. –

3

Sie müssen Anwendungsdomänen verwenden, es gibt keine andere Möglichkeit, eine Assembly zu entladen. Grundsätzlich müssen Sie folgenden Code verwenden:

 

AppDomain tempDomain = AppDomain.CreateDomain("Temp Domain"); 
tempDomain.Load(assembly); 
AppDomain.Unload(tempDomain); 

9

Auch wenn sie nicht wirklich über Baugruppen Entladen, wenn Sie nur versuchen, die Versionsnummer einer Datei erhalten Sie System.Diagnostics.FileVersionInfo verwenden können.

var info = FileVersionInfo.GetVersionInfo(path); 

FileVersionInfo weist folgende Eigenschaften auf:

public string Comments { get; } 
public string CompanyName { get; } 
public int FileBuildPart { get; } 
public string FileDescription { get; } 
public int FileMajorPart { get; } 
public int FileMinorPart { get; } 
public string FileName { get; } 
public int FilePrivatePart { get; } 
public string FileVersion { get; } 
public string InternalName { get; } 
public bool IsDebug { get; } 
public bool IsPatched { get; } 
public bool IsPreRelease { get; } 
public bool IsPrivateBuild { get; } 
public bool IsSpecialBuild { get; } 
public string Language { get; } 
public string LegalCopyright { get; } 
public string LegalTrademarks { get; } 
public string OriginalFilename { get; } 
public string PrivateBuild { get; } 
public int ProductBuildPart { get; } 
public int ProductMajorPart { get; } 
public int ProductMinorPart { get; } 
public string ProductName { get; } 
public int ProductPrivatePart { get; } 
public string ProductVersion { get; } 
public string SpecialBuild { get; } 
1

Sie können eine Instanz in der neuen AppDomain erstellen und den Code in diesem Fall ausführen.

var settings = new AppDomainSetup 
{ 
    ApplicationBase = AppDomain.CurrentDomain.BaseDirectory, 
}; 
var childDomain = AppDomain.CreateDomain(Guid.NewGuid().ToString(), null, settings); 

var handle = Activator.CreateInstance(childDomain, 
      typeof(ReferenceLoader).Assembly.FullName, 
      typeof(ReferenceLoader).FullName, 
      false, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, null, CultureInfo.CurrentCulture, new object[0]); 


var loader = (ReferenceLoader)handle.Unwrap(); 

//This operation is executed in the new AppDomain 
var paths = loader.LoadReferences(assemblyPath); 


AppDomain.Unload(childDomain); 

Hier ist die ReferenceLoader

public class ReferenceLoader : MarshalByRefObject 
{ 
    public string[] LoadReferences(string assemblyPath) 
    { 
     var assembly = Assembly.ReflectionOnlyLoadFrom(assemblyPath); 
     var paths = assembly.GetReferencedAssemblies().Select(x => x.FullName).ToArray(); 
     return paths; 
    } 
} 
+0

Perfect - hatte eine kleine Änderung machen in LINQPad laufen wie folgt: 'var Einstellungen = new AppDomainSetup { \t \t Application = Path.GetDirectoryName (this.GetType() Assembly.Location.) \t};' –

Verwandte Themen