Veremos que es, para que puede servir, como desplegar-lo y debugarlo. Lo veremos a través de un sencillo ejemplo que mueve ficheros de un directorio a otro cada segundo. Lo veremos en VB.NET y C#.NET
¿Que es?
Un servicio no deja de ser una aplicación sin interfaz gráfica que se lanza cada X tiempo.
¿Para que sirve?
Sirve por si queremos que cada x minutos haga algún proceso.
Por ejemplo, si tenemos que en nuestra aplicación se generen logs, en lugar de intentar enviar el log justo en el momento, puede interesarnos que se genere un registro en una BDD o que genere un fichero xml y que nos envíe todos los errores cada 30 segundos.
Otro ejemplo podría ser que en el servidor, una vez cada hora compruebe si hay actualizaciones y se las descargue.
Otro ejemplo más, sería la posibilidad de adjuntar información a la BDD, si nos llega una información por xml ( nos llega un alta de un coche por xml y tenemos un servicio que cada 20 segundos los introduce en la BDD )
Como veis puede llegar a ser bastante útil y tiene muchas posibilidades.
Empezamos
Crearemos un servicio muy sencillo que mueva ficheros, en mi caso lo uso para historificar documentos adjuntos de unas operaciones.
Primero, tenemos que crear un proyecto de tipo servicio, para ello le damos a New Project y le damos a Windows Service, le ponemos un nombre y una ubicación. Aquí vemos que hay una sola clase (la llamaremos svr), esta será donde pongamos las llamadas a funciones(clsFunciones) y el thread que lo recorre. Aparte de esta clase le pondremos otras: una clase de instalación (installSvr), una que contendrá la lógica del negocio (clsFunciones) y un directorio «Xml» que contendrá las configuraciones que queramos.
Xml de configuración
En el proyecto crearemos un directorio xml ( es únicamente por organización ) y crearemos un xml que se llame config.xml ( por ejemplo ), este contendrá las configuraciones del servicio como el nombre y descripción del servicio, directorios que necesitemos, etc.
Por ejemplo, una posible estructura sería:
Quitar los espacios de los <, si no, no se veían
<configuraciones> <configuracion> <nombre>NombreServicio</nombre> <valor>Servicio de pruebas</valor> <descripcion>El nombre del servicio que se mostrará en la consola de servicios de windows</descripcion> </configuracion> <configuracion> <nombre>DescripcionServicio <valor>Servicio que se ocupa de historificar los ficheros <descripcion>La descripción del servicio que se mostrará en la consola de servicios de windows </configuracion> <configuracion> <nombre>DirectorioLog</nombre> <valor>D:Logs</valor> <descripcion>El directorio donde están los logs</descripcion> </configuracion> <configuracion> <nombre>DirectorioFicheros</nombre> <valor>Servicio de pruebas</valor> <descripcion>El directorio donde están los ficheros que queremos historificar</descripcion> </configuracion> <configuracion> DirectorioHistorico</nombre> D:File2</valor> El directorio donde estarán los ficheros que queremos historificar</descripcion> </configuracion> </configuraciones>
La clase clsFunciones
Esta clase será la que contendrá lo que queremos hacer realmente (copiar ficheros, introducir datos, …) y la ponemos en una clase aparte para tenerlo bien separado.
Como hemos dicho, este será un servicio que mueva ficheros, así que aquí pondremos las funciones necesarias para ello.
Crearemos una función para hacer las pruebas:
VB.NET
Public Function historificarFicheros() As Boolean Try '' dameConfig es una funcion que busca en el xml una configuración Dim strDirFiles As String = dameConfig("DirectorioFicheros") Dim strDirHistorificar As String = dameConfig("DirectorioHistorico") If System.IO.Directory.Exists(strDirFiles) Then Dim Files As String() = IO.Directory.GetFiles(strDirFiles) 'obtengo todos los ficheros del directorio For Each strFile As String In Files If IO.Directory.Exists(strDirHistorificar) Then Dim strNombre As String = strFile.Split(""(0)).Last() 'obtengo el nombre del fichero If IO.File.Exists(strFile) Then IO.File.Move(strFile, strDirHistorificar & "" & strNombre) End If Next Return True Else ''LOG End If Return False Catch ex As Exception ''LOG Return False End Try End Function
C#.NET
public bool historificarFicheros() { try { // dameConfig es una funcion que busca en el xml una configuración String strDirFiles = dameConfig("DirectorioFicheros"); String strDirHistorificar = dameConfig("DirectorioHistorico"); if (System.IO.Directory.Exists(strDirFiles)) { String[] Files = System.IO.Directory.GetFiles(strDirFiles); //obtengo todos los ficheros del directorio foreach (String strFile in Files) { if (System.IO.Directory.Exists(strDirHistorificar)) { String strNombre = strFile.Split('\').Last(); //obtengo el nombre del fichero if (System.IO.File.Exists(strFile)) System.IO.File.Move(strFile, strDirHistorificar + "\" + strNombre); } } return true; } else { //LOG } return false; } catch (Exception) { //LOG return false; throw; } }
La clase «svr»
Esta es la clase de la sesión, aquí indicaremos lo que hará y cada cuanto. Ponemos las siguientes variables que son para que cada x tiempo lance una función
VB.NET
Private WithEvents temporizador As Timers.Timer Private intervalo As Integer = 1000
C#.NET
private System.Timers.Timer temporizador; private Integer intervalo=1000;
Sobrescribimos el constructor del servicio para que se pueda pausar, continuar y que se inicie directamente.
VB.NET
Public Sub New() MyBase.New() InitializeComponent() CanPauseAndContinue = True OnStart(Nothing) End Sub
C#.NET
public svr() { InitializeComponent(); CanPauseAndContinue = true; OnStart(null); }
Después sobrescribimos los métodos de start, stop y continue. Estos eventos se provocan en la consola de servicios de windows cuando inicias, paras, reseteas …
VB.NET
Protected Overrides Sub OnStart(ByVal args() As String) temporizador = New Timers.Timer(intervalo) 'le indicamos cada cuanto tiene que hacer su funcion temporizador.Start() 'iniciamos el timer" End Sub Protected Overrides Sub OnStop() temporizador.Stop() End Sub Protected Overrides Sub OnContinue() temporizador.Start() End Sub
C#.NET
protected override void OnStart(string[] args) { temporizador = new System.Timers.Timer(intervalo); //le indicamos cada cuanto tiene que hacer su funcion temporizador.Start(); //iniciamos el timer } protected override void OnStop() { temporizador.Stop(); } protected override void OnContinue() { temporizador.Start(); }
Y por último capturamos el evento del timer que se lanza cuando pasa el tiempo
VB.NET
Private Sub temporizador_Elapsed(ByVal sender As Object, _ ByVal e As System.Timers.ElapsedEventArgs) _ Handles temporizador.Elapsed Try ' Se deshabilita temporalmente el timer temporizador.Enabled = False Dim histFiles As New clsFunciones histFiles.historificarFicheros() Catch ex As Exception ''LOG Finally temporizador = New Timers.Timer(intervalo) '' se reinicia el timer temporizador.Enabled = True End Try End Sub
C#.NET
private void temporizador_Elapsed(Object sender, System.Timers.ElapsedEventArgs e) { try { // Se deshabilita temporalmente el timer temporizador.Enabled = false; clsFunciones histFiles = new clsFunciones(); histFiles.historificarFicheros(); } catch (Exception) { //LOG throw; } finally { temporizador = new System.Timers.Timer(intervalo); // se reinicia el timer temporizador.Enabled = true; } }
La clase installSvr
Esta clase es necesaria para que despleguemos el servicio, cuando lo instalemos irá a consultar esta clase. Para añadirla, solo tenemos que agregar un nuevo ítem de tipo InstallerClass.
Insertamos los Imports/Usings de System.ComponentModel, System.Configuration.Install y System.ServiceProcess, si no los tenemos.
Después, os tenéis que fijar que herede de System.Configuration.Install.Installer, si no haced que herede poniéndole Inherits o «:»
Añadimos 2 propiedades
C#.NET
Private ServiceInstaller serviceInstaller1 Private ServiceProcessInstaller processInstaller
VB.NET
Private serviceInstaller1 As ServiceInstaller Private processInstaller As ServiceProcessInstaller
Y Modificamos el constructor para que quede así, voy comentando en el código:
VB.NET
Public Sub New() MyBase.New() InitializeComponent() Try ' Instanciamos el proceso y el servicio de instalación processInstaller = New ServiceProcessInstaller() serviceInstaller1 = New ServiceInstaller() ' Le ponemos los permisos que tendrá processInstaller.Account = ServiceAccount.NetworkService ' Indicamos si queremos que sera automatico el arranque del servicio ( cuando se inicia Windows) serviceInstaller1.StartType = ServiceStartMode.Automatic ' ServiceName must equal those on ServiceBase derived classes. serviceInstaller1.ServiceName = "Nombre de Nuestro Servicio" serviceInstaller1.Description = "Descripcion del Servicio" ' Se añade el servicio y proceso a la instalación. Installers.Add(serviceInstaller1) Installers.Add(processInstaller) Catch ex As Exception ' Esto nos aparecera al intalarlo, se instala a través del cmd, así que allí nos aparecerá el error Console.Out.WriteLine("ERROR EN LA INSTALACION!!!!" & ex.Message) End Try End Sub
C#.NET
public Installer1() { InitializeComponent(); try { //Instanciamos el proceso y el servicio de instalación processInstaller = new ServiceProcessInstaller(); serviceInstaller1 = new ServiceInstaller(); // Le ponemos los permisos que tendrá processInstaller.Account = ServiceAccount.NetworkService; // Indicamos si queremos que sera automatico el arranque del servicio ( cuando se inicia Windows) serviceInstaller1.StartType = ServiceStartMode.Automatic; // ServiceName must equal those on ServiceBase derived classes. serviceInstaller1.ServiceName = "Nombre de Nuestro Servicio"; serviceInstaller1.Description = "Descripcion del Servicio"; // Se añade el servicio y proceso a la instalación. Installers.Add(serviceInstaller1); Installers.Add(processInstaller); } catch (Exception ex) { // Esto nos aparecera al intalarlo, se instala a través del cmd, así que allí nos aparecerá el error Console.Out.WriteLine("ERROR EN LA INSTALACION!!!!" + ex.Message); throw; } }
Instalar un servicio.
Ahora que lo tenemos creado, solo faltaría que funcionase, para ello tenemos que instalarlo con una herramienta que esta en el propio .NET y el cmd. La herramienta se llama InstallUtil.exe y esta en C:WindowsMicrosoft.NETFrameworkv4.0.30319InstallUtil.exe. En mi caso el proyecto esta con el framework 4 y por eso esta en el directorio v4. Una vez localizado el exe, lo copiamos y pegamos en el directorio donde esta el servicio, recomiendo no compilar en el mismo directorio.
¿Por que copiarlo y pegarlo en este directorio?
[spoiler]Es porque normalmente tendremos ficheros de configuración que estar con el .exe. Si no lo hiciésemos y quisiéramos saber el directorio actual en la instalación, nos daría el directorio del framework y tendríamos que tener allí los ficheros de configuración[/spoiler]
Después nos vamos al cmd y vamos a la ruta donde esta el servicio con el InstallUtil.exe, y escribimos «InstallUtill.exe Servicio.exe» ( Servicio.exe sera el exe que crea el servicio)
Debugg
Si queremos debugar el servicio solo nos haría falta adjuntar el proceso al VS. Para hacerlo solo tendríamos que ir a tools/attach process( si no nos aparece tendremos que añadir el botón a través de agregar o quitar botones, en la parte de debug estará attach process), nos aparece una lista, seleccionamos el nuestro y ya estaría, si está en marcha y hay algún punto de debug saltará automáticamente; si está parado, ejecutamos el proyecto en el VS. Recomiendo tener parado el servicio para debugar e ir nosotros a ejecutarlo.