2016-08-02 23 views
0

Dies ist etwas kompliziert, aber ich werde versuchen, es so klar wie möglich zu machen. Ich erstelle eine visuelle Studioerweiterung, bei der ein Menüelement zum Kontextmenü hinzugefügt wird, wenn Sie mit der rechten Maustaste auf ein Projekt klicken (und das Projekt stimmt mit bestimmten Kriterien überein). Das Menü soll einen Prozess auslösen, der wird:Erstellen von Domänen und Laden von Assemblys in Visual Studio-Erweiterung

1) Erstellen Sie das Projekt

2) Erstellen Sie eine neue AppDomain

3) Laden Sie die neue Baugruppe in die neue AppDomain

4) Geben Sie ein Objekt aus der neuen Anwendungsdomäne zurück, die in einer dritten Assembly definiert ist (dh nicht die Erweiterungsbaugruppe oder das erstellte Projekt), aber die beiden anderen Elemente referenzieren.

Das Problem, das ich ständig renne, ist mit Nummer 4 und überzeugt die Erweiterung, dass der Typ von der dritten Baugruppe (nennen wir es foo.dll und der Typ FooAssembly) ist der gleiche Typ.

In meiner Erweiterung (nennen wir, dass extension.dll) Ich habe diese:

// assemblyPath is the path to the dll I just build for the selected project 
var setup = new AppDomainSetup() 
{ 
    ApplicationBase = System.IO.Path.GetDirectoryName(assemblyPath) 
}; 

domain = AppDomain.CreateDomain("Test_AppDomain", AppDomain.CurrentDomain.Evidence, setup); 

// FooAssembly is defined in foo.dll and is reference by *both* the project.dll and 
// but the extension.dll it exists both in the assemblyPath folder 
// and typeof(FooAssembly).Assembly.Location which is NOT the same as 
// the current AppDomain's CodeBase because the current CodeBase is the 
// CodeBase of Visual Studio in this context 
var obj = domain.CreateInstanceAndUnwrap(
    typeof(FooAssembly).Assembly.FullName, 
    typeof(FooAssembly).FullName, 
    false, 
    BindingFlags.Default, 
    null, 
    new object[] { assemblyPath, typeof(FooAssembly).Assembly.Location }, null, null); 

// This says the type is MarshalByRefObject 
System.Diagnostics.Debug.WriteLine(obj.GetType().Name); 

// this sets collection to null because it can't cast it 
// which is essentially my problem 
collection = obj as FooAssembly; 

Nun meine FooAssembly, in foo.dll definiert und referenziert sowohl extension.dll und project.dll sieht wie folgt aus:

public class FooAssembly: MarshalByRefObject 
{ 
    private Assembly _assembly; 

    public FooAssembly(string assemblyPath, string parentPath) 
    { 
     AppDomain.CurrentDomain.AssemblyResolve += (o, e) => 
     { 
      // this event never seems to get fired 
      System.Diagnostics.Debug.WriteLine($"attempt to resolve {e.Name} for {e.RequestingAssembly.FullName}"); 
      return null; 
     }; 
     // this didn't seem to help 
     //Assembly.LoadFrom(parentPath);  // load spider assembly from same place (hopefully)... 
     // I've tried LoadFrom as well, with the same result 
     _assembly = Assembly.LoadFile(assemblyPath); 
    } 

    public override object InitializeLifetimeService() 
    { 
     return null; 
    } 
} 

So was geht hier eigentlich vor und wie kann ich mich verhalten? Ich brauche die AppDomain, die ich erstellt habe, um die Assembly zu laden, die ich gebe, und dann laden Sie ihre Abhängigkeiten (oder mindestens foo.dll) von der gleichen Stelle, die die Erweiterung bekommt, denke ich. Oder zumindest muss ich wissen, dass mein transparenter Proxy FooAssembly ist.

EDIT: Ich habe versucht, das Kopieren meine project.dll in den gleichen Ordner wie extension.dll, so dass er hätte von der gleichen Stelle zu laden. Dies machte keinen Unterschied machen, so habe ich versucht, diese Linie in meinen FooAssembly Konstruktor kleben:

System.Diagnostics.Debug.WriteLine(AppDomain.CurrentDomain.FriendlyName); 
System.Diagnostics.Debug.WriteLine(this.GetType().FullName); 
System.Diagnostics.Debug.WriteLine(this.GetType().Assembly.CodeBase); 

Und ich kann sehen, dass a) ich in der geschaffenen Domain bin. b) Ich habe die richtige Klasse und c) Ich habe den richtigen Weg?!? Die CodeBase kommt zurück:

file:///C:/USERS/MATT.BURLAND/APPDATA/LOCAL/MICROSOFT/VISUALSTUDIO/14.0EXP/EXTENSIONS/SOMECO/FOOTOOLS/1.0/Foo.DLL

Also in meiner Standard-Domain nach der Domäne erstellen und bevor Sie versuchen, ein Objekt zu erstellen, das tue ich dies:

System.Diagnostics.Debug.WriteLine(AppDomain.CurrentDomain.FriendlyName); 
System.Diagnostics.Debug.WriteLine(typeof(SpiderAssembly).FullName); 
System.Diagnostics.Debug.WriteLine(typeof(SpiderAssembly).Assembly.CodeBase); 

Und hier meine Code-Basis ist:

file:///C:/USERS/MATT.BURLAND/APPDATA/LOCAL/MICROSOFT/VISUALSTUDIO/14.0EXP/EXTENSIONS/SOMECO/FOOTOOLS/1.0/foo.dll

der einzige Unterschied hier aufwobei das Gehäusevs Foo.DLL, die ich glaube, sollte nicht signifikant sein.

Ich habe versucht, diese (inspiriert von this):

dynamic dobj = obj; 
string l2 = dobj.GetType().Assembly.CodeBase; 
System.Diagnostics.Debug.WriteLine(l2); 

Und es denkt, klar ist es ein MarshalByRefObject von file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/mscorlib.dll geladen, so dass es scheint, als ob es nicht richtig ist auspackt?

Bearbeiten: Nachdem ich mehr darüber gelesen habe, habe ich eine Theorie. Für die foo.dll, die zwischen Domänen geteilt wird, muss es "domain-neutral" geladen werden und meine aktuelle Theorie ist, dass meine Erweiterung wahrscheinlich in der eigenen Domäne von Visual Studio isoliert ist und so foo.dll wird durch mehr Code in einer Weise geladen, die ist nicht Domain neutral, was bedeutet, dass die App-Domäne, die ich erstelle, eine eigene Kopie laden muss. Macht das irgendeinen Sinn? Gibt es einen Weg dahin?

Antwort

1

Nach vielen Suchen und mehreren Versuchen, den Code neu zu ordnen, habe ich schließlich geknackt, als ich die ProvideBindingPathAttribute gefunden habe.

Ich fügte [ProvideBindingPath] zu meiner Paketklasse hinzu und die Probleme, die mich geplagt hatten, gingen weg. Ich brauchte nicht mehr mit der ApplicationBase für meine Domain rumzualbern und konnte einfach AppDomain.CurrentDomain.SetupInformation für die AppDomainSetup für meine neue AppDomain übergeben.