Ich möchte eine AppDomain
Assembly mit einem komplexen Referenzbaum in eine neue Assembly laden (MyDll.dll -> Microsoft.Office.Interop.Excel.dll -> Microsoft.Vbe.Interop.dll -> Office.dll -> stdole.dll).
Soweit ich verstanden habe, werden beim Laden einer Assembly AppDomain
ihre Referenzen nicht automatisch geladen, und ich muss sie manuell laden. Also, wenn ich es tue:
string dir = @"SomePath"; // different from AppDomain.CurrentDomain.BaseDirectory
string path = System.IO.Path.Combine(dir, "MyDll.dll");
AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
setup.ApplicationBase = dir;
AppDomain domain = AppDomain.CreateDomain("SomeAppDomain", null, setup);
domain.Load(AssemblyName.GetAssemblyName(path));
und bekam FileNotFoundException
:
Datei oder Assembly 'MyDll, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null' oder eine ihrer Abhängigkeiten konnte nicht geladen werden. Die angegebene Datei wurde vom System nicht gefunden.
Ich denke, der Schlüsselteil ist eine seiner Abhängigkeiten .
Ok, das mache ich als nächstes vorher domain.Load(AssemblyName.GetAssemblyName(path));
foreach (AssemblyName refAsmName in Assembly.ReflectionOnlyLoadFrom(path).GetReferencedAssemblies())
{
domain.Load(refAsmName);
}
Aber FileNotFoundException
wieder auf eine andere (referenzierte) Versammlung.
Wie lade ich alle Referenzen rekursiv?
Muss ich einen Referenzbaum erstellen, bevor ich die Root-Assembly lade? Wie erhalte ich die Referenzen einer Baugruppe, ohne sie zu laden?
quelle
Antworten:
Sie müssen aufrufen,
CreateInstanceAndUnwrap
bevor Ihr Proxy-Objekt in der fremden Anwendungsdomäne ausgeführt wird.Beachten Sie außerdem, dass bei Verwendung
LoadFrom
wahrscheinlich eineFileNotFound
Ausnahme auftritt, da der Assembly-Resolver versucht, die zu ladende Assembly im GAC oder im bin-Ordner der aktuellen Anwendung zu finden. Verwenden SieLoadFile
stattdessen, um eine beliebige Assembly-Datei zu laden. Beachten Sie jedoch, dass Sie in diesem Fall alle Abhängigkeiten selbst laden müssen.quelle
AppDomain.CurrentDomain.AssemblyResolve
Ereignis anhängen muss, wie in dieser MSDN-Antwort beschrieben . In meinem Fall habe ich versucht, mich in die unter MSTest ausgeführte SpecRun-Bereitstellung einzubinden, aber ich denke, dies gilt für viele Situationen, in denen Ihr Code möglicherweise nicht über die "primäre" AppDomain ausgeführt wird - VS-Erweiterungen, MSTest usw.assembly
Variable auf die Assembly von "MyDomain" verweist? Ich denke,var assembly = value.GetAssembly(args[0]);
Sie werden Ihreargs[0]
in beide Domänen laden undassembly
Variable wird Kopie von der Hauptanwendungsdomänehttp://support.microsoft.com/kb/837908/en-us
C # -Version:
Erstellen Sie eine Moderatorklasse und erben Sie sie von
MarshalByRefObject
:Anruf vom Kundenstandort
quelle
MarshalByRefObject
kann um Appdomains herumgereicht werden. Ich würde also vermuten, dassAssembly.LoadFrom
versucht wird, die Assembly in eine neue Appdomain zu laden, was nur möglich ist, wenn das aufrufende Objekt zwischen diesen Appdomains übergeben werden könnte. Dies wird auch als Remoting bezeichnet, wie hier beschrieben: msdn.microsoft.com/en-us/library/…MarshalByRefObject
es nicht auf magische Weise in jedes andere geladen.AppDomain
Es weist das .NET Framework lediglich an, einen transparenten Remoting-Proxy zu erstellen, anstatt die Serialisierung zu verwenden, wenn Sie die ReferenzAppDomain
voneinander entfernenAppDomain
(die typische Methode ist dieCreateInstanceAndUnwrap
Methode). Ich kann nicht glauben, dass diese Antwort über 30 positive Stimmen hat. Der Code hier ist nur ein sinnloser UmwegAssembly.LoadFrom
.Sobald Sie die Assembly-Instanz an die Anruferdomäne zurückgeben, versucht die Anruferdomäne, sie zu laden! Aus diesem Grund erhalten Sie die Ausnahme. Dies geschieht in Ihrer letzten Codezeile:
Was auch immer Sie mit der Assembly tun möchten, sollte in einer Proxy-Klasse ausgeführt werden - einer Klasse, die MarshalByRefObject erbt .
Berücksichtigen Sie, dass sowohl die Anruferdomäne als auch die neu erstellte Domäne Zugriff auf die Proxyklassenassembly haben sollten. Wenn Ihr Problem nicht zu kompliziert ist, sollten Sie den ApplicationBase-Ordner unverändert lassen, damit er mit dem Ordner der Anruferdomäne identisch ist (die neue Domäne lädt nur die benötigten Assemblys).
In einfachem Code:
Wenn Sie die Assemblys aus einem Ordner laden müssen, der sich von Ihrem aktuellen App-Domänenordner unterscheidet, erstellen Sie die neue App-Domäne mit einem bestimmten DLL-Suchpfadordner.
Beispielsweise sollte die Erstellungszeile für die App-Domäne aus dem obigen Code ersetzt werden durch:
Auf diese Weise werden alle DLLs automatisch aus dllsSearchPath aufgelöst.
quelle
Versuchen Sie in Ihrer neuen AppDomain, einen AssemblyResolve- Ereignishandler festzulegen . Dieses Ereignis wird aufgerufen, wenn eine Abhängigkeit fehlt.
quelle
Sie müssen die Ereignisse AppDomain.AssemblyResolve oder AppDomain.ReflectionOnlyAssemblyResolve (je nachdem, welche Last Sie ausführen) behandeln, falls sich die referenzierte Assembly nicht im GAC oder im Prüfpfad der CLR befindet.
AppDomain.AssemblyResolve
AppDomain.ReflectionOnlyAssemblyResolve
quelle
Ich habe eine Weile gebraucht, um die Antwort von @ user1996230 zu verstehen, und habe mich daher entschlossen, ein expliziteres Beispiel zu liefern. Im folgenden Beispiel erstelle ich einen Proxy für ein Objekt, das in eine andere AppDomain geladen wurde, und rufe eine Methode für dieses Objekt aus einer anderen Domäne auf.
quelle
Der Schlüssel ist das AssemblyResolve-Ereignis, das von der AppDomain ausgelöst wird.
quelle
Ich musste dies mehrmals tun und habe viele verschiedene Lösungen recherchiert.
Die Lösung, die ich am elegantesten und am einfachsten zu realisierenden finde, kann als solche implementiert werden.
1. Erstellen Sie ein Projekt, mit dem Sie eine einfache Schnittstelle erstellen können
Die Schnittstelle enthält Signaturen aller Mitglieder, die Sie anrufen möchten.
Es ist wichtig, dieses Projekt sauber und einfach zu halten. Es ist ein Projekt, auf das beide
AppDomain
verweisen können und das es uns ermöglicht, nicht auf das zu verweisen, dasAssembly
wir in einer separaten Domäne von unserer Client-Assembly laden möchten.2. Erstellen Sie nun ein Projekt mit dem Code, den Sie separat laden möchten
AppDomain
.Dieses Projekt verweist wie das Client-Projekt auf das Proxy-Projekt und Sie implementieren die Schnittstelle.
3. Laden Sie als Nächstes im Client-Projekt den Code in einen anderen
AppDomain
.Also, jetzt schaffen wir eine neue
AppDomain
. Kann den Basisort für Baugruppenreferenzen angeben. Bei der Prüfung wird nach abhängigen Assemblys im GAC und im aktuellen Verzeichnis sowie in derAppDomain
Basislok gesucht.Wenn nötig, gibt es unzählige Möglichkeiten, eine Baugruppe zu laden. Mit dieser Lösung können Sie einen anderen Weg wählen. Wenn Sie den Assembly-qualifizierten Namen haben, verwende ich gerne den,
CreateInstanceAndUnwrap
da er die Assembly-Bytes lädt und dann Ihren Typ für Sie instanziiert und einen zurückgibtobject
, den Sie einfach in Ihren Proxy-Typ umwandeln können, oder wenn Sie dies nicht in stark typisierten Code tun könnten Verwenden Sie die dynamische Sprachlaufzeit und weisen Sie das zurückgegebene Objekt einerdynamic
typisierten Variablen zu. Rufen Sie dann einfach die Mitglieder direkt auf.Hier hast du es.
Auf diese Weise können Sie eine Assembly laden, auf die Ihr Client-Projekt in einem separaten Verzeichnis nicht verweist,
AppDomain
und Mitglieder vom Client darauf aufrufen.Zum Testen verwende ich gerne das Modulfenster in Visual Studio. Es zeigt Ihnen Ihre Client-Assembly-Domäne und was alle Module in dieser Domäne geladen sind sowie Ihre neue App-Domäne und welche Assemblys oder Module in dieser Domäne geladen sind.
Der Schlüssel besteht darin, entweder sicherzustellen, dass der Code entweder abgeleitet
MarshalByRefObject
oder serialisierbar ist.Mit MarshalByRefObject können Sie die Lebensdauer der Domäne konfigurieren, in der sie sich befindet. Beispiel: Sie möchten, dass die Domäne zerstört wird, wenn der Proxy nicht innerhalb von 20 Minuten aufgerufen wurde.
Ich hoffe das hilft.
quelle
Foo, FooAssembly
mit einer Eigenschaft vom Typ zurückgegeben wirdBar, BarAssembly
, dh insgesamt 3 Assemblys. Würde es weiter funktionieren?