2009-09-03 2 views
9

왜 내가 이것을하고 싶은지에 대한 간단한 설명 :기본 AppDomain에서 특정 어셈블리의 섀도 복사본을 사용할 수 있습니까?

저는 Autodesk Revit Architecture 2010 용 플러그인을 작성하는 중입니다. 각 디버그 세션마다 오토 데스크를 다시 시작해야하기 때문에 플러그인 코드를 테스트하는 것이 매우 어려워서 수동으로로드해야합니다 Revit 프로젝트에서 Add-Ins 탭을 클릭 한 다음 플러그인을 시작하십시오. 이것은 너무 오래 걸리고 있습니다.

IronPython 인터프리터를 호스팅하는 두 번째 플러그인을 작성했습니다. 이렇게하면 Revit에서 제공하는 API를 활용할 수 있습니다. 하지만 결국 C#에서 코드를 다시 작성하고 디버깅해야합니다.

쉽게, 나는 생각했다 : IronPython 스크립트에서 플러그인 DLL을로드하고 그것을 실행하자. DLL은 이제 Revit AppDomain에로드되기 때문에 작동하지만 한 번로드되면 Visual Studio에서 다시 컴파일 할 수 없습니다.

쉽고, (StackOverflow에서 약간의 도움을 얻어서) 간단하게 DLL을 새 AppDomain에로드하십시오. 아아, RevitAPI 개체는 MarshalByRefObject을 확장하지 않으므로 다른 AppDomain으로 마샬링 할 수 없습니다.

쉐도우 복사본이있는 것 같습니다. ASP.NET이이를 수행하는 것 같습니다. 그러나 MSDN의 설명서를 읽으면 을 AppDomain으로으로 만들 때만 지정할 수 있습니다.

현재 (기본) AppDomain에 대해 이것을 변경할 수 있습니까? 특정 디렉터리의 DLL 섀도 복사본을 강제로 사용할 수 있습니까?

답변

5

현재 수행하려는 작업을 알 수 없지만 현재 AppDomain에서 ShadowCopy를 사용하지 않으려는 일부 권장되지 않는 방법이 있습니다.

AppDomain.CurrentDomain.SetCachePath(@"C:\Cache"); 
AppDomain.CurrentDomain.SetShadowCopyPath(AppDomain.CurrentDomain.BaseDirectory); 
AppDomain.CurrentDomain.SetShadowCopyFiles(); 
+0

문서가 더 이상 사용되지 않는다고 말합니다. 사용 중지 된 것과 동일합니까? 나는 그것을 시도 할 것이다. 감사! –

+0

나는 그것을 작동시킬 수 없다 ... –

+1

그것은 나를 위해 일한다. 나는 실례로 나의 대답을 수정했다. 그것을 복사하여 Main() 메소드에 붙여 넣으십시오.또한'SetShadowCopyFiles()'가 호출되기 전에 .NET이로드하기 때문에 Main() 메소드가 다른 어셈블리를 직접 참조하지 않도록하십시오. –

2

때로는 예를 들어, 당신이 플러그인을 작성하고, 때문에 main() 메소드의 코드를 수정할 수는 없습니다 그것은 관리자에 의해 인스턴스화합니다.

그런 경우 어셈블리 및 pdb (및 AssemblyResolve 이벤트의 종속적 인 것들)를 임시 위치에 복사하고 거기에서 Assembly.LoadFile() (LoadFrom()이 아닌)을로드하는 것이 좋습니다.

장점 : - DLL 잠금 없음. - 대상 어셈블리를 다시 컴파일 할 때마다 새 버전에 액세스 할 수 있습니다 (그 이유는 .LoadFile()). - 전체 어셈블리는 AppDomain.CurrentDomain에서 완전히 사용할 수 있습니다.

단점 : - 파일 복사가 필요합니다. - 어셈블리를 언로드 할 수 없으며 리소스가 해제되지 않으므로 어셈블리가 불편할 수 있습니다.

감사합니다.

PD :이 코드는 작업을 수행합니다.

/// <summary> 
/// Loads an assembly without locking the file 
/// Note: the assemblys are loaded in current domain, so they are not unloaded by this class 
/// </summary> 
public class AssemblyLoader : IDisposable 
{ 
    private string _assemblyLocation; 
    private string _workingDirectory; 
    private bool _resolveEventAssigned = false; 

    /// <summary> 
    /// Creates a copy in a new temp directory and loads the copied assembly and pdb (if existent) and the same for referenced ones. 
    /// Does not lock the given assembly nor pdb and always returns new assembly if recopiled. 
    /// Note: uses Assembly.LoadFile() 
    /// </summary> 
    /// <param name="assemblyOriginalPath"></param> 
    /// <returns></returns> 
    public Assembly LoadFileCopy(string assemblyLocation) 
    { 
     lock (this) 
     { 
      _assemblyLocation = assemblyLocation; 

      if (!_resolveEventAssigned) 
      { 
       _resolveEventAssigned = true; 

       AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(AssemblyFileCopyResolveEvent); 
      } 

      // Create new temp directory 
      _workingDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); 
      Directory.CreateDirectory(_workingDirectory); 

      // Generate copy 
      string assemblyCopyPath = Path.Combine(_workingDirectory, Path.GetFileName(_assemblyLocation)); 
      System.IO.File.Copy(_assemblyLocation, assemblyCopyPath, true); 

      // Generate copy of referenced assembly debug info (if existent) 
      string assemblyPdbPath = _assemblyLocation.Replace(".dll", ".pdb"); 
      if (File.Exists(assemblyPdbPath)) 
      { 
       string assemblyPdbCopyPath = Path.Combine(_workingDirectory, Path.GetFileName(assemblyPdbPath)); 
       System.IO.File.Copy(assemblyPdbPath, assemblyPdbCopyPath, true); 
      } 

      // Use LoadFile and not LoadFrom. LoadFile allows to load multiple copies of the same assembly 
      return Assembly.LoadFile(assemblyCopyPath); 
     } 
    } 

    /// <summary> 
    /// Creates a new copy of the assembly to resolve and loads it 
    /// </summary> 
    /// <param name="sender"></param> 
    /// <param name="args"></param> 
    /// <returns></returns> 
    private Assembly AssemblyFileCopyResolveEvent(object sender, ResolveEventArgs args) 
    { 
     string referencedAssemblyFileNameWithoutExtension = System.IO.Path.GetFileName(args.Name.Split(',')[0]); 

     // Generate copy of referenced assembly 
     string referencedAssemblyPath = Path.Combine(Path.GetDirectoryName(_assemblyLocation), referencedAssemblyFileNameWithoutExtension + ".dll"); 
     string referencedAssemblyCopyPath = Path.Combine(Path.GetDirectoryName(args.RequestingAssembly.Location), referencedAssemblyFileNameWithoutExtension + ".dll"); 
     System.IO.File.Copy(referencedAssemblyPath, referencedAssemblyCopyPath, true); 

     // Generate copy of referenced assembly debug info (if existent) 
     string referencedAssemblyPdbPath = Path.Combine(Path.GetDirectoryName(_assemblyLocation), referencedAssemblyFileNameWithoutExtension + ".pdb"); 
     if (File.Exists(referencedAssemblyPdbPath)) 
     { 
      string referencedAssemblyPdbCopyPath = Path.Combine(Path.GetDirectoryName(args.RequestingAssembly.Location), referencedAssemblyFileNameWithoutExtension + ".pdb"); 
      System.IO.File.Copy(referencedAssemblyPath, referencedAssemblyCopyPath, true); 
     } 

     // Use LoadFile and not LoadFrom. LoadFile allows to load multiple copies of the same assembly 
     return Assembly.LoadFile(referencedAssemblyCopyPath); 
    } 


    public void Dispose() 
    { 
     Dispose(true); 
    } 

    private void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      if (_resolveEventAssigned) 
      { 
       AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(AssemblyFileCopyResolveEvent); 

       _resolveEventAssigned = false; 
      } 
     } 
    } 
} 
+0

감사합니다. 저는이 기술을 꽤 많이 사용하고 있습니다 : http://code.google.com/p/revitpythonshell/wiki/FeaturedScriptLoadplugin –

+0

당신이 참조하는 기술의 문제점은 주 어셈블리에서 참조 된 어셈블리를 다시 컴파일하면 이미로드 했으므로이 참조 된 어셈블리는 다시로드되지 않습니다 (업데이트 된). 예 : –

+1

예 : 1) B라는 다른 어셈블리에있는 다른 형식의 B1을 사용하는 A1 형식의 어셈블리를로드합니다 (어셈블리 A 참조 어셈블리 비). 2) 응용 프로그램 도메인을 종료하지 않고 어셈블리 B에서 새 유형 B2를 추가하고 어셈블리 A에서 A1 유형의 B2를 참조하십시오. 3) 다시로드하십시오. 여기에서 새 유형 B2는 다음을 수행 할 수 있습니다. 두 번째로 .NET이 어셈블리 B를 다시 해석하지 못하기 때문에 오류가 발생합니다 (1 단계에서 한 번 해결되었습니다). 내가 충분히 명확한 지 모르겠다. 감사합니다, –

1

는 동적으로 로딩/당신은 Revit 프로젝트를 다시 열 필요없이 변경 컴파일 및 테스트 할 수 있도록 다른 REVIT 플러그인을 언로드위한 REVIT 플러그인이있다. Building Coder blog에서 찾았습니다. Revit SDK와 함께 제공됩니다.

+0

덕분에, 나는 블로그와 AddInManager를 알고 있습니다. 이것은 실제로 작동합니다! (XAML WPF 양식의 경우는 예외 인 것 같습니다. 그러나 그것은 FTM을 수행하는 데 신경 쓰지 않을 다른 문제입니다) –