C# Crear instalador NET 6 – Tutorial Inno Setup

En este artículo vamos a ver cómo hacer un instalador para una aplicación .NET 6 aunque el mismo tutorial se puede aplicar para cualquier otra aplicación independientemente del lenguaje de programación.

Este tutorial se basa en la aplicación que generamos en el artículo «Cómo crear un servicio con .NET 6» os recomiendo que antes leáis este tutorial, en especial la parte de agregar el paquete Nuget al servicio. Si estáis haciendo un servicio sin agregar el paquete no os arrancará.

Requisitos

Para seguir el tutorial debemos de descargar e instalar una serie de aplicaciones:

  • Inno Setup: Nos descargaremos la versión estable y la instalaremos, en caso de que ya lo tengas instalado, asegúrate de tener la última versión, si no es posible que el tutorial no te funcione.
  • Inno Setup Dependency Installer: Pulsamos en Download source from Github y descomprimimos los ficheros, más adelante los utilizaremos.
  • Un proyecto .NET 6: Te recomiendo hacer algo parecido a este.
  • Una máquina virtual (Recomendado): No es necesario, pero sería recomendable que las pruebas de instalación las hicieses en una máquina virtual para evitar dañar tu ordenador.

Forzar el proyecto a 64 bits

Hoy en día prácticamente todos los ordenadores tienen procesadores de 64 bits, por lo que salvo que nuestro destino sea de 32 bits, es recomendable forzar a nuestra aplicación para que se ejecute con una arquitectura x64, con este simple gesto conseguimos un mejor rendimiento.

Para forzar los 64 bits debemos pulsar en Any CPU (al lado de donde escogemos si queremos compilar la aplicación con Debug o con Release), y clicamos en Administrador de configuración.

Crear instalador .NET 6 con Inno Setup

En la ventana que se nos abre clicamos en Any CPU y pulsamos en Nueva.

Crear instalador .NET 6 con Inno Setup

En «escriba o seleccione la nueva plataforma» escogemos x64 y dejamos la opción de «copiar configuración de» con Any CPU y marcado el check «Crear nuevas plataformas del proyecto».

Crear instalador .NET 6 con Inno Setup

Cerramos todas las ventanas y donde antes nos aparecía Any CPU ahora nos aparecerá x64, muy bien, ya compilamos nuestra aplicación con x64.

Publicar el proyecto

El primer paso es publicar el proyecto en una carpeta para ello abrimos el Visual Studio 2022 y hacemos clic derecho sobre el proyecto y publicar.

Crear instalador .NET 6 con Inno Setup

Después se nos abrirá una ventana en la que debemos de clicar sobre «Carpeta».

Crear instalador .NET 6 con Inno Setup

En el siguiente paso podemos cambiar la ubicación donde se publicarán los ficheros de la aplicación, en mi caso lo dejo por defecto. A continuación nos mostrará la configuración de publicación, pulsamos en «Mostrar todas las configuraciones» y en configuración seleccionamos Release | x64.

Crear instalador .NET 6 con Inno Setup

En la siguiente ventana pulsamos en «Publicar«.

Crear instalador .NET 6 con Inno Setup

Habiendo hecho esto ya tendremos los compilados de nuestra aplicación en el directorio escogido.

Creando el instalador con Inno Setup

Llego la hora de crear nuestro instalador, para que todo esto funcione recuerda que debes de tener la última versión de Inno Setup y también recuerda que para que el servicio se ejecute en caso de que sea un proyecto .NET Core 3, NET 5 o NET 6 debes instalar el hosting de Windows.

Bueno comencemos, abrimos Inno Setup y creamos un script vacío.

Tutorial Inno Setup

Guardamos este script vacío en una carpeta. Nos descargamos el fichero Inno Setup Dependency Installer, abrimos el Zip y descomprimos el fichero CodeDependencies.iss y el netcorecheck_x64.exe (se encuentra en la carpeta src del zip) en la misma carpeta.

Nos deberían de quedar 3 ficheros en la carpeta como puedes apreciar en la imagen, en mi caso mi script vacío es el de instalacion.iss. Abrimos nuestro script vacío y escribimos lo siguiente:

//Nombre de la aplicación #define MyAppName "Test Service" //Versión de la aplicación #define MyAppVersion "1.0" //Autor #define MyAppPublisher "Andres Ledo" //Sitio Web de la aplicación #define MyAppURL "https://andresledo.es" //Ejecutable del servicio #define MyAppExeName "ServiceTest.exe" //Nombre con el que se creará el servicio #define MyService "Service Test" //Directorio donde publicastes los ficheros de la aplicación //ES MUY IMPORTANTE DEJAR EL ASTERISCO DEL FINAL YA QUE INDICA QUE SE COPIEN TODOS LOS FICHEROS DEL DIRECTORIO #define PublishFolder "E:\Ejemplos\ServiceTest\ServiceTest\bin\Release\net6.0\publish\*" //Directorio donde se instalará nuestra aplicación #define InstallationDir "C:\TestService\" //Nombre que tendrá nuestro instalador #define InstallerName "Instalador Test Service" //Agregamos la librería que nos instalará NET 6 en caso de que no este instalado. #define public Dependency_NoExampleSetup #include "CodeDependencies.iss" [Setup] //GUID de la aplicación, ES MUY IMPORANTE CAMBIARLO, pulsando en Tools -> Generate GUID Inno Setup nos genera uno nuevo. //Si no se cambia y se da la casualidad de que hay dos aplicaciones que utilizan Inno Setup habran conflictos en la instalación. AppId={{9DB72F57-1060-4CBD-B287-E4A02E1725E3} //Mapeo de variables AppName={#MyAppName} AppVersion={#MyAppVersion} AppPublisher={#MyAppPublisher} AppPublisherURL={#MyAppURL} AppSupportURL={#MyAppURL} AppUpdatesURL={#MyAppURL} DefaultDirName={#InstallationDir}{#MyAppName} DefaultGroupName={#MyAppName} DisableProgramGroupPage=yes OutputBaseFilename={#InstallerName} //Formato de compresión Compression=lzma SolidCompression=yes //Estilo del instalador WizardStyle=modern //Privilegios requeridos PrivilegesRequired=admin //Indicamos que la aplicación es de 64 bits. ArchitecturesInstallIn64BitMode=x64 //Llamamos a la extensión para que instale NET 6 si es necesario [Code] function InitializeSetup: Boolean; begin Dependency_AddDotNet60Desktop; Result := True; end; //Idiomas del instalador, puedes comentar los que no necesites. [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" Name: "catalan"; MessagesFile: "compiler:Languages\Catalan.isl" Name: "french"; MessagesFile: "compiler:Languages\French.isl" Name: "portuguese"; MessagesFile: "compiler:Languages\Portuguese.isl" Name: "spanish"; MessagesFile: "compiler:Languages\Spanish.isl" [Files] //Indicamos de donde debe copiar los ficheros el instalador, podemos excluir alguno que no nos interese con Excludes Source: {#PublishFolder}; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "conf.xml" //Incluimos también la librería que nos comprobará si está instalado .NET 6 Source: netcorecheck_x64.exe; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; [Icons] Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" [Run] //Instalamos o actualizamos el servicio si no estamos instalando un servicio puedes comentar estas líneas Filename: {sys}\sc.exe; Parameters: "stop ""{#MyService}""" ; Flags: runhidden Filename: {sys}\sc.exe; Parameters: "delete ""{#MyService}""" ; Flags: runhidden Filename: {sys}\sc.exe; Parameters: "create ""{#MyService}"" start= auto binPath= ""{app}\{#MyAppExeName}""" ; Flags: runhidden Filename: {sys}\sc.exe; Parameters: "start ""{#MyService}""" ; Flags: runhidden [UninstallRun] //Desinstalamos el servicio si no estamos instalando un servicio puedes comentar estas líneas Filename: "{cmd}"; Parameters: "/C ""taskkill /im {#MyAppExeName} /f /t" Filename: {sys}\sc.exe; Parameters: "stop ""{#MyService}""" ; Flags: runhidden Filename: {sys}\sc.exe; Parameters: "delete ""{#MyService}""" ; Flags: runhidden
Lenguaje del código: PHP (php)

Con los comentarios que he incluido creo que queda bastante claro lo que hace cada punto, aquí hay varias cosas importantes:

  • #define PublishFolder: es importante dejar «/*» del final, ya que este indica que se copien todos los ficheros del directorio.
  • AppId= es fundamental que sustituyas este GUID por uno nuevo, es muy improbable que encuentres otra instalación en el equipo que tenga el mismo GUID, sin embargo, si haces varios instaladores para diferentes aplicaciones y no lo cambias verás como Inno Setup se cree que es la misma aplicación y en vez de instalar actualiza…

Una vez copiado el script guardamos y en Inno Setup pulsamos en Build – Compile (en la barra de herramientas), esto compilará nuestro instalador. Una vez compilado podemos encontrarlo fácilmente pulsando sobre Build – Open Output Folder.

Para realizar pruebas yo recomiendo hacerlas en una máquina virtual, aunque en principio no debería ocurrir nada en nuestro equipo.

Ejecutamos el instalador y probamos que todo funcione correctamente.

Tutorial Inno Setup

Miramos que el servicio se esté ejecutando.

Tutorial Inno Setup

Comprobamos que nuestro servicio esté realizando la tarea para la que fue creado, en mi caso no hace nada que no sea mostrar la fecha y la hora en un fichero de texto.

Tutorial Inno Setup

Para acabar probamos la desinstalación como si fuese una aplicación normal y corriente desde el panel de control.

Tutorial Inno Setup

Una vez haya finalizado la desinstalación comprobamos que el servicio se haya desinstalado correctamente y ya hemos acabado nuestro instalador.

7 comentarios en «C# Crear instalador NET 6 – Tutorial Inno Setup»

  1. Buenas, necesito generar un instalador(uno solo) que me instale una app de formulario WPF dejando un icono en el escritorio, pero que al mismo tiempo me instale en el equipo mi servicio y lo empiece a ejecutar. Ya el servicio y la app están montadas, pero estoy intentando que se instalen ambos a la vez y no consigo, o se rompe uno, o se rompe el otro, o ambos. Guiándome de tu script de InnoSetup conseguí que se instalase bien el servicio, y también he conseguido que a parte se instale bien la app; cuando intento combinar ambos scripts viene el problema. Me puedes echar una mano? sabes de algo de lo que me pueda guiar? hay poca documentación útil en internet. Muchas gracias y disculpa por las molestias. Un saludo.

    Responder
    • Hola Carlos,

      No sé exactamente el código que estás implementando y tampoco concretas el tipo de error que te da.
      La forma más fácil de tener dos proyectos es un mismo instalador, es referenciar el proyecto secundario desde el proyecto principal, haciendo esto consigues que los ficheros de ambos proyectos se generen en el mismo directorio.

      En tu caso lo que yo haría sería desde el proyecto WPF referenciar al proyecto del servicio, ahora cuando realices la publicación deberías tener ambos ejecutables en el directorio de publicación.

      Este directorio es el que debes de incluir en #define PublishFolder, después en #define MyAppExeName tienes que modificarlo por el ejecutable de la aplicación WPF y para acabar deberías crear otra variable, por ejemplo #define ServiceExe «EjecutableServicio.exe» y en la parte final del Script [RUN] y [UninstallRun] tienes que substituir #MyAppExeName por #ServiceExe para hacer referencia al servicio.

      Haciendo esto debería de funcionarte.

      Saludos.

      Responder
    • Hola Carlos,

      No entiendo muy bien a que te refieres, el servicio siempre se está ejecutando y tú configuras el tiempo que debe esperar a repetir la acción en el propio ejecutable. En el caso de .NET en await Task.Delay(1000, stoppingToken); para que se ejecute cada segundo, por ejemplo.

      Saludos.

      Responder
  2. Hola Andrés, gracias por el tutorial, ha sido muy útil.
    Ahora me gustaría añadir a mi instalador una pantalla para poner el Usuario y la contraseña del pc, para ponerlos cuando hace el create en el [Run].
    He puesto ésto en el procedure Dependency_Internal1:

    Usuario, Contrasenya: String;

    // Create the page
    Page := CreateInputQueryPage(wpWelcome, ‘Credenciales’, », »);

    // Add items (False means it’s not a password edit)
    Page.Add(‘&Usuario:’, False);
    Page.Add(‘&Contraseña:’, False);

    // Read values into variables
    Usuario := Page.Values[0];
    Contrasenya := Page.Values[1];

    Y me ha funcionado. Ahora bien, cuando capturo las variables no sé como pasarlas del CodeDependencies.iss al MainScript. ¿Tu sabes como debe hacerse?

    Muchas gracias!

    Responder
  3. Hola Andrés, gracias por responder tan rápido.

    Pues al final lo conseguí y la respuesta es más simple de lo que creía. Quité esas líneas del CodeDependencies.iss y añadí las siguentes en el apartado [Code] del MainScript:

    var
    Page: TInputQueryWizardPage;

    function ObtenerUsuario(Param: string): String;
    begin
    Result := Page.Values[0];
    end;

    function ObtenerContrasenya(Param: string): String;
    begin
    Result := Page.Values[1];
    end;

    procedure InitializeWizard2;
    begin
    Page := CreateInputQueryPage(wpWelcome, ‘Credenciales’, », »);
    Page.Add(‘&Usuario:’, False);
    Page.Add(‘&Contraseña:’, True);
    end;

    Y en el [Run] pude capturarlas así:

    Filename: {sys}\sc.exe; Parameters: «create «»{#MyService}»» start= auto binPath= «»{app}\{#MyAppExeName}»» displayname=»»Nombre»» obj= «»{code:ObtenerUsuario}»» password= «»{code:ObtenerContrasenya}»»»; Flags: runhidden

    Un saludo!

    Responder

Deja un comentario

Send this to a friend