2013-07-24 14 views
5

Eksperymentuję z ładowaniem złożenia przy użyciu tylko tablic bajtowych, ale nie wiem, jak sprawić, by działał poprawnie. Oto konfiguracja:Ładowanie zespołu tablicy bajtowej

public static void Main() 
{ 
    PermissionSet permissions = new PermissionSet(PermissionState.None); 
    AppDomainSetup setup = new AppDomainSetup { ApplicationBase = Environment.CurrentDirectory }; 
    AppDomain friendlyDomain = AppDomain.CreateDomain("Friendly", null, setup, permissions); 

    Byte[] primary = File.ReadAllBytes("Primary.dll_"); 
    Byte[] dependency = File.ReadAllBytes("Dependency.dll_"); 

    // Crashes here saying it can't find the file. 
    friendlyDomain.Load(dependency); 

    AppDomain.Unload(friendlyDomain); 

    Console.WriteLine("Stand successful"); 
    Console.ReadLine(); 
} 

Stworzyłem dwie makiety bibliotek DLL i przemianowany na ich przedłużenie do celowo więc system „.dll_” nie byłby w stanie znaleźć pliki fizyczne. Zarówno primary i dependency wypełnić poprawnie, ale gdy próbuję wywołać metodę AppDomain.Load z danych binarnych, to wraca z:

Could not load file or assembly 'Dependency, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

Dlaczego byłoby przeszukanie systemu do pliku?

UPDATE

to z drugiej strony wydaje się działać:

public class Program { 
    public static void Main() { 
     PermissionSet permissions = new PermissionSet(PermissionState.Unrestricted); 
     AppDomainSetup setup = new AppDomainSetup { ApplicationBase = Environment.CurrentDirectory }; 
     AppDomain friendlyDomain = AppDomain.CreateDomain("Friendly", null, setup, permissions); 

     Byte[] primary = File.ReadAllBytes("Primary.dll_"); 
     Byte[] dependency = File.ReadAllBytes("Dependency.dll_"); 

     // Crashes here saying it can't find the file. 
     // friendlyDomain.Load(primary); 

     Stage stage = (Stage)friendlyDomain.CreateInstanceAndUnwrap(typeof(Stage).Assembly.FullName, typeof(Stage).FullName); 
     stage.LoadAssembly(dependency); 

     Console.WriteLine("Stand successful"); 
     Console.ReadLine(); 
    } 

} 

public class Stage : MarshalByRefObject { 
    public void LoadAssembly(Byte[] data) { 
     Assembly.Load(data); 
    } 
} 

Wygląda więc na to istnieje różnica między AppDomain.Load i Assembly.Load.

+0

Czy biblioteka zależności Dependency nie ma żadnych zależności, które nie zostały skopiowane? –

+0

Podstawowy polega na zależności. Zależność nie ma jednak żadnych zależności (nie od CLR). Wygląda na to, że środowisko wykonawcze nie powinno najpierw wyszukiwać pliku. – sircodesalot

Odpowiedz

9

Jest to normalne, CLR nie uważa „zależność” załadowany być odpowiedni montaż, gdy szuka zespołu, że „pierwotna” potrzebuje. Problem związany z "ładowaniem kontekstu", nie ma takiego dla złożeń ładowanych w ten sposób. Jest to celowe, CLR nie może zagwarantować, że DLL DLL nie będzie problemem, ponieważ nie ma pojęcia, skąd pochodzi zestaw. Odkąd otworzyłeś drzwi do DLLa Hell, musisz także sam uniknąć piekła.

Musisz zaimplementować zdarzenie AppDomain.AssemblyResolve. Uruchomi się, gdy CLR nie znajdzie "zależności", możesz zwrócić zestaw, który otrzymałeś z Assembly.Load (byte []). Będziesz jednak musiał to robić konsekwentnie, gdy będzie on uruchamiał się więcej niż jeden raz dla tego samego zespołu, innymi słowy, zwróci dokładnie to samo Zgromadzenie, lub będziesz mieć więcej problemów wywołanych przez tożsamość typu .NET. Produkując trudne do zrozumienia wyjątki odlewania, "nie można odrzucić stylu Foo do Foo".

Są inne problemy, raczej nieefektywne. Pamięć wirtualna dla zespołu nie może być wspierana przez plik na dysku, dlatego jest on wspierany przez plik stronicowania. Co zwiększa wielkość zatwierdzenia dla twojego procesu.

Z pewnością lepiej tego nie robić.

+0

Dlaczego 'Assembly.Load' wydaje się zezwalać na to, a' AppDomain.Load' nie? Również dobry punkt na wielkość zatwierdzenia. – sircodesalot

+0

To nie ma nic wspólnego z Assembly.Load. Kiedy używasz AssemblyResolve, CLR mówi "Potrzebuję tego" i mówisz "tutaj jest". Tak więc CLR wie, co to jest. Twoje oryginalne podejście brzmiało "Oto jest coś", a CLR mówi "nie mam pojęcia, co to jest". –

+0

Podążam za tym, co mówisz, ale tylko przez zmianę metod wydaje się działać. Nigdy nie musiałem używać 'AssemblyResolve', ponieważ CLR zaakceptował to, gdy pochodziło z' Assembly.Load'. – sircodesalot

0

Jeśli używasz FusionLogViewer można zobaczyć więcej szczegółów na temat konkretnego problemu CLR jest posiadanie w załadunku zespół .... może pokazać, które lokalizacje to próbuje sondować, aby dać wskazówkę, itp

można również obsługiwać zdarzenia AssemblyLoad/AssemblyResolve/ResourceResolve na swoim AppDomain w kodzie, aby śledzić za pośrednictwem sekwencji .

Jest to poręczny przykład, który używa niestandardowych krok MSBuild osadzić wszystkich swoich projektów zespołów zależnych jako zasoby do programu EXE, a następnie użyj AssemblyResolve załadować je z ResourceStream (wykonanie Assembly.Load() w tablicy byte []).

+0

'Może pokazać, które lokalizacje próbuje sondować. Po prostu to nie powinno wydawać się, że to wygląda. Już określam plik binarny, więc nie ma powodu, aby go wychodził i ponownie ładował z systemu plików. – sircodesalot

+0

'Podstawowy' zależy jednak od' Zależności'. Ładowanie 'Dependency' nie powinno powodować żadnych problemów, i faktycznie dowiedziałem się powyżej, że używanie' Assembly.Load' wydaje się działać dobrze, podczas gdy 'AppDomain.Load' nie działa. – sircodesalot

3

Nie ma żadnej różnicy między tymi dwiema metodami (możesz sprawdzić, czy chcesz, official source code).

Na stronie MSDN dla AppDomain.Load Method (Byte[]) jest on zauważył, że ta metoda jest ładowanie zespołu w bieżącej domenie aplikacji:

metoda ta powinna być stosowana tylko do ładowania zespół do bieżącej domenie aplikacji. Ta metoda jest dostępna dla wygody użytkowników wywołujących interakcję, którzy nie mogą wywołać metody static.Load . Aby załadować złożenia do innych domen aplikacji, użyj metody , na przykład CreateInstanceAndUnwrap.

linia:

friendlyDomain.Load(dependency); 

zachowuje się dokładnie tak samo:

Assembly.Load(dependency); 

przyczyną tego, że pracuje w zaktualizowanym przykładowy kod, to dlatego, że obiekt Stage faktycznie dzwoni Assembly.Load Wewnątrz dziecko AppDomain.

Uwaga: Ta odpowiedź uzupełnia odpowiedzi Hansa Passanta i colinsmith.

+0

Ostatecznie zdecydowałem się przejść do zapisywania złożeń w folderze tymczasowym na dysku, a następnie ustawić bazę 'AppDomain' na ten folder. Mogę być szalony, ale przysięgam, że wygląda na to, że 'AppDomain.Load' nadal sprawdza dysk pod kątem fizycznej obecności zespołu, podczas gdy' Assembly.Load' nie. W rzeczywistości, w mojej obecnej implementacji, po prostu przekazuję pełną nazwę zestawu do 'friendlyDomain.Load' i pobiera go z' ApplicationBase' (zgodnie z oczekiwaniami).Ale dlaczego nie weźmie ona tablicy bajtów bez weryfikacji, jest poza mną. – sircodesalot

Powiązane problemy