2012-03-08 7 views
5

Mam projekt WPF, który pobiera biblioteki dll, które są używane przez inny projekt tutaj w mojej pracy. To bałagan zależności, używałem techniki tutaj: http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application do osadzenia zależności w jednym pliku wykonywalnym.Zdarzenie AssemblyResolve jest wywoływane podczas wywoływania Assembly.Load (byte())

Teraz, gdy wywołuję określoną metodę wewnątrz jednej z zależności, trafiam na zdarzenie AssemblyResolve. Moje zdarzenie OnResolveAssembly działa, znajduje zestaw jako zasób osadzony (cool!) I "zwraca Assembly.Load (assembyRawBytes)". Jeśli uderzę w F11 w tym momencie (z punktem przerwania na początku OnResolveAssembly), otrzymam kolejne połączenie w to samo zdarzenie. To samo dotyczy tego samego zestawu (args.Name jest takie samo).

Jeśli pozwolę temu biegowi, trafiam w stos przepełniony, ponieważ nie mogę uciec przed tym rekurencyjnym wywołaniem zdarzeń.

Dokumenty MSDN tak naprawdę nie mówią, kiedy Assembly.Load może się nie udać, z wyjątkiem wyjątków FileNotFoundException lub BadImageFormatException.

Próbowałem odłączyć OnResolveAssembly w tej chwili, zanim zadzwonię Assembly.Load, ale wtedy moja aplikacja umiera tajemniczą śmierć, nawet pod VS to po prostu idzie pufa.

Prawdopodobnie łamiemy kilka zasad tutaj, ale niektóre pomysły na to, gdzie zacząć szukać problemów byłyby mile widziane.

Mam zamiar zacząć grzebać w problematycznej bibliotece DLL, aby sprawdzić, czy istnieją wskazówki na temat tego, co jest z nim nie tak (może to mieszany zestaw?).

Oto mój OnResolveAssembly obsługi:

private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args) 
{ 
    Assembly executingAssembly = Assembly.GetExecutingAssembly(); 
    AssemblyName assemblyName = new AssemblyName(args.Name); 

    string path = assemblyName.Name + ".dll"; 

    if (assemblyName.CultureInfo.Equals(System.Globalization.CultureInfo.InvariantCulture) == false) 
    { 
     path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path); 
    } 
    using (Stream stream = executingAssembly.GetManifestResourceStream(path)) 
    { 
     if (stream == null) 
      return null; 

     byte[] assemblyRawBytes = new byte[stream.Length]; 
     stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length); 
     assemblyDictionary.Add(assemblyName.Name, Assembly.Load(assemblyRawBytes)); 
     return assemblyDictionary[assemblyName.Name]; 
    } 
} 

Na razie, mam rozwiązane przez iteracja wszystkich moich zasobów i próby Assembly.Load na nich i przechowywanie ich w słowniku dla pobierania (podczas OnResolveAssembly zdarzenia):

[STAThread] 
public static void Main() 
{ 
    AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly; 
    Assembly executingAssembly = Assembly.GetExecutingAssembly(); 
    string[] resources = executingAssembly.GetManifestResourceNames(); 
    foreach (string resource in resources) 
    { 
     if (resource.EndsWith(".dll")) 
     { 
      using (Stream stream = executingAssembly.GetManifestResourceStream(resource)) 
      { 
       if (stream == null) 
        continue; 

       byte[] assemblyRawBytes = new byte[stream.Length]; 
       stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length); 
       try 
       { 
        assemblyDictionary.Add(resource, Assembly.Load(assemblyRawBytes)); 
       } 
       catch (Exception ex) 
       { 
        System.Diagnostics.Debug.Print("Failed to load: " + resource + " Exception: " + ex.Message); 
       } 
      } 
     } 
    } 
    App.Main(); 
} 

private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args) 
{ 
    Assembly executingAssembly = Assembly.GetExecutingAssembly(); 
    AssemblyName assemblyName = new AssemblyName(args.Name); 

    string path = assemblyName.Name + ".dll"; 

    if (assemblyDictionary.ContainsKey(path)) 
    { 
     return assemblyDictionary[path]; 
    } 
    return null; 
} 

wydaje się być obecnie pracuje bez zarzutu (dalej „braku” montaż zostanie załadowany prawidłowo w moim drugim fragmencie), ale byłbym zainteresowany, aby dowiedzieć się, dlaczego to nie działa w pierwszy.

+2

Fragment kodu jest wymagany. –

+0

Dodano mój kod i obejście, które odkryłem. –

+0

@ Jonathan Yee Czy naprawdę dostajesz 'AssemblyResolve' podniesiony od wewnątrz wywołanie' Assembly.Load (byte []) '? Dokładnie to właśnie szukam [tutaj] (http://stackoverflow.com/questions/24718917/can-a-call-to-assembly-loadbyte-raise-the-appdomain-assemblyresolve-event), ale Nie mogłem zbudować takiego przykładu. Czy mógłbyś podać więcej szczegółów? –

Odpowiedz

3

Ładowanie zespołu z bajtu [] jest dobrym sposobem na znalezienie się w .dll hell (miejsce, w którym znajduje się zbyt wiele/złożonych zależności). Problem polega na tym, że chociaż załadowałeś bibliotekę dll do AppDomain, to nie jest ona automatycznie rozwiązywana, kiedy potrzebujesz jej ponownie dla typów zależnych. Skomentowałem ten problem: AssemblyResolve Does not fire

Krótko mówiąc, zjazdy są ładowane do różnych "kontekstów" wewnątrz AppDomains. Kontekst użyty przez Load (byte []) nie rozwiązuje automatycznie Zespołów.

Rozwiązaniem jest śledzenie załadowanych zespołów i zwracanie już załadowanego zespołu, zamiast ładowania go po raz drugi. Istnieje punkt wyjścia do tego podejścia w mojej odpowiedzi na: Need to hookup AssemblyResolve event when DisallowApplicationBaseProbing = true

Ale myślę, że masz to dobrze z twoim obejściem.

BTW. Dwukrotne ładowanie zestawu jest sposobem uzyskania identycznych, ale niekompatybilnych typów. Czy kiedykolwiek rzuciłeś obiekt MyType z MyAssembly do MyType z tego samego zestawu i uzyskałeś wartość null?

To jest ciepłe "Witamy w piekle .dll".

Powiązane problemy