2009-10-08 5 views
3

일부 특성 메타 데이터를 얻기 위해 어셈블리 내부에서 엿보기 사용자 지정 MSBuild 작업이 있습니다.Assembly.ReflectionOnlyLoadFrom을 사용하여 WPF 프로젝트에서 참조하는 어셈블리를로드 할 때 이상한 FileLoadException이 발생했습니다.

Assembly assembly = Assembly.ReflectionOnlyLoadFrom(AssemblyFile) 

이것은 자동화 된 빌드/릴리스 프로세스에서 사용되며 클래스 라이브러리, 콘솔 응용 프로그램 및 웹 프로젝트에서 사용하거나 참조하는 어셈블리와 완벽하게 작동합니다. MSBuild 작업은 다른 MSBuild 프로세스가 프로젝트를 컴파일 한 후에 호출됩니다.

어제이 특정 어셈블리 (.NET 3.5 클래스 라이브러리)를 참조한 WPF 프로젝트를 추가하면 작업이 중단되었습니다.

System.IO.FileLoadException: API restriction: The assembly 'file:///bogus.dll' has already loaded from a different location. 
It cannot be loaded from a new location within the same appdomain. 
at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection) 
at System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection) 
at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) 
at System.Reflection.Assembly.InternalLoadFrom(String assemblyFile, Evidence securityEvidence, Byte[] hashValue, AssemblyHashAlgorithm hashAlgorithm, Boolean forIntrospection, StackCrawlMark& stackMark) 
at System.Reflection.Assembly.ReflectionOnlyLoadFrom(String assemblyFile) 
at RadicaLogic.MSBuild.Tasks.GetAssemblyAttribute.Execute() 
at Microsoft.Build.BuildEngine.TaskEngine.ExecuteInstantiatedTask(EngineProxy engineProxy, ItemBucket bucket, TaskExecutionMode howToExecuteTask, ITask task, Boolean& taskResult) 

나는 그것이 WPF 예외 내가 AssemblyFile는 WPF 프로젝트에서 참조되지 동일한 솔루션에서 다른 어셈블리를 가리 키도록 변경하는 경우 발생하지 않기 때문에 관련이 알고있다.

예외 메시지는 동일한 응용 프로그램 도메인에 대한 부분이

... already loaded from a different location.

It cannot be loaded from a new location within the same appdomain.

주를 언급하고있다. 또 다른 있기 때문에, 문제의 어셈블리가 의미가 있습니다 현재 도메인 (에 아니라고 말할 필요도

Assembly assembly = null; 
try 
{ 
    assembly = Assembly.ReflectionOnlyLoadFrom(AssemblyFile); 
} 
catch (FileLoadException) 
{ 
    List<string> searched = new List<string>(); 
    foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) 
    { 
     if (Path.GetFileName(asm.CodeBase).Equals(Path.GetFileName(AssemblyFile), 
      StringComparison.OrdinalIgnoreCase)) 
     { 
      message = string.Format("Found assembly {0} in current domain", 
       asm.CodeBase); 
      MSBuildHelper.Log(this, message, MessageImportance.High); 
      assembly = asm; 
      break; 
     } 
     else 
     { 
      searched.Add(Path.GetFileName(asm.CodeBase)); 
     } 
    } 
    if (assembly == null) 
    { 
     message = string.Format(
      "Unable to find {0} after looking in current domain assemblies {1}", 
      Path.GetFileName(AssemblyFile), string.Join(", ", searched.ToArray())); 
     MSBuildHelper.Log(this, message, MessageImportance.High);      
    } 
} 

그것은 간다 :

그래서 나는이 예외를 잡아 CurrentDomain에서 보는 코드를 수정 컴파일을 수행하는 MSBuild 프로세스가 생성됩니다.) 오류 메시지가 참이라고 가정 할 때, 어떻게 살고 있는지 알아 내려면 어떻게해야합니까? 그것은 나에게 오류 메시지가 그것이 CurrentDomain이어야한다고 제안하기 때문에 혼란 스럽다.

WPF 경험이 많은 사람이 성공적인 빌드 후에이 어셈블리가 여전히 앱 도메인에서 떠 다니는 이유를 설명 할 수 있습니까?

여기에 another question의 예외가 발생했습니다. 제이스 코멘트

bool found = false; 
string value = string.Empty; 
Type attributeType = Type.GetType(Attribute); 
AssemblyDefinition assembly = AssemblyFactory.GetAssembly(AssemblyFile); 
foreach (CustomAttribute attribute in assembly.CustomAttributes) 
{ 
    if (attribute.Constructor.DeclaringType.Name == attributeType.Name) 
    { 
     value = attribute.ConstructorParameters[0].ToString(); 
     found = true; 
    } 
} 

업데이트 : 일반적으로

내가 사용

+0

을, 당신은 아마 별도의 응용 프로그램 도메인이 조회를해야한다. 이렇게하면 이러한 종류의 문제를 쉽게 피할 수 있습니다. –

+0

@Jamie - 내가 충돌 한 유일한 시간은 WPF 참조 된 프로젝트이며 다른 (별도의) MSBuild 프로세스가 컴파일을 마친 후에 트리거됩니다. 새로운 AppDomain도 도움이 될 것입니까?또한 Assembly *가 CurrentDomain에서 발견되지 않았다고 간주합니다. – si618

+0

@Si 나는 방금 일반적으로 의미했습니다. 동적으로 어셈블리를로드하는 것은 현재 AppDomain으로로드되는 내용을 제어 할 수없는 경우 파열되는 경향이 있습니다. 향후 개발자는 호환되지 않는 버전의 공유 어셈블리를로드하거나 예상되는 어셈블리를 제거 할 수 있습니다. 이 중 아무 것도 컴파일 타임에 검사되지 않으므로로드 할 어셈블리를 명시 적으로 설정할 수있는 새로운 AppDomain이 정말로 필요합니다. 어셈블리 검사를 완료하면 AppDomain을 종료하고 메모리를 확보 할 수 있습니다. 그러나 장시간 실행되는 프로세스에만 적용됩니다. –

답변

1

내 솔루션 AttributeAssemblyFile에서 얻을 수 Cecil를 사용하여 오픈 소스 : 이동했다 속성의 속성 값으로 AssemblyFileVersion을하고있다 setter 로직이 누락 된 조각을 채 웁니다.

속성 정의 방법은 다음과 같습니다.

string attribute; 
[Required] 
public string Attribute 
{ 
    get { return attribute; } 
    set 
    { 
     string tempValue = value; 
     if (!tempValue.StartsWith("System.Reflection.")) 
     { 
      tempValue = "System.Reflection." + tempValue; 
     } 
     if (!value.EndsWith("Attribute")) 
     { 
      tempValue += "Attribute"; 
     } 
     attribute = tempValue; 
    } 
} 

단위 테스트 보여주는 특성 속성 값 산세가 접두사 나 접미사 필요한 : 옆으로 그냥으로

[Test] 
public void Execute_WithoutSystemReflectionPrefixOrAttributeSuffix_ReturnsExpectedResult() 
{ 
    string version = getAssemblyFileVersion(); 
    Assert.IsNotNull(version, "Expected AssemblyFileVersionAttribute to contain value"); 

    task.AssemblyFile = assemblyFile; 
    task.Attribute = "AssemblyFileVersion"; 
    task.Value = "Bogus"; 

    result = task.Execute(); 

    Assert.IsTrue(result, "Expected execute to pass on valid assembly and attribute name"); 

    Assert.AreEqual(task.Value, version, 
     "Expected task value to match assembly file version attribute value"); 
} 
+0

@Si : 나는 이것이 낡은 대답 인 것을 알고 있지만, 당신은 라인의 "Attribute"문자열에 대해 무엇을 전달 했는가? Type attributeType = Type.GetType (Attribute); 나중에 코드에서 비교할 대상을 결정하려고합니다. – Jay

+0

헤이 제이, 추가 정보를 추가했습니다. – si618