Tiempo de lectura: ~ 6 minutos
# Autodesk Revit
# RevitAPI
Actualizado 28 de febrero de 2022
Contenido
Una pregunta recurrente en los cursos del cursos Automatización de procesos con la API de Revit es como se puede crear un instalador en formato .msi para un aplicativo creado.
Los multiples tutoriales de inicio siempre nos mencionan que para que un aplicativo creado con la API de Revit sea reconocido por el programa Autodesk Revit es necesario generar una crear un archivo manifiesto (.addin) y un biblioteca de clases (.dll) y estos archivos deben ser copiados en una ruta especifica (C:\Users%User%\AppData\Roaming\Autodesk\Revit\Addins%Version%)que leerá dicho archivo manifiesto.
La solución mas sencilla es crear eventos de compilación que durante el build copie los archivos .dll y .addin en la carpeta mencionada. La otra solución es crear un archivo comprimido auto extraíble con winrar que simplemente extrae los archivos en una ruta especifica. Ver tutorial
copy "$(ProjectDir)*.addin" "$(AppData)\Autodesk\REVIT\Addins\20XX" copy "$(ProjectDir)bin\debug\*.dll" "$(AppData)\Autodesk\REVIT\Addins\20XX"
Dado lo mencionado lo anterior, no convence estas dos soluciones que he venido aplicando es por lo cual investigando me encontré con el tutorial de Joshua Lumley, Revit API C# make Installer MSI File (Executable) ... and avoid my Blunders! (secrets part 2)A, y lo he replicado para una aplicación mas sencilla y claro para la mayoría de mis lectores que manejas ESPANOL
Lo primero que tenemos que entender es que necesitamos una estructura base minima de tres proyectos para crear un instalador una librería de clases que contendrá el external command o el external aplicación que es nuestro addin de Revit, un proyecto de tipo Setup project que lo obtendremos instalando Microsoft visual studio installer project o la version que función para Visual studio 2022 y por ultimo una librería de clases para el archivo que obtendrá los métodos install y uninstall que ejecutara el proyecto setup.
Primero crearemos un proyecto de tipo library class (.Net framework) que llamaremos RevitSimpleCommand en el cual solo crearemos un simple external command que muestre un mensaje a través de un task dialog.
command.cs1using Autodesk.Revit.DB; 2using Autodesk.Revit.UI; 3using Autodesk.Revit.Attributes; 4 5namespace RevitSimpleCommand 6{ 7 [Transaction(TransactionMode.ReadOnly)] 8 public class CmdHelloWorld : IExternalCommand 9 { 10 public Result Execute(ExternalCommandData commandData, 11 ref string message, 12 ElementSet elements) 13 { 14 TaskDialog.Show("Simple title", "Hello World"); 15 return Result.Succeeded; 16 } 17 } 18} 19
En nuestro proyecto creamos un nuevo elemento de tipo installer class que llamaremos InstallerCommands donde solo tendremos dos métodos que servirán para decirle al setup project que secuencia de acciones realizar cuando ejecute el install y un install. Además de los comandos es necesarios instalar dos paquetes de Nuget Extended.Wpf.Toolkit y Ookii.Dialogs.Wpf.
command.cs1AddinType addinType = AddinType.Command; 2string commandProject = "RevitSimpleCommand"; 3string commandName = "CmdHelloWorld"; 4string companyName = "Lambda Ingenieria e Innovacion"; 5string companyURL = "https://lambda.com.pe/"; 6 7enum AddinType 8 { 9 Command = 0, 10 Application = 1 11 } 12
command.cs1public override void Install(IDictionary stateSaver) 2{ 3 Microsoft.Win32.RegistryKey rkbase = null; 4 5 rkbase = Microsoft.Win32.RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, 6 Microsoft.Win32.RegistryView.Registry64); 7 8 rkbase.CreateSubKey($"SOFTWARE\\Wow6432Node\\{companyName}\\Revit API NuGet Example 2019 Packages", Microsoft.Win32.RegistryKeyPermissionCheck.ReadWriteSubTree).SetValue("OokiiVersion", typeof(Ookii.Dialogs.Wpf.CredentialDialog).Assembly.FullName); 9 10 rkbase.CreateSubKey($"SOFTWARE\\Wow6432Node\\{companyName}\\Revit API NuGet Example 2019 Packages", 11 Microsoft.Win32.RegistryKeyPermissionCheck.ReadWriteSubTree).SetValue("XceedVersion", 12 typeof(Xceed.Wpf.Toolkit.PropertyGrid.PropertyGrid).Assembly.FullName); 13 14 string sDir = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\Autodesk\\Revit\\Addins"; 15 bool exists = Directory.Exists(sDir); 16 17 if (!exists) Directory.CreateDirectory(sDir); 18 19 XElement XElementAddIn; 20 if (addinType == AddinType.Command) 21 { 22 XElementAddIn = new XElement("AddIn", new XAttribute("Type", "Command")); 23 XElementAddIn.Add(new XElement("Text", commandProject)); 24 } 25 else { 26 XElementAddIn = new XElement("AddIn", new XAttribute("Type", "Application")); 27 XElementAddIn.Add(new XElement("Name", commandProject)); 28 } 29 30 XElementAddIn.Add(new XElement("Assembly", this.Context.Parameters["targetdir"].Trim() + commandProject + ".dll")); 31 XElementAddIn.Add(new XElement("AddInId", Guid.NewGuid().ToString())); 32 XElementAddIn.Add(new XElement("FullClassName", $"{commandProject}.{commandName}")); 33 XElementAddIn.Add(new XElement("VendorId", "ADSK")); 34 XElementAddIn.Add(new XElement("VendorDescription", $"{companyName}, {companyURL}")); 35 36 XElement XElementRevitAddIns = new XElement("RevitAddIns"); 37 XElementRevitAddIns.Add(XElementAddIn); 38 39 try 40 { 41 foreach (string d in Directory.GetDirectories(sDir)) 42 { 43 new XDocument(XElementRevitAddIns).Save(d + "\\" + commandProject + ".addin"); 44 } 45 } 46 catch (Exception excpt) 47 { 48 MessageBox.Show(excpt.Message); 49 } 50} 51
command.cs1public override void Uninstall(IDictionary stateSaver) 2{ 3 string sDir = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\Autodesk\\Revit\\Addins"; 4 5 bool exists = Directory.Exists(sDir); 6 7 Microsoft.Win32.RegistryKey rkbase = null; 8 rkbase = Microsoft.Win32.RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine,Microsoft.Win32.RegistryView.Registry64); 9 rkbase.DeleteSubKeyTree($"SOFTWARE\\Wow6432Node\\${companyName}\\Revit API NuGet Example 2019 Packages"); 10 11 if (exists) 12 { 13 try 14 { 15 foreach (string d in Directory.GetDirectories(sDir)) 16 { 17 File.Delete(d + "\\" + commandProject + ".addin"); 18 } 19 } 20 catch (Exception excpt) 21 { 22 MessageBox.Show(excpt.Message); 23 } 24 } 25} 26
Lo primero que haremos luego de crear nuestro proyecto que llamaremos RevitAddinInstaller es configurar las propiedades InstallAllUser = true, RemovePreviewVersion = true y TargetPlaform = x64 dentro de la barra de propiedades del proyecto.
Cambiamos los prerrequisitos del proyecto accediendo a las propiedades con una anti clic sobre nuestro proyecto y seleccionados que tipo de .Net framework trabajamos. coloco una imagen para saber que .net framework son compatible según la version de Revit.
Viene la parte mas complicada de nuestro setup project que es configurar para que este proyecto tenga un correcto funcionamiento.
Para el panel de File System Config es necesario agregar los resultados del build de los proyectos InstallerCommands y RevitSimpleCommand dentro de la carpeta Aplication Folder. Para agregar esta información es necesario entrar el menu desplegable de Aplication folder y seleccionar Resultado de Proyecto. Seleccionamos cada uno de los proyecto y agregamos el Resultado principal.
Para el panel de Register Config es necesario agregar dos variables. ProductVersion y TARGETDIR dentro de la carpeta [ProductName].
1|— 📂HKEY_LOCAL_MACHINE 2 3 |— 📂 Software 4 5 |— 📂 [Manufacturer] 6 7 |— 📂 [ProductName]
Name | Value |
---|---|
ProductVersion | [ProductVersion] |
TARGETDIR | [TARGETDIR] |
Dentro de las Custom Actions, configuramos las acciones de Install y UnInstall agregando el proyecto InstallerCommands.
Y eso es todo! Es un proceso largo pero creo que con una buena plantilla de proyecto se puede simplificar mucho el proceso. Para los que se perdieron siguiendo los pasos he subido un video a YouTube donde hago un instalador para un complemento de la API de Revit y ya que están por ahí suscríbanse al canal y compártanlo (Me siento el Rubius) para que este tipo de contenido llegue a mas personas.
Recibe semanalmente tutoriales, recursos, noticias sobre temas innovadores dentro del sector construcción y destaca profesionalmente.