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?