Cómo crear un instalador para un servicio de Windows .net usando Visual Studio

Respuestas:

227

En el proyecto de servicio, haga lo siguiente:

  1. En el explorador de soluciones, haga doble clic en el archivo .cs de servicios. Debería mostrar una pantalla que es todo gris y habla sobre arrastrar cosas desde la caja de herramientas.
  2. Luego haga clic derecho en el área gris y seleccione agregar instalador. Esto agregará un archivo de proyecto de instalación a su proyecto.
  3. Entonces tendrá 2 componentes en la vista de diseño de ProjectInstaller.cs (serviceProcessInstaller1 y serviceInstaller1). A continuación, debe configurar las propiedades según lo necesite, como el nombre del servicio y el usuario con el que debe ejecutarse.

Ahora necesita hacer un proyecto de configuración. Lo mejor que puede hacer es usar el asistente de configuración.

  1. Haga clic derecho en su solución y agregue un nuevo proyecto: Agregar> Nuevo proyecto> Proyectos de configuración e implementación> Asistente de configuración

    a. Esto podría variar ligeramente para diferentes versiones de Visual Studio. si. Visual Studio 2010 se encuentra en: Plantillas de instalación> Otros tipos de proyectos> Configuración e implementación> Instalador de Visual Studio

  2. En el segundo paso, seleccione "Crear una configuración para una aplicación de Windows".

  3. En el tercer paso, seleccione "Salida primaria de ..."

  4. Haga clic para finalizar.

Luego edite su instalador para asegurarse de que se incluye la salida correcta.

  1. Haga clic derecho en el proyecto de configuración en su Explorador de soluciones.
  2. Seleccione Ver> Acciones personalizadas. (En VS2008 podría ser Ver> Editor> Acciones personalizadas)
  3. Haga clic derecho en la acción Instalar en el árbol de Acciones personalizadas y seleccione 'Agregar acción personalizada ...'
  4. En el cuadro de diálogo "Seleccionar elemento en el proyecto", seleccione Carpeta de la aplicación y haga clic en Aceptar.
  5. Haga clic en Aceptar para seleccionar la opción "Salida principal de ...". Se debe crear un nuevo nodo.
  6. Repita los pasos 4 a 5 para las acciones de confirmación, reversión y desinstalación.

Puede editar el nombre de salida del instalador haciendo clic derecho en el proyecto Instalador en su solución y seleccionando Propiedades. Cambie el 'Nombre del archivo de salida:' a lo que quiera. Al seleccionar el proyecto de instalación, así y mirando a las ventanas de propiedades, puede editar el Product Name, Title, Manufacturer, etc ...

Luego construya su instalador y producirá un MSI y un setup.exe. Elija el que quiera usar para implementar su servicio.

Kelsey
fuente
37
@El Ronnoco, tuve la respuesta mucho antes de publicar. Quería documentarlo aquí porque siempre tengo que buscarlo cada 6 a 12 meses (y no ha sido tan fácil de encontrar), así que ahora puedo buscarlo fácilmente para todos y puedo encontrarlo yo mismo rápidamente :)
Kelsey
1
Desafortunadamente, también es la respuesta incorrecta. Sí, sé que encontrará esto en libros y MSDN, pero es un caso en el que un grupo de Microsoft no habló con otro grupo en Microsoft y encontró una solución inferior a un problema que ya se había resuelto. Consulte blog.iswix.com/2006/07/msi-vs-net.html para obtener más información.
Christopher Painter
9
@ Christopher Painter He estado usando el instalador de MS desde2k5 y nunca ha tenido un problema. Si está de acuerdo o no con él y lo considera un 'antipatrón' no es el punto de esta pregunta, es cómo hago x con y, no cómo hago a con b. Cuando publiqué la pregunta fue para fines de documentación.
Kelsey
3
Entonces has tenido suerte durante 6 años, simplemente no lo sabes. Es posible que desee leer: robmensching.com/blog/posts/2007/4/19/…
Christopher Painter
1
Si obtiene un Service name contains invalid characters, is empty, or is too long (max length = 80)error al agregar el instalador, haga clic derecho en el área gris nuevamente, vaya a Propiedades y asegúrese de que el valor del Nombre del servicio esté configurado.
wolfyuk
51

Sigo los primeros pasos de Kelsey para agregar las clases de instalador a mi proyecto de servicio, pero en lugar de crear un instalador MSI o setup.exe, hago que el servicio se instale / desinstale automáticamente. Aquí hay un poco de código de muestra de uno de mis servicios que puede usar como punto de partida.

public static int Main(string[] args)
{
    if (System.Environment.UserInteractive)
    {
        // we only care about the first two characters
        string arg = args[0].ToLowerInvariant().Substring(0, 2);

        switch (arg)
        {
            case "/i":  // install
                return InstallService();

            case "/u":  // uninstall
                return UninstallService();

            default:  // unknown option
                Console.WriteLine("Argument not recognized: {0}", args[0]);
                Console.WriteLine(string.Empty);
                DisplayUsage();
                return 1;
        }
    }
    else
    {
        // run as a standard service as we weren't started by a user
        ServiceBase.Run(new CSMessageQueueService());
    }

    return 0;
}

private static int InstallService()
{
    var service = new MyService();

    try
    {
        // perform specific install steps for our queue service.
        service.InstallService();

        // install the service with the Windows Service Control Manager (SCM)
        ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location });
    }
    catch (Exception ex)
    {
        if (ex.InnerException != null && ex.InnerException.GetType() == typeof(Win32Exception))
        {
            Win32Exception wex = (Win32Exception)ex.InnerException;
            Console.WriteLine("Error(0x{0:X}): Service already installed!", wex.ErrorCode);
            return wex.ErrorCode;
        }
        else
        {
            Console.WriteLine(ex.ToString());
            return -1;
        }
    }

    return 0;
}

private static int UninstallService()
{
    var service = new MyQueueService();

    try
    {
        // perform specific uninstall steps for our queue service
        service.UninstallService();

        // uninstall the service from the Windows Service Control Manager (SCM)
        ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location });
    }
    catch (Exception ex)
    {
        if (ex.InnerException.GetType() == typeof(Win32Exception))
        {
            Win32Exception wex = (Win32Exception)ex.InnerException;
            Console.WriteLine("Error(0x{0:X}): Service not installed!", wex.ErrorCode);
            return wex.ErrorCode;
        }
        else
        {
            Console.WriteLine(ex.ToString());
            return -1;
        }
    }

    return 0;
}

fuente
1
Por curiosidad, ¿cuál es el beneficio de tener un servicio de autoinstalación / desinstalación? Si el servicio se instala solo, ¿cómo se inicia el servicio primero para que pueda instalarse en primer lugar? Si hay un mecanismo para iniciar el servicio sin instalarlo, ¿por qué molestarse en instalarlo?
Kiley Naro
3
@ Christopher - Yo no. Mi solución no es un reemplazo para un instalador completo que usarías para distribuir software. Estoy presentando otra opción que funciona para algunas situaciones, como la mía, donde escribo software que maneja PCs incrustadas en quioscos desatendidos.
44
Cuando lo instale en una máquina de producción, recuerde ejecutarlo como administrador. Creé un archivo BAT que llama al archivo EXE con el parámetro / i pero no funcionó en el entorno de producción, a pesar de que ejecuté el archivo BAT como administrador. Tuve que abrir un indicador de línea de comandos como administrador e invocar el archivo EXE / i explícitamente (sin usar el archivo BAT). Al menos eso me sucedió en un Windows Server 2012.
Francisco Goldenstein
1
RE: Sin salida en la línea de comando. Utilizando VS 2017 Comunidad de mi nuevo proyecto de servicio por defecto en Tipo de salida: Windows Applicationy el objeto de inicio: (none). Tuve que cambiar el tipo de salida Console Applicationy establecer mi objeto de inicio, por ejemplo myservice.Program. Si puede haber ramificaciones que desconozco, por favor avise.
Jonathan
1
¿El código de ejemplo tiene errores tipográficos? ¿Por qué hay tres servicios diferentes (CSMessageQueueService, MyService, MyQueueService)?
Nils Guillermin
27

Ni Kelsey, ni las soluciones de Brendan no me funcionan en Visual Studio 2015 Community.

Aquí están mis breves pasos sobre cómo crear un servicio con el instalador:

  1. Ejecute Visual Studio, vaya a File->New->Project
  2. Seleccione .NET Framework 4, en 'Buscar plantillas instaladas', escriba 'Servicio'
  3. Seleccione 'Servicio de Windows'. Escriba el nombre y la ubicación. Presione OK.
  4. Haga doble clic en Service1.cs, haga clic derecho en el diseñador y seleccione 'Agregar instalador'
  5. Haga doble clic en ProjectInstaller.cs. Para serviceProcessInstaller1, abra la pestaña Propiedades y cambie el valor de la propiedad 'Cuenta' a 'LocalService'. Para serviceInstaller1, cambie 'ServiceName' y configure 'StartType' en 'Automatic'.
  6. Haga doble clic en serviceInstaller1. Visual Studio crea serviceInstaller1_AfterInstalleventos. Escribir código:

    private void serviceInstaller1_AfterInstall(object sender, InstallEventArgs e)
    {
        using (System.ServiceProcess.ServiceController sc = new 
        System.ServiceProcess.ServiceController(serviceInstaller1.ServiceName))
        {
            sc.Start();
        }
    }
  7. Construir solución. Haga clic derecho en el proyecto y seleccione 'Abrir carpeta en el Explorador de archivos'. Vaya a bin \ Debug .

  8. Cree install.bat con el siguiente script:

    :::::::::::::::::::::::::::::::::::::::::
    :: Automatically check & get admin rights
    :::::::::::::::::::::::::::::::::::::::::
    @echo off
    CLS 
    ECHO.
    ECHO =============================
    ECHO Running Admin shell
    ECHO =============================
    
    :checkPrivileges 
    NET FILE 1>NUL 2>NUL
    if '%errorlevel%' == '0' ( goto gotPrivileges ) else ( goto getPrivileges ) 
    
    :getPrivileges 
    if '%1'=='ELEV' (shift & goto gotPrivileges)  
    ECHO. 
    ECHO **************************************
    ECHO Invoking UAC for Privilege Escalation 
    ECHO **************************************
    
    setlocal DisableDelayedExpansion
    set "batchPath=%~0"
    setlocal EnableDelayedExpansion
    ECHO Set UAC = CreateObject^("Shell.Application"^) > "%temp%\OEgetPrivileges.vbs" 
    ECHO UAC.ShellExecute "!batchPath!", "ELEV", "", "runas", 1 >> "%temp%\OEgetPrivileges.vbs" 
    "%temp%\OEgetPrivileges.vbs" 
    exit /B 
    
    :gotPrivileges 
    ::::::::::::::::::::::::::::
    :START
    ::::::::::::::::::::::::::::
    setlocal & pushd .
    
    cd /d %~dp0
    %windir%\Microsoft.NET\Framework\v4.0.30319\InstallUtil /i "WindowsService1.exe"
    pause
  9. Cree el archivo uninstall.bat (cambie la línea pen-ult /ia /u)
  10. Para instalar e iniciar el servicio, ejecute install.bat, para detener y desinstalar, ejecute uninstall.bat
Alexey Obukhov
fuente
14

Para VS2017, deberá agregar la extensión VS "Proyectos del instalador de Microsoft Visual Studio 2017". Esto le dará plantillas de proyecto adicionales del instalador de Visual Studio. https://marketplace.visualstudio.com/items?itemName=VisualStudioProductTeam.MicrosoftVisualStudio2017InstallerProjects#overview

Para instalar el servicio de Windows, puede agregar un nuevo proyecto tipo asistente de configuración y seguir los pasos de la respuesta de Kelsey https://stackoverflow.com/a/9021107/1040040

JustSomeDev
fuente
1

Las clases InstallUtil (ServiceInstaller) son consideradas un antipatrón por la comunidad de Windows Installer. Es una reinvención de la rueda frágil, fuera de proceso, que ignora el hecho de que Windows Installer tiene soporte incorporado para los Servicios.

Los proyectos de implementación de Visual Studio (que tampoco se consideran ni se consideran en desuso en la próxima versión de Visual Studio) no tienen soporte nativo para los servicios. Pero pueden consumir módulos de fusión. Por lo tanto, echaría un vistazo a este artículo de blog para comprender cómo crear un módulo de combinación utilizando Windows Installer XML que pueda expresar el servicio y luego consumir ese módulo de combinación en su solución VDPROJ.

Aumento de InstallShield utilizando Windows Installer XML - Servicios de Windows

Tutorial de servicio de Windows IsWiX

Video del servicio de Windows IsWiX

Christopher Painter
fuente
1
En el antiguo Visual Studio había un proyecto de implementación, con un instalador de creación fácil. ¿Ahora tengo que comprar un componente de software de terceros?
Alexey Obukhov
@AlexeyObukhov Puedes usar Wix gratis, eso es lo que VS usa, pero el problema con Wix es el mismo que el problema con Git: la curva de aprendizaje casi vertical.
Alan B