Walkthrough: Creating a Windows Service Application in the Component Designer

 

En este artículo se demuestra cómo crear una sencilla aplicación de servicio de Windows en Visual Studio que escribe mensajes en un registro de eventos. A continuación se incluyen los pasos básicos que se realizan para crear y usar el servicio:

  1. Crear un servicio mediante la plantilla del proyecto Servicio de Windows y configurarlo. Esta plantilla crea una clase que hereda de System.ServiceProcess.ServiceBase y escribe gran parte del código básico del servicio, como el código que lo inicia.

  2. Agregar características al servicio para los procedimientos OnStart y OnStop, y reemplazar los otros métodos que quiera redefinir.

  3. Configurar el estado del servicio. De manera predeterminada, los servicios creados con System.ServiceProcess.ServiceBase implementan solo un subconjunto de las marcas de estado disponibles. Si el servicio tarda mucho tiempo en iniciarse, pausarse o detenerse, puede implementar valores de estado como Inicio pendiente o Finalización pendiente para indicar que está trabajando en una operación.

  4. Agregar instaladores al servicio para la aplicación de servicio.

  5. (Opcional) Establecer parámetros de inicio, especificar los argumentos de inicio predeterminados y permitir a los usuarios reemplazar la configuración predeterminada al iniciar el servicio manualmente.

  6. Compilar el servicio.

  7. Instalar el servicio en el equipo local.

  8. Obtener acceso al Administrador de control de servicios de Windows e Iniciar y ejecutar el servicio.

  9. Desinstalar un servicio de Windows.

System_CAPS_ICON_warning.jpg Advertencia

La plantilla de proyecto Servicios de Windows que se requiere para este tutorial no está disponible en la edición Express de Visual Studio.

System_CAPS_ICON_note.jpg Nota

Es posible que tu equipo muestre nombres o ubicaciones diferentes para algunos de los elementos de la interfaz de usuario de Visual Studio en las siguientes instrucciones. La edición de Visual Studio que se tenga y la configuración que se utilice determinan estos elementos. Para obtener más información, consulta Personalizar el IDE.

Para empezar, debe crear el proyecto y definir los valores necesarios para que el servicio funcione correctamente.

Para crear y configurar el servicio

  1. En Visual Studio, en la barra de menús, elija Archivo, Nuevo, Proyecto.

    Aparece el cuadro de diálogo Nuevo proyecto.

  2. Seleccione Servicio de Windows en la lista de plantillas de proyecto de Visual Basic o Visual C# y asigne al proyecto el nombre MyNewService. Elija Aceptar.

    La plantilla de proyecto agrega automáticamente una clase de componente, denominada Service1, que hereda de System.ServiceProcess.ServiceBase.

  3. En el menú Editar, elija Buscar y reemplazar, Buscar en archivos (teclado: Ctrl+Mayús+F). Cambie todas las apariciones de Service1 a MyNewService. Encontrará casos en Service1.cs, Program.cs y Service1.Designer.cs (o sus equivalentes .vb).

  4. En la ventana Propiedades para Service1.cs [Diseño] o Service1.vb [Diseño], establezca ServiceName y la propiedad (Name) para Service1 en MyNewService, si todavía no están establecidos.

  5. En el Explorador de soluciones, cambie el nombre de Service1.cs a MyNewService.cs o de Service1.vb a MyNewService.vb.

En esta sección, agregará un registro de eventos personalizado al servicio de Windows. Los registros de eventos no están asociados de ningún modo a los servicios de Windows. Aquí, el componente EventLog se utiliza como ejemplo del tipo de componente que se puede agregar a un servicio de Windows.

Para agregar la funcionalidad de registro de eventos personalizado al servicio

  1. En el Explorador de soluciones, abra el menú contextual de MyNewService.cs o MyNewService.vb y, a continuación, elija Diseñador de vistas.

  2. En la sección Componentes del Cuadro de herramientas, arrastre un componente EventLog hasta el diseñador.

  3. En el Explorador de soluciones, abra el menú contextual de MyNewService.cs o MyNewService.vb y, a continuación, elija Ver código.

  4. Agregue una declaración para el objeto eventLog en la clase MyNewService, después de la línea que declara la variable components:

      private System.ComponentModel.IContainer components;
      private System.Diagnostics.EventLog eventLog1;
    

  5. Agregue o edite el constructor para definir un registro de eventos personalizado:

    	public MyNewService()
    	{
    		InitializeComponent();
                    eventLog1 = new System.Diagnostics.EventLog();
    		if (!System.Diagnostics.EventLog.SourceExists("MySource")) 
    		{         
    				System.Diagnostics.EventLog.CreateEventSource(
    					"MySource","MyNewLog");
    		}
    		eventLog1.Source = "MySource";
    		eventLog1.Log = "MyNewLog";
    	}
    

Para definir qué ocurre al iniciar el servicio

  • En el Editor de código, busque el método OnStart que se reemplazó automáticamente al crear el proyecto y reemplace el código con lo siguiente. Agrega una entrada al registro de eventos cuando el servicio comienza a ejecutarse:

    	protected override void OnStart(string[] args)
    	{
    		eventLog1.WriteEntry("In OnStart");
    	}
    

    Una aplicación de servicio está diseñada para ejecutarse a largo plazo, por lo que suele sondear o supervisar algún elemento del sistema. La supervisión se puede establecer en el método OnStart. Sin embargo, OnStart no lleva a cabo la supervisión. El método OnStart debe volver al sistema operativo después de que haya comenzado el funcionamiento del servicio. No debe bloquearse ni ejecutar un bucle infinito. Para establecer un mecanismo de sondeo sencillo, puede usar el componente System.Timers.Timer de la siguiente manera: en el método OnStart, establezca los parámetros en el componente y después establezca la propiedad Enabled en true. El temporizador activa eventos periódicamente en el código y, en esos instantes, el servicio podría realizar su control. Para ello puede usar el código siguiente:

    // Set up a timer to trigger every minute.  
    System.Timers.Timer timer = new System.Timers.Timer();  
    timer.Interval = 60000; // 60 seconds  
    timer.Elapsed += new System.Timers.ElapsedEventHandler(this.OnTimer);  
    timer.Start();  
    
    

    Agregue código para controlar el evento de temporizador:

    public void OnTimer(object sender, System.Timers.ElapsedEventArgs args)  
    {  
        // TODO: Insert monitoring activities here.  
        eventLog1.WriteEntry("Monitoring the System", EventLogEntryType.Information, eventId++);  
    }  
    
    

    Es posible que desee realizar tareas mediante el uso de subprocesos de trabajo en segundo plano en lugar de ejecutar todo el trabajo en el subproceso principal. Para obtener un ejemplo de esto, vea la página de referencia de System.ServiceProcess.ServiceBase.

Para definir qué debe ocurrir al detener el servicio

  • Reemplace el código del método OnStop por el código siguiente: Agrega una entrada al registro de eventos cuando el servicio se detiene:

    	protected override void OnStop()
    	{
    		eventLog1.WriteEntry("In onStop.");
    	}
    

En la siguiente sección, puede reemplazar los métodos OnPause, OnContinue, y OnShutdown para definir procesamiento adicional para el componente.

Para definir otras acciones para el servicio

  • Localice el método que desee controlar y reemplácelo para definir lo que desea que suceda.

    En el siguiente código se muestra cómo reemplazar el método OnContinue:

    	protected override void OnContinue()
    	{
    		eventLog1.WriteEntry("In OnContinue.");
    	}  
    

Deben realizarse algunas acciones personalizadas cuando la clase Installer instala un servicio de Windows. Visual Studio puede crear estos instaladores específicamente para un servicio de Windows y agregarlos al proyecto.

Los servicios informan de su estado al Administrador de control de servicios, para que los usuarios puedan saber si un servicio funciona correctamente. De manera predeterminada, los servicios que se heredan de ServiceBase informan de un conjunto limitado de parámetros de estado, incluidos Detenido, En pausa y En ejecución. Si un servicio tarda un poco en iniciarse, puede ser útil informar de un estado Inicio pendiente. También puede implementar la configuración de estado Inicio pendiente y Detención pendiente si agrega código que llama a la función SetServiceStatus de Windows.

Para implementar el estado pendiente del servicio

  1. Agregue una instrucción using o una declaración Imports al espacio de nombres System.Runtime.InteropServices en el archivo MyNewService.cs o MyNewService.vb:

    using System.Runtime.InteropServices;  
    
    
  2. Agregue el código siguiente a MyNewService.cs para declarar los valores de ServiceState y agregue una estructura para el estado, que usará en una llamada de invocación de plataforma:

    public enum ServiceState  
      {  
          SERVICE_STOPPED = 0x00000001,  
          SERVICE_START_PENDING = 0x00000002,  
          SERVICE_STOP_PENDING = 0x00000003,  
          SERVICE_RUNNING = 0x00000004,  
          SERVICE_CONTINUE_PENDING = 0x00000005,  
          SERVICE_PAUSE_PENDING = 0x00000006,  
          SERVICE_PAUSED = 0x00000007,  
      }  
    
      [StructLayout(LayoutKind.Sequential)]  
      public struct ServiceStatus  
      {  
          public long dwServiceType;  
          public ServiceState dwCurrentState;  
          public long dwControlsAccepted;  
          public long dwWin32ExitCode;  
          public long dwServiceSpecificExitCode;  
          public long dwCheckPoint;  
          public long dwWaitHint;  
      };  
    
    
  3. Ahora, en la clase MyNewService, declare la función SetServiceStatus con la plataforma de invocación:

    [DllImport("advapi32.dll", SetLastError=true)]  
            private static extern bool SetServiceStatus(IntPtr handle, ref ServiceStatus serviceStatus);  
    
    
    
  4. Para implementar el estado Inicio pendiente, agregue el código siguiente al principio del método OnStart:

    // Update the service state to Start Pending.  
    ServiceStatus serviceStatus = new ServiceStatus();  
    serviceStatus.dwCurrentState = ServiceState.SERVICE_START_PENDING;  
    serviceStatus.dwWaitHint = 100000;  
    SetServiceStatus(this.ServiceHandle, ref serviceStatus);  
    
    
  5. Agregue código para establecer el estado en En ejecución al final del método OnStart.

    // Update the service state to Running.  
    serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING;  
    SetServiceStatus(this.ServiceHandle, ref serviceStatus);  
    
    
    ' Update the service state to Running.  
    serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING  
    SetServiceStatus(Me.ServiceHandle, serviceStatus)  
    
    
  6. (Opcional) Repita este procedimiento para el método OnStop.

System_CAPS_ICON_caution.jpg Precaución

El Administrador de control de servicios usa los miembros dwWaitHint y dwCheckpoint de la estructura SERVICE_STATUS para determinar durante cuánto tiempo hay que esperar a que un servicio de Windows se inicie o apague. Si los métodos OnStart y OnStop se ejecutan durante mucho tiempo, el servicio puede solicitar más tiempo por medio de una llamada a SetServiceStatus de nuevo con un valor de dwCheckPoint incrementado.

Para poder ejecutar un servicio de Windows, antes debe instalarlo, lo que lo registra con el Administrador de control de servicios. Puede agregar instaladores al proyecto que controlen los detalles del registro.

Para crear los instaladores necesarios para el servicio

  1. En el Explorador de soluciones, abra el menú contextual de MyNewService.cs o MyNewService.vb y, a continuación, elija Diseñador de vistas.

  2. Haga clic en el fondo del diseñador para seleccionar el propio servicio, en vez de cualquier elemento de su contenido.

  3. Abra el menú contextual de la ventana del diseñador (si usa un dispositivo señalador, haga clic con el botón secundario dentro de la ventana) y, a continuación, elija Agregar instalador.

    De forma predeterminada, se agrega al proyecto una clase de componente que contiene dos instaladores. El componente se denomina ProjectInstaller, y los instaladores que contiene son el instalador para el servicio y el instalador para el proceso asociado al servicio.

  4. En la vista Diseño de ProjectInstaller, elija serviceInstaller1 para un proyecto de Visual C# o ServiceInstaller1 para un proyecto de Visual Basic.

  5. En la ventana Propiedades, asegúrese de que la propiedad ServiceName esté establecida en MyNewService.

  6. Establezca texto en la propiedad Descripción, como "Servicio de ejemplo". Este texto aparece en la ventana Servicios y ayuda al usuario a identificar el servicio y comprender para qué se usa.

  7. Establezca la propiedad DisplayName en el texto que desea que aparezca en la ventana Servicios en la columna Nombre. Por ejemplo, puede escribir "Nombre para mostrar de MyNewService". Este nombre puede ser diferente de la propiedad ServiceName, que es el nombre usado por el sistema (por ejemplo, al usar el comando net start para iniciar el servicio).

  8. Establezca la propiedad StartType en Automatic.

    Propiedades del instalador de un servicio de Windows

  9. En el diseñador, elija serviceProcessInstaller1 para un proyecto de Visual C# o ServiceProcessInstaller1 para un proyecto de Visual Basic. Establezca la propiedad Account en LocalSystem. Esto hará que se instale el servicio y se ejecute con una cuenta de servicio local.

    System_CAPS_ICON_important.jpg Importante

    La cuenta LocalSystem tiene amplios permisos, incluida la capacidad para escribir en el registro de eventos. Utilice esta cuenta con precaución porque podría aumentar el riesgo de ataques por parte de software malintencionado. Para otras tareas, considere la posibilidad de usar la cuenta LocalService, que actúa como un usuario sin privilegios en el equipo local y presenta credenciales anónimas a cualquier servidor remoto. En este ejemplo se produce un error si intenta usar la cuenta LocalService, ya que necesita permiso de escritura en el registro de eventos.

    Para obtener más información acerca de los instaladores, vea How to: Add Installers to Your Service Application.

Un servicio de Windows, al igual que cualquier otro archivo ejecutable, puede aceptar argumentos de línea de comandos o parámetros de inicio. Cuando se agrega código para procesar los parámetros de inicio, los usuarios pueden iniciar el servicio con sus propios parámetros de inicio personalizados mediante la ventana Servicios del Panel de control de Windows. Sin embargo, estos parámetros de inicio no se conservan la próxima vez que se inicia el servicio. Para establecer los parámetros de inicio de manera permanente, puede establecerlos en el Registro, como se muestra en este procedimiento.

System_CAPS_ICON_note.jpg Nota

Antes de decidirse a agregar parámetros de inicio, piense en si es la mejor manera de pasar información al servicio. Aunque los parámetros de inicio son fáciles de usar y analizar, y los usuarios los pueden reemplazar fácilmente, pueden ser más difíciles de descubrir y usar para los usuarios sin documentación. Por lo general, si el servicio requiere más de unos pocos parámetros de inicio, debe considerar el uso del Registro o un archivo de configuración en su lugar. Todos los servicios de Windows tienen una entrada en el Registro en HKLM\System\CurrentControlSet\services. En la clave del servicio, se puede usar la subclave Parameters para almacenar la información a la que puede tener acceso su servicio. Puede usar archivos de configuración de aplicación para un servicio de Windows del mismo modo que lo hace para otros tipos de programas. Para obtener código de ejemplo, vea AppSettings.

Agregar parámetros de inicio

  1. En el método Main de Program.cs o MyNewService.Designer.vb, agregue un argumento para la línea de comandos:

    static void Main(string[] args)        {            ServiceBase[] ServicesToRun;            ServicesToRun = new ServiceBase[]             {                 new MyNewService(args)             };            ServiceBase.Run(ServicesToRun);        }  
    
    
  2. Cambie el constructor MyNewService de la siguiente forma:

    public MyNewService(string[] args)        {            InitializeComponent();            string eventSourceName = "MySource";            string logName = "MyNewLog";            if (args.Count() > 0)            {                eventSourceName = args[0];            }            if (args.Count() > 1)            {                logName = args[1];            }            eventLog1 = new System.Diagnostics.EventLog();            if (!System.Diagnostics.EventLog.SourceExists(eventSourceName))            {                System.Diagnostics.EventLog.CreateEventSource(                    eventSourceName, logName);            }            eventLog1.Source = eventSourceName;            eventLog1.Log = logName;        }  
    
    

    Este código establece el nombre de registro y el origen de eventos según los parámetros de inicio proporcionados, o bien, usa los valores predeterminados si no se proporcionan argumentos.

  3. Para especificar los argumentos de línea de comandos, agregue el código siguiente a la clase ProjectInstaller en ProjectInstaller.cs o ProjectInstaller.vb:

    protected override void OnBeforeInstall(IDictionary savedState)        {            string parameter = "MySource1\" \"MyLogFile1";            Context.Parameters["assemblypath"] = "\"" + Context.Parameters["assemblypath"] + "\" \"" + parameter + "\"";            base.OnBeforeInstall(savedState);        }  
    
    

    Este código modifica la clave del Registro ImagePath, que suele contener la ruta de acceso completa al archivo ejecutable para el servicio de Windows. Para ello, agrega los valores de parámetros predeterminados. Las comillas alrededor de la ruta de acceso (y alrededor de cada parámetro individual) son obligatorias para que el servicio se inicie correctamente. Para cambiar los parámetros de inicio para este servicio de Windows, los usuarios pueden cambiar los parámetros proporcionados en la clave del Registro ImagePath, aunque la mejor manera es cambiarlos mediante programación y exponer la funcionalidad a los usuarios de una manera fácil de usar (por ejemplo, en una utilidad de administración o configuración).

Para compilar el proyecto de servicio

  1. En el Explorador de soluciones, abra el menú contextual del proyecto y, a continuación, elija Propiedades. Aparecerán las páginas de propiedades del proyecto.

  2. En la pestaña Aplicación, en la lista Objeto de inicio, elija MyNewService.Program.

  3. En el Explorador de soluciones, abra el menú contextual del proyecto y, a continuación, elija Compilar para compilar el proyecto (teclado: Ctrl+Mayús+B).

Ahora que ha compilado el servicio de Windows, puede instalarlo. Para instalar un servicio de Windows, debe tener credenciales administrativas en el equipo en el que lo va a instalar.

Para instalar un servicio de Windows

  1. En Windows 7 y Windows Server, abra el Símbolo del sistema para desarrolladores en Visual Studio Tools en el menú Inicio. En Windows 8 o Windows 8.1, elija el icono de Visual Studio Tools en la pantalla Inicio y ejecute el Símbolo del sistema para desarrolladores con credenciales administrativas. (Si usa un mouse, haga clic con el botón derecho en Símbolo del sistema para desarrolladores y, a continuación, elija Ejecutar como administrador).

  2. En la ventana de símbolo del sistema, desplácese hasta la carpeta que contiene la salida del proyecto. Por ejemplo, en la carpeta Mis documentos, desplácese hasta Visual Studio 2013\Projects\MyNewService\bin\Debug.

  3. Escriba el comando siguiente:

    installutil.exe MyNewService.exe  
    
    

    Si el servicio se instala correctamente, installutil.exe indicará que la instalación ha sido correcta. Si el sistema no encuentra InstallUtil.exe, asegúrese de que existe en el equipo. Esta herramienta se instala con .NET Framework en la carpeta %WINDIR%\Microsoft.NET\Framework[64]\versión_Framework. Por ejemplo, la ruta de acceso predeterminada para la versión de 32 bits de .NET Framework 4, 4.5, 4.5.1 y 4.5.2 es C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe.

    Si el proceso installutil.exe informa de un error, compruebe el registro de instalación para averiguar por qué. De manera predeterminada, el registro está en la misma carpeta que el ejecutable del servicio. La instalación puede fallar si RunInstallerAttribute Clase no está presente en la clase ProjectInstaller, o si el atributo no está establecido en true o la ProjectInstaller clase no es pública.

    Para obtener más información, consulta How to: Install and Uninstall Services.

Para iniciar y detener el servicio

  1. En Windows, abra la pantalla Inicio o el menú Inicio y escriba services.msc.

    Podrá ver el servicio MyNewService en la lista de la ventana Servicios.

    MyNewService en la ventana Servicios.

  2. En la ventana Servicios, abra el menú contextual del servicio y, a continuación, elija Iniciar.

  3. Abra el menú contextual del servicio y, a continuación, elija Detener.

  4. (Opcional) Desde la línea de comandos, puede usar los comandos net start ServiceName y net stop ServiceName para iniciar y detener el servicio.

Para comprobar el registro de eventos del servicio

  1. En Visual Studio, abra el Explorador de servidores (teclado: Ctrl+Alt+S) y obtenga acceso al nodo Registros de eventos del equipo local.

  2. Localice la lista correspondiente a MyNewLog (o MyLogFile1, si se usa el procedimiento opcional para agregar argumentos de línea de comandos) y expándala. Debería ver las entradas de las dos acciones (iniciar y detener) que ha realizado el servicio.

    Utilice el Visor de eventos para consultar las entradas del registro de eventos.

Para desinstalar el servicio

  1. Abra un Símbolo del sistema para desarrolladores con credenciales administrativas.

  2. En la ventana de símbolo del sistema, desplácese hasta la carpeta que contiene la salida del proyecto. Por ejemplo, en la carpeta Mis documentos, desplácese hasta Visual Studio 2013\Projects\MyNewService\bin\Debug.

  3. Escriba el comando siguiente:

    installutil.exe /u MyNewService.exe  
    
    

    Si el servicio se desinstala correctamente, installutil.exe indicará que el servicio se ha desinstalado correctamente. Para obtener más información, consulta How to: Install and Uninstall Services.

Puede crear un programa de instalación independiente que otros usuarios pueden usar para instalar el servicio de Windows, pero esto requiere pasos adicionales. ClickOnce no admite servicios de Windows, por lo que no puede utilizar el Asistente para publicación. Puede utilizar una versión completa de InstallShield, que Microsoft no proporciona. Para obtener más información acerca de InstallShield, vea InstallShield Limited Edition. También puede usar Conjunto de herramientas de Windows Installer XML para crear un instalador para un servicio de Windows.

Podría probar también el uso de un componente ServiceController para enviar comandos al servicio instalado.

Puede usar un instalador para crear un registro de eventos al instalar la aplicación, en lugar de crearlo cuando se ejecuta la aplicación. Además, el instalador eliminará el registro de eventos cuando se desinstale la aplicación. Para obtener más información, vea la página de referencia de EventLogInstaller.

Windows Service Applications
Introduction to Windows Service Applications
How to: Debug Windows Service Applications
How to: Access and Initialize Server Explorer/Database Explorer
Servicios (Windows)

Mostrar: