Crear e instalar un servicio Windows

Estos días he estado trabajando con servicios Windows, los cuales se utilizan para realizar operaciones en background. Una de las principales ventajas de los mismos es que tenemos la posibilidad de ejecutarlos sin necesidad de tener un usuario logado en el sistema.

En este post voy a mostrar la forma de crear un proyecto con una pequeña demo y varias formas para instalarlo.

Creación del proyecto

Creamos un proyecto del tipo de Windows Service. En este post lo llamaré CheckerWindowsService.

Cuando carga la plantilla de Visual Studio, vemos que tenemos un nuevo archivo llamado Service1.cs y el típico Program.cs que acompaña a las aplicaciones de consola. Si accedemos al primero de ellos, nos mostrará la parte de diseño que básicamente es un fondo gris 😀 Si pulsamos sobre ella con el botón derecho y hacemos clic en View Code veremos la siguiente estructura:

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text; 

namespace CheckerWindowsService
{
    public partial class Service1 : ServiceBase
    {
        public Service1()
        {
            InitializeComponent();
        } 

        protected override void OnStart(string[] args)
        {
        } 

        protected override void OnStop()
        {
        }
    }
} 

La clase Service1 debe heredar de ServiceBase para obtener todos los métodos y propiedades relacionadas con los servicios Windows. En esta plantilla se muestran los métodos que normalmente se sobrescriben: OnStart donde insertaremos el código que queremos que se ejecute nada más iniciar el servicio y OnStop en el caso de que el mismo sea parado. Para este ejemplo, voy a renombrar la clase Service1.cs a CheckerService.cs.

Implementación

Lo primero que debemos modificar son las propiedades del archivo CheckerService.cs

 

Por nombrar algunas de las propiedades más utilizadas, AutoLog se utiliza para permitir que el servicio registre o no los inicios, paradas, etcétera en el visor de eventos del sistema (opción por defecto). CanPauseAndContinue nos da la posibilidad de pausar y continuar con la ejecución del servicio, CanStop pararlo (al hacer esta opción pasará por el método OnStop) y ServiceName donde asignamos el nombre del servicio.

En esta demo lo que vamos a hacer es que cada minuto vamos a comprobar si existe algún proceso del explorador Firefox abierto y, de ser así, vamos a finalizarlo (y si se resiste matarlo :D).  Sobre la vista de diseño pulsamos con el botón derecho y seleccionamos View Code para agregar lo siguiente:


protected override void OnStart(string[] args)
{

   var timer = new Timer { AutoReset = true, Interval = 60000 };
   timer.Elapsed += timer_Elapsed;
   timer.Start();
}

private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
    var processes = Process.GetProcessesByName("firefox");

    foreach (var process in processes)
    {
       process.CloseMainWindow();

       if (!process.HasExited)
       {
            process.Kill();
            process.Close();
       }
    }
}

Añadir Instalador

Una vez que tengamos nuestro código implementado, es necesario añadir un instalador a la solución. Para ello nos ponemos en el modo diseño y con el botón derecho seleccionamos Add Installer.

En este momento se agregará una nueva clase llamado ProjectInstaller.cs con dos componentes. Hacemos clic sobre serviceInstaller1 donde indicaremos el nombre del servicio (el mismo ServiceName que en CheckerService.cs) y el tipo de inicio (Manual, Automático o Deshabilitado). Además podemos indicar el nombre a mostrar y una descripción de lo que hace la aplicación. Estos dos últimos datos serán los que se mostrarán en el listado de servicios de Windows.

Por último, si seleccionamos el otro elemento, serviceProjectInstaller1, podemos seleccionar el tipo de cuenta con la que queremos que se ejecute el servicio. En este caso, utilizaré LocalSystem para tener los permisos suficientes para matar procesos.

serviceProjectInstaller-Account

Instalación/Desinstalación

Para la instalación del servicio Windows tenemos varias alternativas:

  • Utilizar la plantilla para el proyecto del tipo Setup Project.
  • Crear instaladores con InstallShield.
  • El comando InstallUtil.exe desde la línea de comandos.

Setup Project

Adjuntamos a nuestra solución un nuevo proyecto del tipo Setup Project.

Seleccionamos el proyecto que acabamos de crear y añadimos la salida del proyecto del servicio web.

Aparecerá un cuadro de diálogo con el proyecto Windows Service por defecto donde dejaremos seleccionado Primary output y confirmamos.

En el Explorer Solution hacemos click en el botón Custom Actions Editor.

 Y aparecerá una nueva ventana donde se alojan las acciones personalizadas. Con el botón derecho añadimos una nueva como se muestra en la imagen:

Seleccionamos Application Folder.

Y por último aparecerá únicamente Primary output from CheckerWindowsService que es la que añadimos anteriormente. Pulsamos en OK y ¡Listo!

Primary-output-from-CheckerWindowsService1

Para instalarlo, compilamos nuestra solución y posteriormente el proyecto Setup Project. Seleccionamos el proyecto de instalación con el botón derecho y hacemos click en Install.

Para desinstalar la operación sería la misma pero con Unistall 😀

InstallShield

De lo poco que pude trastear con InstallShield, pude ver que las opciones de configuración son mayores que con Setup Project y te permite una mejor personalización de las instalaciones.

Si bien con Visual Studio 2010 puedes hacer uso de InstallShield Limited Edition.

Para poder crear un installer para un windows service necesitas tener al menos una versión Premium, obviamente de pago.

InstallUtil.exe

Desde la línea de comandos también podemos instalar/desinstalar un servicio Windows. El comando InstallUtil.exe se instala automáticamente con Visual Studio y podemos ejecutarlo a través de Visual Studio Command Prompt.

Instalación

InstallUtil.exe miEjecutable.exe

Desinstalación

InstallUtil.exe /u miEjecutable.exe

Espero que os sea de utilidad 😀

¡Saludos!