Tiempo de lectura: ~ 6 minutos

959 vistas

Como generar un instalador (.msi) para un plugin de Revit

# Revit API

# Automatización

Actualizado 21 de diciembre de 2024

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

Crear un archivo .msi para un aplicativo con la API de Revit

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.

directory structure.PNG

1. Proyecto de Revit addin

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.cs
1using 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}

2. Comandos para instalar

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.

Variables Globales

command.cs
1AddinType 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    }</Code>
12
13### Metodo Install
14
15<Code language="csharp">
16public override void Install(IDictionary stateSaver)
17{
18     Microsoft.Win32.RegistryKey rkbase = null;
19
20     rkbase = Microsoft.Win32.RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine,
21                                                             Microsoft.Win32.RegistryView.Registry64);
22
23     rkbase.CreateSubKey($"SOFTWARE\\Wow6432Node\\{companyName}\\Revit API NuGet Example 2019 Packages", Microsoft.Win32.RegistryKeyPermissionCheck.ReadWriteSubTree).SetValue("OokiiVersion", typeof(Ookii.Dialogs.Wpf.CredentialDialog).Assembly.FullName);
24
25     rkbase.CreateSubKey($"SOFTWARE\\Wow6432Node\\{companyName}\\Revit API NuGet Example 2019 Packages",
26                                Microsoft.Win32.RegistryKeyPermissionCheck.ReadWriteSubTree).SetValue("XceedVersion",
27                                typeof(Xceed.Wpf.Toolkit.PropertyGrid.PropertyGrid).Assembly.FullName);
28
29     string sDir = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\Autodesk\\Revit\\Addins";
30     bool exists = Directory.Exists(sDir);
31
32     if (!exists) Directory.CreateDirectory(sDir);
33
34     XElement XElementAddIn;
35     if (addinType == AddinType.Command)
36     {
37        XElementAddIn = new XElement("AddIn", new XAttribute("Type", "Command"));
38        XElementAddIn.Add(new XElement("Text", commandProject));
39     }
40     else {
41        XElementAddIn = new XElement("AddIn", new XAttribute("Type", "Application"));
42        XElementAddIn.Add(new XElement("Name", commandProject));
43     }
44
45            XElementAddIn.Add(new XElement("Assembly", this.Context.Parameters["targetdir"].Trim()  + commandProject + ".dll"));
46            XElementAddIn.Add(new XElement("AddInId", Guid.NewGuid().ToString()));
47            XElementAddIn.Add(new XElement("FullClassName",  $"{commandProject}.{commandName}"));
48            XElementAddIn.Add(new XElement("VendorId", "ADSK"));
49            XElementAddIn.Add(new XElement("VendorDescription", $"{companyName}, {companyURL}"));
50
51            XElement XElementRevitAddIns = new XElement("RevitAddIns");
52            XElementRevitAddIns.Add(XElementAddIn);
53
54     try
55     {
56         foreach (string d in Directory.GetDirectories(sDir))
57         {
58            new XDocument(XElementRevitAddIns).Save(d + "\\" + commandProject + ".addin");
59         }
60     }
61     catch (Exception excpt)
62     {
63         MessageBox.Show(excpt.Message);
64     }
65}

Metodo Uninstall

command.cs
1public 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}

3. Proyecto Instalador

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.

Propiedades.png

Viene la parte mas complicada de nuestro setup project que es configurar para que este proyecto tenga un correcto funcionamiento.

prerrequisitos.PNG

A. Configurar el File System

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.

Configurar el file System.PNG

B. Configurar Register

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]
NameValue
ProductVersion[ProductVersion]
TARGETDIR[TARGETDIR]

Configuración de Registro.PNG

C. Configurar Custom Actions Editor

Dentro de las Custom Actions, configuramos las acciones de Install y UnInstall agregando el proyecto InstallerCommands.

Importante
Al momento de configurar la propiedad de TARGETDIR en el panel de propiedades de cada acciones. Asignar el valor /TargetDir="[TARGETDIR] ". Note que se esta dejando un espacio antes de cerrar las comillas. NO OLVIDAR!

Configuración de Acciones personalizadas.PNG

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.

Comparte este artículo

Suscribirse para recibir actualizaciones

Recibe semanalmente tutoriales, recursos, noticias sobre temas innovadores dentro del sector construcción y destaca profesionalmente.