2012-04-02 2 views
4

어셈블리가 Application Base 디렉터리와 다른 경로에 있으므로 어셈블리를 Assembly.LoadFrom()을 사용하여로드하고 있습니다.Assembly.LoadFrom 이후 ResolveEventHandler

Dim oAssembly As Assembly = _ 
Assembly.LoadFrom("C:\\MyFolder\\" + ddlXlate.SelectedItem.ToString() + ".dll") 

그리고 나는 아무 문제없이 해당 어셈블리에서 Type 소비 : 나는 아래와 같은 다른 방법 내에서이 어셈블리에서 Type를 사용하려고하면

oXML = CType(oAssembly.CreateInstance(sBaseType + ".XlateContainer"), _ 
XlateBase.XlateContainer) 

그러나, 문제가 발생합니다 :

oComboBox.DataSource = _ 
[Enum].GetValues(Type.GetType(sType + "+ItemEnum," + sAssemblyName)) 

sAssemblyName

내가 실제로 LoadFrom()를 사용하여로드 하나입니다. 그것이 어셈블리를 찾을 수 없다고 후, 나는 내 문제를 해결 AssemblyResolve 이벤트를 사용 :

AddHandler AppDomain.CurrentDomain.AssemblyResolve, _ 
AddressOf MyResolveEventHandler 

이벤트 처리기 방법 :

Private Shared Function MyResolveEventHandler(ByVal sender As Object, _ 
    ByVal args As ResolveEventArgs) As Assembly 
    Return Assembly.LoadFrom("C:\\PSIOBJ\\" + args.Name + ".dll") 
End Function 

그리고

AssemblyResolve 이벤트를 구독 그게 아마도를 사용하여로드 된 어셈블리 매니페스트 파일에 정의 된 종속 어셈블리를 찾을 수 없기 때문에 오류가 발생했을 가능성이 있습니다.이미 있지만, 내가 args.Name을 검사했을 때, 나는 그것이 동일한 어셈블리를로드하려고 시도하는 것을 보았고 아무런 문제없이 작동했다. 기본적으로로드 된 어셈블리의 유형은 이벤트를 변경하기 전에 찾을 수 없습니다.

내 이전 코드는 AppDomain.CurrentDomain.Load()Assembly.Load() 메서드를 사용하고 있었고 AssemblyResolve 이벤트 없이는 정상적으로 작동했습니다. 동적으로로드 된 유형 Assembly에 도달 할 수있었습니다. 같은 위치의 모든 곳에서 AppDomain.

LoadFrom()은 동일한 요청 된 어셈블리 경로 내에서 자동으로 종속성을 찾을 수 있으며이 모든 것이 문제가되지 않을 수도 있습니다. dll 필요합니다. 그래서 처음에는 AppDomain처럼 보였으므로 컨텍스트 대신 Load 컨텍스트에서 어셈블리에 도달 할 수있는 것처럼 보입니다. 지금은 LoadFrom 컨텍스트를 사용하고 있습니다.

  1. 이제로드 된 어셈블리의 모든 유형을 사용하려면 oAssembly 인스턴스 evertwhere를 전달해야합니다.
  2. 간단한 Type.GetType(...) 메서드를 사용하여 모든 곳 (동일한 AppDomain)에 연결할 수있는 어셈블리를로드하지 않습니까?

누락 된 부분을 채우고 내 질문에 답변 해주십시오.

사실 C#을 사용하면 VB.NET이 마음에 들지 않지만 Office에서 사용해야합니다.

+0

조금 더 많은 정보가 필요합니다. LoadFile과 LoadFrom을 모두 사용하여 어셈블리를로드하십시오. 결과로 나타나는 어셈블리 위치 및 FullName 속성을 비교하십시오. 또한 Type.GetType 호출에서 어셈블리의 전체 이름을 추가하십시오. "MyFolder"및 응용 프로그램 EXE 폴더에 해당 어셈블리 또는 해당 종속성을 여러 복사본이 있습니까? 여러 AppDomains를 사용하고 있습니까? 여러 스레드? 이 프로젝트는 IIS에서 호스팅됩니까? 아니면 다른 그림자 복사 시스템? LoadFrom 전에 GetType을 호출하면 성공합니까? 어셈블리의 위치와 Fullname은 무엇입니까? – Brannon

+0

우리가 테스트 할 수 있도록 문제를 되풀이하는보다 완벽한 테스트 앱을 게시 할 수 있습니까? 일관되게 실패했거나로드 된 경로/어셈블리에 따라 다르며 동일한 어셈블리의 여러 버전이 있습니까? – NSGaga

+0

'LoadFrom'을 사용하고'Type.GetType (string) '을 'LoadFrom'에 의해 반환 된 완전한 어셈블리 이름은'AssemblyResolve' 이벤트 핸들러를 설정하지 않으면 작동하지 않습니다. 그러나 이벤트 처리기에서 사용하는 경로 인 'C : \ PSIOBJ \ ...'와 LoadFrom 행의 경로 인 'C : \ MyFolder \ ...'는 일치하지 않습니다. 잘라 내기 및 붙여 넣기 오류입니까 아니면 실제로 다른 어셈블리를로드합니까? – Abel

답변

9

, 당신은 그 라인을 따라 뭔가를하려고 :

var asm = Assembly.LoadFrom(@"D:\Projects\_Libraries\FluentNH 1.1\Castle.Core.dll"); 
var obj = asm.CreateInstance("Castle.Core.GraphNode"); 
var type = Type.GetType(obj.GetType().AssemblyQualifiedName, true); // fails 

당신이 발생하는 문제가 당신이 무엇을 사용하는 어셈블리를로드의 형태로,시 도서관 가 실행 파일과 동일한 경로에 있지 않으면 변수 type은 항상 null이됩니다. 당신이 different loading contexts for assemblies in .NET의 문제가 발생할 무엇

원인

. 보통 세 가지 유형, 실제로 네 가지 유형의로드 컨텍스트가 있습니다.

  • 기본로드 컨텍스트. 이것은 GAC의 모든 어셈블리, 현재 실행중인 어셈블리, 현재 경로의 어셈블리 (BaseDirectory 참조) 및 PrivatePath의 어셈블리 (RelativeSearchPath 참조)에 사용됩니다. Assembly.Load(string,..)은이 컨텍스트를 사용합니다.
  • 로드 - 인 컨텍스트. 이것은 디스크상의 모든 어셈블리에 사용되는 컨텍스트입니다 Assembly.LoadFrom으로로드되며 조사 경로에는이 아닙니다.
  • 리플렉션 전용 컨텍스트. 이 컨텍스트의 유형은 실행할 수 없습니다.
  • 컨텍스트가없는 컨텍스트. Assembly.Load(byte\[\],..)Assembly.LoadFile 메서드 중 하나를 사용하여 어셈블리를로드하거나 디스크에 저장되지 않은 동적 어셈블리를로드하면이 컨텍스트가 사용됩니다.

하나의 컨텍스트에서로드되는 유형은 다른 컨텍스트와 호환되지 않습니다. 한 컨텍스트에서 다른 컨텍스트로 동일한 유형을 캐스팅 할 수 없습니다. 특정 컨텍스트에서 작동하는 메서드는 다른 컨텍스트에 액세스 할 수 없습니다. Type.GetType(string)은 약간의 방법을 돕지 않는 한 기본 컨텍스트에서만 유형을로드 할 수 있습니다.

이것은 정확히 사용자가 겪은 것입니다. 어셈블리 dll이 응용 프로그램의 경로에 있으면 모든 것이 잘 동작합니다. 당신이 그것을 옮기 자마자, 일이 무너지기 시작했습니다.

더 구체적으로 :
당신이 Type.GetType(string) 전화
, 그것은 현재 경로 (AppDomain.BaseDirectory)의 모든 정적 경로에 참조 어셈블리 및 동적으로로드 된 어셈블리를 조회 할 것이라고 GAC 및 AppDomain.RelativeSearchPath하고있다. 유감스럽게도 상대 경로 인 이 기본 디렉토리와 관련이 있어야합니다.

결과 :
이 동작의 결과는 GetType 단순히로드 된 모든 어셈블리를 확인하지 않는다는 것입니다. 대신, 주변에 다른 방식으로 작동, 그것은 수행합니다

  1. GetType을 찾을 경우 Assembly.Load
  2. 사용하여 어셈블리의 위치를 ​​첫 번째 매개 변수의 조립 부분을 사용하여, 해당 어셈블리에서 유형을 반영한다.
  3. 찾을 수없는 경우 다른 것을 시도하지 않고 null을 반환합니다 (또는 FileNotFoundException을 던짐). 다소 혼란 스러울 수 있습니다. 당신은 자신이를 테스트 할 수 있습니다

: 당신은 단지에 어셈블리 이름을 제공 할 때 Assembly.Load이 작동하지 않습니다.

솔루션

몇 가지 해결책이 있습니다. 하나는 이미 자신의 이름을지었습니다. 각각 자신의 단점과 함께, 좀 더있다 : 대신 정적 메서드 Type.GetType(string)의 인스턴스 객체 자체에

  1. 사용 GetType()는. 이것은 얻을 수있는 유형의 어셈블리 정규화 된 이름을 필요로하지 않는다는 이점이 있습니다 (예를 들어, sAssemblyName을 어떻게 설정했는지 말하지는 않지만 주위에 떠 있어야하는 것도 아닙니다. ?).

  2. 로드 된 어셈블리를 확인하고로드 된 어셈블리를 반환하는 일반 리졸버를 사용하십시오. 다시 LoadFrom 번으로 전화하지 않아도됩니다. 나는 다음과 같은 테스트하고 그 화려하고 매우 빠르게 작동합니다

    // works for any loaded assembly, regardless of the path 
    private static Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args) 
    { 
        // you may not want to use First() here, consider FirstOrDefault() as well 
        var asm = (from a in AppDomain.CurrentDomain.GetAssemblies() 
           where a.GetName().FullName == args.Name 
           select a).First(); 
        return asm; 
    } 
    
    // set it as follows somewhere in the beginning of your program: 
    AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve; 
    
  3. 가 함께 AppDomain.CurrentDomain.AssemblyLoad 및 .AssemblyResolve 이벤트를 사용합니다. 첫 번째는로드 된 각 어셈블리를 사전 캐시 (전체 이름)에 암기하는 데 사용되며, 두 번째는 이름에서 값을 가져 와서 해당 사전을 검사하는 데 사용합니다. 이것은 구현하기가 상대적으로 쉽지 않으며 이전 솔루션보다 약간 나은 성능을 보입니다.

  4. AppDomain.CurrentDomain.TypeResolve 이벤트 핸들러를 사용하십시오. 나는 이것을 시도하지 않았기 때문에 시나리오에서 잘 작동하는지 확신 할 수 없습니다. :이 방법은 작동하지 않습니다. GetType 처음으로 어셈블리를로드하려고하면 실패 할 경우 형식을 확인하지 않고이 이벤트가 결코 발생하지 않습니다.

  5. GAC 또는 응용 프로그램의 (상대적) 경로로 확인하려는 라이브러리를 추가하십시오. 이것은 훨씬 쉬운 해결책입니다.

  6. app.config에 경로를 추가하십시오. 이는 강력한 형식의 어셈블리에서만 작동하며,이 경우 GAC에서 어셈블리를 쉽게로드 할 수 있습니다. 강하게 유형화되지 않은 어셈블리는 현재 응용 프로그램에 대한 상대 경로에 있어야합니다.

결론

처음보기에로드 된 어셈블리에 올 때 Type.GetType(..) 오히려 unintuitively 동작 정적 방법 그룹. 여러 컨텍스트 뒤에있는 아이디어를 이해하면 어셈블리를 기본 컨텍스트에 배치하십시오. 그럴 수 없다면 AssemblyResolve 이벤트 핸들러를 만들 수 있습니다. 일반적으로 적용하기는 어렵지 않습니다.

+0

매우 광범위하고 잘 쓰여진 답변입니다. 정말 고맙습니다. – Tarik

0

args.name이 정확한 곳에서만 어셈블리를 반환하려고합니다 ...내가 제대로 질문을 이해한다면

Private Shared Function MyResolveEventHandler(ByVal sender As Object, _ 
ByVal args As ResolveEventArgs) As Assembly 
//Ex:  if (args.name.contains("XLATEBASE")) {Return Assembly.LoadFrom("C:\\PSIOBJ\\" + args.Name + ".dll")} 
End Function