Ambrosio o cómo crear un servicio de Windows obediente en .NET.

Últimamente he estado programando un servicio de Windows para que el sistema realice de manera periódica y en segundo plano unas determinadas tareas. En mi caso concreto se trata de replicar unas determinadas tablas desde una base de datos Oracle hacia otra MySQL con una determinada periodicidad en función de cada tabla (unos datos son más críticos y necesitan actualizarse cada hora, otros menos variables los actualizamos una vez al día durante las horas valle de actividad). El caso es que con VisualBasic.NET me ha resultado bastante sencillo crearlo y después instalarlo, así que paso a explicar un ejemplo de cómo hacer un sencillo servicio de Windows.

Paso 1. Creamos el proyecto.

Para empezar simplemente iniciamos VisualStudio.NET (en mi caso 2005) y seleccionamos la opción para crear un nuevo proyecto. Ahí seleccionamos Servicio de Windows, le damos un nombre agradable (a poder ser más que WindowsService1, por ejemplo ServicioAlertas) y aceptamos. Nos aparecerá directamente la clase Service1.vb que también podemos renombrar a algo más intuitivo como clsAlert.vb.

Paso 2. Agregamos un instalador.

En la vista diseño de la clase que se nos ha creado hacemos clic con el botón derecho del ratón y seleccionamos la opción Agregar instalador. Con esto se nos creará la clase ProjectInstaller.vb que contiene dos controles: ServiceInstaller1 y ServiceProcessInstaller1. En mi caso renombro estos dos controles simplemente para eliminarles el 1 final (maniático que es uno). Del código de ProjectInstaller.vb nos olvidaremos por completo, pero vamos a hacer algún ajuste en las propiedades de estos dos controles.

Paso 3. Configuramos el instalador.

En el control ServiceInstaller modificamos las propiedades DisplayName y ServiceName para dejarlas ambas en ServicioAlertas. Modificamos también la propiedad StartType para dejarla en Automatic. Esto último sirve para que después cuando instalemos el servicio, éste quede puesto para que arranque automáticamente al iniciarse el sistema operativo. Si no se desea este comportamiento, ajustar esta propiedad según convenga. Es decir:

DisplayName: ServicioAlertas
ServiceName: ServicioAlertas
StartType:   Automatic

En el control ServiceProcessInstaller modificamos la propiedad Account para dejarla en LocalSystem. Esto indica qué tipo de cuenta se utilizará para ejecutar el servicio. Podemos dejarla en User, pero después al instalar el servicio pedirá cuenta de usuario y contraseña, así que yo recomiendo pasarla a LocalSystem. Así pues:

Account:     LocalSystem

Con la clase ProjectInstaller.vb hemos terminado ya por completo, ahora nos centraremos en clsAlert.vb.

Paso 4. Programamos el servicio.

Accedemos al código de la clase clsAlert.vb y observamos que por defecto ya se nos han generado dos métodos: OnStart y OnStop. Está claro para lo que sirven, ¿no? Eso es, para configurar qué hacer y cómo para arrancar y detener el servicio. Personalmente no utilizo el OnStop para nada, así que no hablaré de él, pero por supuesto el que esté interesado puede buscar más información en la propia ayuda del IDE o en internet. Me voy a centrar en el OnStart y voy a ponerle un temporizador para que cada 5 segundos me escriba en un log la hora actual. Sí, lo sé, es un servicio de alertas mediocre, pero servirá como ejemplo de código sencillo con el que programar un servicio y hacer uso, además, de un temporizador (cosa terriblemente útil en ocasiones).

Muestro el código íntegro que dejo pues en la clase clsAlert.vb:

'--------------------------------------------------------------------
' Author:      Albert Mata (www.albertmata.net)
' Date:        20080723
' Description: Class to show how to create a Windows service and how
'              to work with a timer. 
'--------------------------------------------------------------------
Public Class clsAlert

    '----------------------------------------------------------------
    ' Attributes.
    '----------------------------------------------------------------
    Private DBTimer As System.Timers.Timer

    '----------------------------------------------------------------
    ' Starts service.
    '----------------------------------------------------------------
    Protected Overrides Sub OnStart(ByVal args() As String)
        'Creating timer with interval = 5000 milisec = 5 seconds.
        DBTimer = New System.Timers.Timer(5000)
        DBTimer.Enabled = True
        AddHandler DBTimer.Elapsed, AddressOf Me.ShowAlert
    End Sub

    '----------------------------------------------------------------
    ' Stops service.
    '----------------------------------------------------------------
    Protected Overrides Sub OnStop()
        'Void, as I'm not using this method.
    End Sub

    '----------------------------------------------------------------
    ' Main process executed every time DBTimer gives a signal.
    '----------------------------------------------------------------
    Private Sub ShowAlert(ByVal source As Object, _
    ByVal e As System.Timers.ElapsedEventArgs)
        DBTimer.Enabled = False
        Dim LogFile As New System.IO.StreamWriter("C:\log.txt", True)
        LogFile.WriteLine("Alerta www.albertmata.net - " & Date.Now)
        LogFile.Close()
        DBTimer.Enabled = True
    End Sub

End Class

Con esto tenemos terminado nuestro servicio de Windows. Ahora sólo nos falta instalarlo.

Paso 5. Generamos el ejecutable.

Generamos el ejecutable de nuestro servicio (Generar o Volver a generar). A partir de aquí no necesitamos más el IDE, de modo que podemos cerrarlo. A partir de ahora sólo necesitamos el .exe que se nos acaba de generar y un par de archivos .bat que vamos a crear a continuación (podríamos no generarlos y escribir las instrucciones directamente en línea de comandos, pero a mí me resulta más cómodo hacerlo en archivos batch). El archivo .exe (que encontramos en la carpeta bin/Release de nuestro proyecto ServicioAlertas) lo copiaremos en una ruta más corta (C:\). Y los dos archivos .bat que necesitamos son los siguientes:

Instalador.bat

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\InstallUtil 
                                 "C:\ServicioAlertas.exe"
pause

Desinstalador.bat

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\InstallUtil /U
                                 "C:\ServicioAlertas.exe"
pause

En ambos las dos primeras líneas deben ser una única línea continuada (el ancho del blog no lo permite) y la ruta de la aplicación InstallUtil puede variar ligeramente en función de la versión del Framework que estemos utilizando. Basta con verificarlo con el Explorador de Windows y modificarla según convenga.

Para lo que sirven está claro: uno instala el servicio y el otro lo desinstala.

Paso 6. Instalamos el servicio.

Simplemente ejecutamos el archivo Instalador.bat y se nos abrirá una ventana de línea de comando con el proceso de la instalación terminando exitosamente. Ahora nos vamos a Panel de control - Herramientas administrativas - Servicios y encontraremos nuestro servicio ServicioAlertas. Lo iniciamos y observaremos al cabo de pocos segundos que en la ruta C:\ se nos ha creado un archivo log.txt. Si dejamos el servicio un rato funcionando y después lo detenemos veremos que este archivo ha quedado más o menos así:

Alerta www.albertmata.net - 23/07/2008 21:12:18
Alerta www.albertmata.net - 23/07/2008 21:12:23
Alerta www.albertmata.net - 23/07/2008 21:12:28
Alerta www.albertmata.net - 23/07/2008 21:12:33
Alerta www.albertmata.net - 23/07/2008 21:12:38

Paso 7. Desinstalamos el servicio.

Si en algún momento queremos desinstalar el servicio (si es tan poco útil como ServicioAlertas seguro que querremos) solo debemos ejecutar Desinstalador.bat y el servicio quedará desinstalado (tendremos que refrescar la lista de servicios para ver que efectivamente así es).

Con esto queda visto el tema de los servicios de Windows. En internet se puede encontrar mucha otra información al respecto (p.ej. ésta en CodeGuru) en la que nos alertan de no utilizar MsgBox y similares en un servicio porque suelen dar problemillas. ;-)

Tags: , , , , ,

7 Responses to “Ambrosio o cómo crear un servicio de Windows obediente en .NET.”

  1. Aldrin Ronco se atrevió a comentar:

    Muchas gracias por esta información, llevaba bastante buscando y no había encontrado un ejemplo tan práctico como este, de verdad, muchísimas gracias.

  2. Albert Mata se atrevió a comentar:

    Pues muchísimas de nada, encantado de que te haya sido útil...

  3. Frederick se atrevió a comentar:

    Perfecto!! era lo que buscaba, me ha quedado claro como el agua, por cierto no se si puedes ayudarme, tengo que crear una carpeta caliente “HotFolder” donde cuando ponga un archivo PDF me salga impreso en una impresora determinada, se te ocurre algo?, gracias por adelantado,, un saludo..

  4. Albert Mata se atrevió a comentar:

    Ostras, primera vez que oigo ese concepto de 'hot folder', así que ni idea, pero puestos a imaginar... igual puedes crear un servicio que cada X segundos revise los archivos en un directorio (puede mantener un registro en un archivo .log) y cuando encuentre uno nuevo ejecute la acción que sea (mandarlo a imprimir, en este caso). ¡De hecho me gusta la idea, igual un día de estos desarrollo algo así

    Un saludo.

  5. Matheoz se atrevió a comentar:

    Muchas gracias por el ejemplo, fue de gran ayuda, sobre todo como instalarlo.

    Me dio el siguiente error:
    No se encontro 'Sub Main' en 'ServicioAlertas.Service1'

    para corregir este error, Solo escoji como Objeto inicial ServicioAlertas.clsAlert

    Gracias.

  6. Juan Fernando Ospina se atrevió a comentar:

    Megustaría aportar un articulo parecido en visual studio 2008 con c# utilizando hilos.

    http://www.algoritmosen.net/Lecciones/LeccionesenVisualStudio/Implementar_Servicio_Windows/tabid/92/Default.aspx

  7. Marín se atrevió a comentar:

    Excelente ejemplo. saludos

Leave a Reply