2012-03-08 4 views
5

그래서 여기 내 작업에서 다른 프로젝트에서 사용되는 DLL을 가져 오는 WPF 프로젝트가 있습니다. 의존성이 혼란 스럽습니다. 여기에이 기술을 사용했습니다 : http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application 종속성을 단일 실행 파일에 포함 시키십시오.Assembly.Load (byte())를 호출 할 때 AssemblyResolve 이벤트가 발생합니다.

이제 종속성 내부에서 특정 메서드를 호출하면 AssemblyResolve 이벤트가 발생합니다. 내 OnResolveAssembly 이벤트가 실행되면 어셈블리가 포함 된 리소스 (멋진 버전)로 어셈블리를 찾고 "Assembly.Load (assembyRawBytes)"를 반환합니다. 이 시점에서 (OnResolveAssembly 시작 부분에 중단 점을 사용하여) F11을 누르면, 같은 이벤트가 호출됩니다. 그것은 같은 어셈블리를 위해서도 사용됩니다 (args.Name은 동일합니다).

이 재귀 이벤트 호출을 피할 수 없기 때문에이 스택을 오버플로 할 수 있습니다.

MSDN 문서는 실제로 FileNotFoundException 또는 BadImageFormatException을 제외하고 Assembly.Load가 실패 할 수 있다고 말하지 않습니다.

Assembly.Load를 호출하기 전에 내가 OnResolveAssembly를 언인 킹하려고했지만 VS에서 신비한 죽음이 생겼다. 멍청이.

여기에 몇 가지 규칙이 있지만 아마도 문제를 찾기 시작할 수있는 몇 가지 아이디어가 좋을 것입니다.

문제가있는 DLL에서 무엇이 잘못 되었는가에 대한 힌트가 있는지 살펴볼 것입니다 (혼합 어셈블리일까요?).

가 여기 내 OnResolveAssembly 핸들러의 : 당분간

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]; 
    } 
} 

, 나는 (내 모든 리소스를 반복하고 그들에 Assembly.Load을 시도하고, 검색을 위해 사전에 저장하여 해결했습니다)이 OnResolveAssembly 이벤트 기간 동안 :

[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; 
} 

그것은 (이하 "실패"어셈블리) 내 두 번째 조각에서 미세한로드 지금 잘 작동 할 것 같다,하지만 나는 그것이 작동하지 않는 이유를 배우고 싶네 먼저.

+2

코드 스 니펫이 필요합니다. –

+0

내가 찾은 내 코드와 해결 방법이 추가되었습니다. –

+0

@JonathanYee AssemblyResolve가 Assembly.Load (byte []) 호출에서 실제로 발생합니까? 이것은 내가 정확히 [여기] (http://stackoverflow.com/questions/24718917/can-a-call-to-assembly-loadbyte-raise-the-appdomain-assemblyresolve-event)를 찾고있는 경우입니다. 나는 그런 예를 만들 수 없었다. 자세한 내용을 알려 주실 수 있습니까? –

답변

3

바이트 []에서 어셈블리를로드하는 것은 .dll hell (너무 많은/복잡한 종속성을 처리하는 곳)에서 끝나는 좋은 방법입니다. 문제는 여기에서 dll을 AppDomain에로드했지만 자동으로 해결되지 않았기 때문에 종속성 유형으로 을 다시 필요합니다. 여기에서이 문제에 대해 의견을 말했습니다. AssemblyResolve Does not fire

요약하면 어셈블리는 AppDomains의 다른 "컨텍스트"에로드됩니다. Load (byte [])에 사용되는 컨텍스트는 어셈블리를 자동으로 확인하지 않습니다.

해결 방법은로드 된 어셈블리를 추적하고 이미로드 된 어셈블리를 두 번째로드하는 대신 반환하는 것입니다. 이 접근법의 시작점은 다음과 같습니다. Need to hookup AssemblyResolve event when DisallowApplicationBaseProbing = true

하지만 문제 해결 방법은 맞습니다.

BTW. 어셈블리를 두 번로드하는 것은 동일하지만 호환되지 않는 형식을 가져 오는 방법입니다. MyAssembly의 MyType 객체를 MyType으로 동일한 어셈블리에서 캐스팅 한 적이 있고 null이 있습니까?

따뜻한 "환영합니다 .dll hell"입니다.

관련 문제