2017-01-02 1 views
0

Mein Ziel: Ich schreibe eine vsix (Visual Studio-Erweiterung), wo ich ein Projekt kompilieren möchte, laden Sie dann die resultierende DLL und überprüfen Sie es durch Reflexion. Aufgrund der Art, wie der Code geschrieben wird, kann ich ReflectionOnlyLoad() nicht verwenden. Wenn ich einfach Assembly.Load mache, dann ist die Datei gesperrt, bis der Benutzer die gesamte IDE neu startet.Assembly.LoadFrom() oder Assembly.Load() kann Datei löschen

Ich versuche, eine separate AppDomain basierend auf den Beispielen, die ich online gefunden habe, einzurichten.

Der Kern ist: 1. ich einen Proxy-Klasse erstellt, die die Daten über AppDomain Instanzen Marschall würde:

internal class AppDomainProxy : MarshalByRefObject 
    { 
     public Assembly GetAssembly(string assemblyPath) 
     { 
      return Assembly.LoadFile(assemblyPath); 
     } 
} 

ich dann eine Instanz davon: Dieses

var domaininfo = new AppDomainSetup { ApplicationBase = System.Environment.CurrentDirectory, ShadowCopyDirectories = "true", ShadowCopyFiles = "true", LoaderOptimization = LoaderOptimization.MultiDomainHost }; 

      var adevidence = System.AppDomain.CurrentDomain.Evidence; 
      var domain = System.AppDomain.CreateDomain("reflection", adevidence, domaininfo); 

      var proxyType = new AppDomainProxy().GetType(); 

      var proxyInstance = (AppDomainProxy)domain.CreateInstanceFromAndUnwrap(proxyType.Assembly.Location, proxyType.FullName); 

      var loadedAssembly = (proxyInstance as AppDomainProxy).GetAssembly(this._assemblyLocation); 

kann meinen transparenten Proxy nicht an meinen Typ übertragen. Um dies zu umgehen, kann man leicht eine Baugruppe Resolver wie so liefern:

  this.domain.AssemblyResolve += CurrentDomainOnAssemblyResolve; 

private Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args) 
     { 
      var loadedAssemblies = System.AppDomain.CurrentDomain.GetAssemblies(); 

      foreach (var assembly in loadedAssemblies) 
      { 
       if (assembly.FullName == args.Name) 
       { 
        return assembly; 
       } 
      } 

      return null; 
     } 

Dies funktioniert gut, mein Proxy gegossen wird. Wenn ich jedoch meine Methode aufrufen, wird die CurrentDomainOnAssemblyResolve erneut aufgerufen, was darauf hindeutet, dass Assembly Eigenschaft ist nicht wirklich serialisierbar und so. NET versucht einfach die Assembly auf der ursprünglichen Seite zu laden, so dass das gleiche Problem wie Assembly.Load. Das ist leicht zu sehen, weil einfache Microsoft-Beispiele wie gut funktionieren.

UPDATE Zur Umgehung des Problems Ich zog mein Code, der die Montage benötigt auf der anderen Seite (innerhalb der Domäne) laufen und dann die Zeichenfolge wieder zurück, die feinen Marschälle. Ich werde die Frage jedoch offen halten, weil ich wissen möchte, ob es eine Lösung für das eigentliche Problem gibt.

+0

Nun, Sie sind wieder auf Platz eins wieder, weil die Assembly jetzt in _both_ AppDomains geladen ist und Sie nur 'Unload()' einer von ihnen. Warum nicht alles tun, was in Ihrer sekundären AppDomain getan werden muss? – MickyD

+0

@MickyD Nach dem Update auf die Frage, das ist, was ich getan habe. Aber ich mag es nicht:/ – zaitsman

+0

Mag es nicht? Das ist der ganze Sinn von AppDomains. Das Erstellen einer Appdomain zum Isolieren zum Laden einer Assembly oder eines Plugins, nur um sie an die übergeordnete Domäne zurückzugeben, widerspricht genau dem Zweck von Sandboxes, wenn Sie ein untergeordnetes Thread erstellen, um eine langwierige Task auszuführen, aber nur auf die Beendigung warten . Es gibt einige gute Artikel, die ich Ihnen im MSDN Magazin für App-Domains empfohlen habe. – MickyD

Antwort

0

Nun, natürlich müssen Sie den Code, der mit der Baugruppe arbeiten muss, auf die separate AppDomain verschieben. Es gibt keine Magie - wenn Sie mit einem Typ aus einer referenzierten Assembly arbeiten müssen, müssen Sie diese Assembly laden.

Es gibt zwei Hauptmethoden zum Marshallen von Objekten über AppDomain Grenzen (und andere Remoting-Szenarien): Entweder Sie erstellen eine Kopie des Objekts, oder Sie ordnen alle Methodenaufrufe zu. Beide sind sehr schwierig - Sie müssen sicherstellen, dass Sie niemals die Typen auslassen, die sich in der referenzierten Assembly befinden, andernfalls müssen Sie sie in beide Domänen laden. In Ihrem Fall können Sie die Methodenaufrufe nicht marshalden, da Assemblynicht ist (und nicht sein kann) MarshalByRefObject - Ihr Proxy muss ein echtes Assembly-Objekt zurückgeben und den Marshalling-Proxy nicht erstellen können.

Um eine ordnungsgemäße Isolierung zu erhalten, müssen Sie vermeiden, dass Typen aus nicht zu verwendenden Assemblys und nicht marshal- lable Typen, die diese enthalten können, verloren gehen. Dies bedeutet in der Regel, dass man sich aus einer gemeinsamen Bibliothek an Primitive und Typen hält, wenn man sich das leisten kann. Behalten Sie eine nette enge Schnittstelle bei, und versuchen Sie nicht, das "automatische" Marshalling zu sehr auszunutzen - die Rückgabe eines automatischen Proxy an ein komplexes Objekt ist praktisch, macht es aber viel schwieriger, den Umfang der Schnittstelle zu verstehen. Verfügt das Objekt über eine Methode, die einen Typ von der nicht freigegebenen Assembly zurückgibt? Schade, Sie müssen es auch in Ihre Domain laden.

Im Allgemeinen ist die Arbeit mit mehreren AppDomains, die tatsächlich zusammenarbeiten (und das zugehörige .NET-Remoting), ein großer Schmerz.Es ist komplex, fehleranfällig und eher schwer zu bekommen. Sie können ganze Bücher zu diesem Thema lesen. Es gibt einen Grund, warum sie nicht mehr verwendet werden sollten - und warum sie nicht in PCLs unterstützt werden (und lange nicht in Mono unterstützt wurden). Sind sie noch nützlich? Ja. Software-Isolation hat sehr schöne Vorteile. Aber Sie müssen wirklich vorsichtig sein, um Dinge nicht zu vermasseln :)

+0

_ "Es ist komplex, anfällig für Fehler und eher schwer zu bekommen. Sie können ganze Bücher zum Thema lesen" _ - vielleicht. Sie könnten dasselbe über Verschlüsselung sagen; Schadensbasierte Sicherheit; VS Sprachdienste und visuelle Erkennung. Das bedeutet nicht, dass wir nicht einfach wegen ein paar schlecht geschriebenen Büchern aufgeben sollten. Wie für AD, fand ich einige brillante MSDN mag circa 2006 Artikel, die mir sehr geholfen – MickyD

+0

@MickyD Yup, ich sage nicht, dass Sie nicht verwenden sollten. Ich sage, du musst viel lernen und sicherstellen, dass du es richtig machst. Sie werden feststellen, dass Sie in vielen Fällen, in denen Sie App-Domains aufrufen, tatsächlich eine bessere Lösung finden. Und im Idealfall vermeiden Sie es, direkt mit ihnen in Kontakt zu kommen - ein ASP.NET-Programmierer muss fast nie über Anwendungsdomänen nachdenken, obwohl sie für ASP.NET-Programme sehr wichtig sind. Genauso wie Sie Ihren eigenen Verschlüsselungsalgorithmus nicht schreiben würden - Sie verwenden die höchste verfügbare Abstraktion, die Ihr Problem tatsächlich löst. – Luaan

+0

Ich werde es akzeptieren, weil neben dem philosophischen Punkt die Antwort richtig ist - 'Assembly' ist nicht' MarshalByRefObject' und wird somit in beiden App-Domänen geladen. Um dies zu umgehen, habe ich die gesamte Arbeit in der Child-App-Domain erledigt. – zaitsman

Verwandte Themen