2009-09-03 10 views
9

Una breve explicación de por qué quiero hacer esto:¿Puedo hacer que el AppDomain predeterminado use instantáneas de ciertos ensamblajes?

estoy ocupado escribiendo un plugin para Autodesk Revit Architecture 2010. Prueba de mi código plugin es extremadamente engorroso, ya que tengo que reiniciar Autodesk para cada sesión de depuración, cargue manualmente un proyecto de Revit, haga clic en la pestaña Complementos y luego inicie mi complemento. Esto solo lleva demasiado tiempo.

He escrito un segundo complemento que aloja un intérprete de IronPython. De esta forma, puedo jugar con la API proporcionada por Revit. Pero eventualmente, el código debe ser reescrito en C# - y depurado.

Fácil, pensé: Simplemente cargue los complementos de la secuencia de comandos de IronPython y ejercítelos. Esto funciona, pero una vez cargado, no puedo recompilar en Visual Studio, ya que la DLL ahora está cargada en Revits AppDomain.

Fácil, pensé (con un poco de ayuda de StackOverflow): Simplemente cargue la DLL en un nuevo dominio de aplicación. Desgraciadamente, los objetos de RevitAPI no se pueden ordenar en otro AppDomain, ya que no extienden MarshalByRefObject.

Creo que podría estar en algo con las instantáneas. ASP.NET parece estar haciendo esto. Pero al leer la documentación en MSDN, parece que solo puedo especificar esto cuando cree un AppDomain.

¿Puedo cambiar esto por el dominio de aplicación actual (predeterminado)? ¿Puedo forzarlo a utilizar instantáneas de archivos DLL de un directorio específico?

Respuesta

5

No sé lo que intenta hacer, pero hay algunos métodos en desuso para activar ShadowCopy en el dominio de aplicación actual.

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

los documentos dicen que están obsoletos. ¿Es esto lo mismo que obsoleto? Lo probaré. ¡Gracias! –

+0

No puedo hacer que funcione ... –

+1

Funciona para mí. He modificado mi respuesta con un ejemplo de trabajo. Copie y pegue en su método Main().También asegúrese de que su método Main() no haga referencia directamente a sus otros ensambles porque .NET los cargará antes de que 'SetShadowCopyFiles()' reciba el nombre –

2

A veces no es posible modificar el código método Main(), ya que, por ejemplo, que está escribiendo un plug-in y se está instanciado por un administrador.

En ese caso, sugiero que copie el ensamblado y pdb (y los dependientes en el evento AssemblyResolve) en una ubicación temporal y los cargue desde allí con Assembly.LoadFile() (no LoadFrom()).

Pros: - sin bloqueo dll. - cada vez que se vuelve a compilar el conjunto de destino obtiene acceso a la nueva versión (es por eso que .LoadFile()). - todo el conjunto está totalmente disponible en AppDomain.CurrentDomain.

Contras: - copia de archivos es necesario. - los ensamblajes no se pueden descargar y eso podría ser un inconveniente porque los recursos no se liberan.

Saludos,

PD: Este código hace el trabajo.

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

Gracias. Estoy prácticamente usando esta técnica aquí: http://code.google.com/p/revitpythonshell/wiki/FeaturedScriptLoadplugin –

+0

El problema con la técnica a la que se refiere es que si recompila un ensamblaje al que se hace referencia desde su ensamblaje principal, y ya lo ha cargado, este ensamblaje al que se hace referencia no se recarga (se actualiza). P.ej. carga un ensamble llamado A que depende de otro ensamble llamado B. –

+1

Eg: 1) carga un ensamble llamado A que contiene un Tipo A1 que usa otro tipo B1 que está ubicado en un ensamble diferente llamado B (conjunto de referencias de ensamblaje A) SEGUNDO). 2) sin cerrar el dominio de la aplicación (sin cerrar su aplicación), agrega un nuevo tipo B2 al ensamblaje B y referencia B2 del tipo A1 del ensamble A. 3) carga de nuevo: aquí el nuevo tipo B2 puede no se encuentra y se obtiene un error porque, la segunda vez, .Net no resuelve el ensamblaje B (se resolvió una vez en el paso 1). No sé si fui lo suficientemente claro. Saludos, –

1

Ahora hay un plug-in de Revit para cargar dinámicamente/descarga de otros plugins de Revit para que pueda cambiar, recompilar, y la prueba sin tener que volver a abrir el proyecto de Revit. Lo encontré en el Building Coder blog. Viene con el SDK de Revit.

+0

gracias, soy consciente tanto del blog como del AddInManager. ¡Esto realmente funciona! (excepto, parece, para formularios XAML WPF, pero ese es otro asunto que no voy a molestar ir después de FTM) –

Cuestiones relacionadas