Chcę załadować do nowego AppDomain
zestawu, który ma złożone drzewo odwołań (MyDll.dll -> Microsoft.Office.Interop.Excel.dll -> Microsoft.Vbe.Interop.dll -> Office.dll -> stdole.dll)
O ile zrozumiałem, kiedy ładowany jest zestaw AppDomain
, jego odwołania nie byłyby ładowane automatycznie i muszę je ładować ręcznie. Więc kiedy to zrobię:
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));
i otrzymałem FileNotFoundException
:
Nie można załadować pliku lub zestawu „MyDll, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null” lub jednej z jego zależności. System nie może odnaleźć określonego pliku.
Myślę, że kluczową częścią jest jedna z jego zależności .
Ok, zrobię to wcześniej domain.Load(AssemblyName.GetAssemblyName(path));
foreach (AssemblyName refAsmName in Assembly.ReflectionOnlyLoadFrom(path).GetReferencedAssemblies())
{
domain.Load(refAsmName);
}
Ale dostałem FileNotFoundException
ponownie, na innym (wymienionym) zestawie.
Jak rekurencyjnie załadować wszystkie odwołania?
Czy muszę utworzyć drzewo odwołań przed załadowaniem zestawu głównego? Jak uzyskać odwołania do zestawu bez ładowania go?
źródło
Odpowiedzi:
Musisz wywołać,
CreateInstanceAndUnwrap
zanim obiekt proxy zostanie wykonany w obcej domenie aplikacji.Należy również zauważyć, że jeśli użyjesz
LoadFrom
, prawdopodobnie otrzymaszFileNotFound
wyjątek, ponieważ program rozpoznawania zestawu spróbuje znaleźć zestaw, który ładujesz w GAC lub w folderze bin bieżącej aplikacji. UżyjLoadFile
zamiast tego do załadowania dowolnego pliku zestawu - ale pamiętaj, że jeśli to zrobisz, będziesz musiał samodzielnie załadować wszelkie zależności.źródło
AppDomain.CurrentDomain.AssemblyResolve
zdarzenia, jak opisano w tej odpowiedzi MSDN . W moim przypadku próbowałem podłączyć się do wdrożenia SpecRun działającego pod MSTest, ale myślę, że ma to zastosowanie do wielu sytuacji, w których Twój kod może nie działać z „podstawowej” domeny AppDomain - rozszerzenia VS, MSTest itp.assembly
zmienna będzie odwoływać się do zestawu z „MyDomain”? Myślę, że przezvar assembly = value.GetAssembly(args[0]);
Ciebie załadujesz swojeargs[0]
do obu domen, aassembly
zmienna odniesie kopię z głównej domeny aplikacjihttp://support.microsoft.com/kb/837908/en-us
Wersja C #:
Utwórz klasę moderatora i odziedzicz ją z
MarshalByRefObject
:zadzwoń ze strony klienta
źródło
MarshalByRefObject
można przekazywać między domenami aplikacji. Więc domyślam się, żeAssembly.LoadFrom
próbuje załadować zestaw w nowej domenie aplikacji, co jest możliwe tylko wtedy, gdy obiekt wywołujący mógłby zostać przekazany między tymi domenami aplikacji. Nazywa się to również zdalnym działaniem, jak opisano tutaj: msdn.microsoft.com/en-us/library/…MarshalByRefObject
nie powoduje magicznego ładowania wszystkich innychAppDomain
, po prostu nakazuje platformie .NET utworzenie przezroczystego zdalnego proxy zamiast korzystania z serializacji podczas rozpakowywania referencji z jednegoAppDomain
w drugimAppDomain
(typowym sposobem jestCreateInstanceAndUnwrap
metoda). Nie mogę uwierzyć, że ta odpowiedź ma ponad 30 głosów za; kod tutaj tylko bezcelowy okrężny sposób dzwonieniaAssembly.LoadFrom
.Po przekazaniu wystąpienia zestawu z powrotem do domeny wywołującego domena wywołującego spróbuje go załadować! Dlatego otrzymujesz wyjątek. Dzieje się to w ostatniej linii kodu:
Zatem cokolwiek chcesz zrobić z zestawem, powinno być zrobione w klasie proxy - klasie, która dziedziczy MarshalByRefObject .
Weź pod uwagę, że domena wywołująca i nowo utworzona domena powinny mieć dostęp do zestawu klasy proxy. Jeśli problem nie jest zbyt skomplikowany, rozważ pozostawienie niezmienionego folderu ApplicationBase, aby był taki sam jak folder domeny wywołującej (nowa domena będzie ładować tylko zestawy, których potrzebuje).
W prostym kodzie:
Jeśli musisz załadować zestawy z folderu, który jest inny niż bieżący folder domeny aplikacji, Utwórz nową domenę aplikacji z określonym folderem ścieżki wyszukiwania bibliotek dll.
Na przykład wiersz tworzenia domeny aplikacji z powyższego kodu powinien zostać zastąpiony przez:
W ten sposób wszystkie biblioteki dll zostaną automatycznie rozwiązane z dllsSearchPath.
źródło
W nowej AppDomain spróbuj ustawić procedurę obsługi zdarzeń AssemblyResolve . To zdarzenie jest wywoływane, gdy brakuje zależności.
źródło
Musisz obsłużyć zdarzenia AppDomain.AssemblyResolve lub AppDomain.ReflectionOnlyAssemblyResolve (w zależności od tego, które ładowanie wykonujesz) w przypadku, gdy zestaw, do którego się odwołujesz, nie znajduje się w GAC lub na ścieżce sondowania CLR.
AppDomain.AssemblyResolve
AppDomain.ReflectionOnlyAssemblyResolve
źródło
Zrozumienie odpowiedzi @ user1996230 zajęło mi trochę czasu, więc zdecydowałem się podać bardziej wyraźny przykład. W poniższym przykładzie tworzę proxy dla obiektu załadowanego do innej domeny AppDomain i wywołuję metodę na tym obiekcie z innej domeny.
źródło
Klucz jest zdarzeniem AssemblyResolve zgłoszonym przez AppDomain.
źródło
Musiałem to zrobić kilka razy i zbadałem wiele różnych rozwiązań.
Rozwiązanie, które uważam za najbardziej eleganckie i łatwe do wykonania, można zaimplementować jako takie.
1. Utwórz projekt, w którym możesz stworzyć prosty interfejs
interfejs będzie zawierał podpisy wszystkich członków, do których chcesz zadzwonić.
Ważne jest, aby projekt był czysty i lekki. Jest to projekt, do którego oba
AppDomain
mogą się odwoływać i który pozwoli nam nie odwoływać się do tego,Assembly
który chcemy załadować w oddzielnej domenie z naszego zestawu klienta.2. Teraz utwórz projekt zawierający kod, który chcesz załadować oddzielnie
AppDomain
.Ten projekt, podobnie jak w przypadku projektu klienta, będzie odnosił się do projektu proxy i zaimplementujesz interfejs.
3. Następnie w projekcie klienta załaduj kod do innego
AppDomain
.Więc teraz tworzymy nowy
AppDomain
. Może określić lokalizację podstawową dla odniesień do zestawów. Sondowanie sprawdzi zależne zestawy w GAC oraz w bieżącym katalogu iAppDomain
bazowym locie.jeśli zajdzie taka potrzeba, istnieje mnóstwo różnych sposobów ładowania zespołu. Z tym rozwiązaniem możesz skorzystać w inny sposób. Jeśli masz nazwę kwalifikowaną zestawu, lubię używać,
CreateInstanceAndUnwrap
ponieważ ładuje bajty zestawu, a następnie tworzy dla Ciebie wystąpienie typu i zwracaobject
, że możesz po prostu rzutować na typ serwera proxy lub jeśli nie, to w silnie wpisanym kodzie, możesz użyj środowiska uruchomieniowego języka dynamicznego i przypisz zwrócony obiekt dodynamic
zmiennej o typie strukturalnym, a następnie bezpośrednio wywołaj członków.Masz to.
Pozwala to na załadowanie zestawu, do którego Twój projekt klienta nie ma odniesienia, w osobnym
AppDomain
i wywołanie członków z klienta.Aby przetestować, lubię używać okna modułów w programie Visual Studio. Pokaże Ci domenę zestawu klienta i jakie wszystkie moduły są ładowane w tej domenie, a także nową domenę aplikacji i jakie zestawy lub moduły są ładowane w tej domenie.
Kluczem jest upewnienie się, że kod pochodzi
MarshalByRefObject
lub można go serializować.`MarshalByRefObject pozwoli ci skonfigurować czas życia domeny, w której się znajduje. Przykład, powiedzmy, że chcesz, aby domena została zniszczona, jeśli serwer proxy nie został wywołany w ciągu 20 minut.
Mam nadzieję, że to pomoże.
źródło
Foo, FooAssembly
która ma właściwość typeBar, BarAssembly
, czyli łącznie 3 zestawy. Czy nadal będzie działać?