2009-09-03 7 views
9

Eine kurze Erklärung, warum ich dies tun will:Kann ich die Standard-Anwendungsdomäne Schattenkopien bestimmter Assemblys verwenden?

Ich bin damit beschäftigt, ein Plugin für Autodesk Revit Architecture 2010 Testing meines Plugin Code zu schreiben ist extrem umständlich, da ich Autodesk für jede Debug-Sitzung neu starten, manuell laden ein Revit-Projekt, klicken Sie auf den Tab Add-Ins und starten Sie dann mein Plugin. Das dauert einfach zu lange.

Ich habe ein zweites Plugin geschrieben, das einen IronPython-Interpreter hostet. Auf diese Weise kann ich mit der von Revit bereitgestellten API experimentieren. Aber irgendwann muss der Code in C# neu geschrieben und debugged werden.

Einfach, dachte ich: Einfach die Plugins-DLL aus dem IronPython-Skript laden und üben. Das funktioniert, aber sobald es geladen ist, kann ich in Visual Studio nicht neu kompilieren, da die DLL jetzt in Revits AppDomain geladen ist.

Einfach, dachte ich (mit ein wenig Hilfe von StackOverflow): Laden Sie einfach die DLL in eine neue AppDomain. Leider können die RevitAPI-Objekte nicht zu einer anderen AppDomain gemarshallt werden, da sie nicht MarshalByRefObject erweitern.

Ich denke, ich könnte auf etwas mit Schattenkopien sein. ASP.NET scheint dies zu tun. Aber das Lesen der Dokumentation auf MSDN scheint es, ich kann dies nur angeben, wenn eine AppDomain erstellen.

Kann ich dies für die aktuelle (Standard) AppDomain ändern? Kann ich die Verwendung von Schattenkopien von DLLs aus einem bestimmten Verzeichnis erzwingen?

Antwort

5

Ich weiß nicht, was Sie zu tun versuchen, aber es gibt einige veraltete Methoden, um ShadowCopy auf der aktuellen AppDomain zu aktivieren.

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

Die Dokumente sagen, dass sie veraltet sind - ist das das gleiche wie veraltet? Ich werde es versuchen. Vielen Dank! –

+0

Ich kann es nicht zur Arbeit ... –

+1

Es funktioniert für mich. Ich habe meine Antwort mit einem Arbeitsbeispiel geändert. Kopieren Sie es und fügen Sie es in Ihre Main() -Methode ein.Stellen Sie außerdem sicher, dass Ihre Main() -Methode nicht direkt auf Ihre anderen Assemblies verweist, weil .NET sie vor 'SetShadowCopyFiles()' lädt, wird –

2

Manchmal ist es nicht möglich, die Methode Main() Code zu ändern, weil zum Beispiel, sind Sie einen Plug-in zu schreiben und es wird von einem Manager instanziiert.

In diesem Fall schlage ich vor, Sie kopieren Sie die Assembly und PDB (und abhängigen in AssemblyResolve -Ereignis) an einen temporären Speicherort und laden Sie sie von dort mit Assembly.LoadFile() (nicht LoadFrom()).

Pros: - keine DLL-Sperre. - Jedes Mal, wenn die Zielassembly neu kompiliert wird, erhalten Sie Zugriff auf die neue Version (deshalb .LoadFile()). - Die gesamte Assembly ist in AppDomain.CurrentDomain vollständig verfügbar.

Nachteile: - Kopieren von Dateien ist erforderlich. - Assemblys können nicht entladen werden und das könnte unpraktisch sein, da Ressourcen nicht freigegeben werden.

Grüße,

PD: Dieser Code macht die Arbeit.

/// <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

Dank aufgerufen. Ich verwende diese Technik hier: http://code.google.com/p/revitpythonshell/wiki/FeaturedScriptLoadplugin –

+0

Das Problem mit der Technik, auf die Sie verweisen, besteht darin, dass Sie eine Assembly rekompilieren, die von Ihrer Hauptbaugruppe referenziert wird, und Sie haben es bereits geladen, diese referenzierte Assembly wird nicht neu geladen (aktualisiert). Z.B. Sie laden eine Assembly mit dem Namen A, die von einer anderen Assembly namens B abhängt. –

+1

Beispiel: 1) Sie laden eine Assembly namens A mit einem Typ A1, die einen anderen Typ B1 auf einer anderen Assembly mit dem Namen B verwendet (assembly A references assembly) B). 2) ohne die App-Domäne herunterzufahren (ohne unsere Anwendung zu schließen) fügen Sie der Baugruppe B einen neuen Typ B2 hinzu und B2 vom Typ A1 aus Baugruppe A. 3) Sie laden erneut: hier kann der neue Typ B2 wird nicht gefunden, und Sie erhalten einen Fehler, weil .Net beim zweiten Mal Assembly B nicht erneut auflöst (es wurde nur einmal in Schritt 1 aufgelöst). Weiß nicht, ob ich klar genug war. Grüße, –

1

Es gibt jetzt eine Revit-Plugin zum dynamischen Laden/Entladen andere Revit Plugins, so dass Sie ändern können, rekompilieren und testen, ohne das Revit-Projekt wieder zu öffnen zu müssen. Ich habe es auf der Building Coder blog gefunden. Es kommt mit dem Revit SDK.

+0

danke, mir sind sowohl der Blog als auch der AddInManager bekannt. Das funktioniert wirklich! (außer, es scheint, für XAML WPF Formulare, aber das ist eine andere Sache, die ich nicht nach FTM gehen werde) –

Verwandte Themen