Explicamos paso a paso cómo crear un servicio Windows funcional y completo usando Microsoft Visual Studio .Net 2019, con el lenguaje C#.

Requisitos para crear aplicación de servicio Windows con .Net C#

Necesitaremos disponer de un equipo con Windows y con el IDE de desarrollo Microsoft Visual Studio .Net 2019, servirá su versión gratuita community. En el siguiente enlace explicamos cómo descargarlo e instalarlo, aunque es una versión inferior el proceso será el mismo para la 2019:

Para las pruebas instalaremos un servicio en el equipo Windows, por lo que necesitaremos un usuario con permisos suficientes para realizar esta tarea.

¿Servicio o aplicación de consola?

En función del uso que queramos dar a la aplicación emplearemos la modalidad de aplicación de consola de la línea de comandos (aplicación sin modo gráfico) o bien un servicio de Windows. La diferencia es sencilla, el servicio de Windows es una aplicación que está ejecutándose continuamente, salvo que se detenga el servicio expresamente. Y, además, las aplicaciones de servicio no presentan consola ni ventanas de resultados o interacción con el usuario. Se ejecutan en segundo plano, de forma oculta al usuario. Únicamente se podrá ver el proceso en ejecución en el administrador de tareas de Windows. Estos servicios se iniciarán de forma automática cada vez que arranquemos el equipo.

En cambio, una aplicación de consola permite la interacción con el usuario. Puede mostrar una ventana de la línea de comandos (MS-DOS) y mostrar determinados resultados o solicitar introducción de datos al usuario. El usuario puede cerrar la aplicación cerrando la ventana de MS-DOS, por ejemplo. Estas aplicaciones no se inician de forma automática, hay que iniciarlas manualmente ejecutando el fichero .exe correspondiente.

Crear proyecto en Visual Studio .Net 2019 con C# para servicio Windows

En primer lugar abriremos Visual Studio .Net y elegiremos «Crear un proyecto»:

Crear proyecto en Visual Studio .Net 2019 con C# para servicio Windows

Filtramos por lenguaje «C#», por plataforma «Windows» y por tipo de proyecto «Servicio» y elegimos «Servicio de Windows (.NET Framework):

Crear proyecto en Visual Studio .Net 2019 con C# para servicio Windows

Introducimos el nombre para el proyecto y elegimos la carpeta donde se creará la solución. Dejaremos el Framework por defecto o bien lo cambiaremos por la versión que queramos usar y pulsaremos «Crear»:

Crear proyecto en Visual Studio .Net 2019 con C# para servicio Windows

Nos mostrará el siguiente mensaje:

Para agregar componentes a la clase, arrástrelos desde el cuadro de herramientas y use la ventana Propiedades para establecer sus propiedades. Si desea crear métodos y eventos para la clase, cambie a la vista de código.

Crear proyecto en Visual Studio .Net 2019 con C# para servicio Windows

Nos está diciendo que en este contenedor podemos colocar componentes del cuadro de herramientas, si los necesitamos. Y si queremos establecer código para el servicio deberemos pulsar en «cambie a la vista de código» o bien pulsar con el botón derecho del ratón sobre «Service1.cs» y elegir «Ver código»:

Crear proyecto en Visual Studio .Net 2019 con C# para servicio Windows

Nos mostrará el código fuente del servicio por defecto, que es este:

En el método OnStart introduciremos el código que se ejecutará cuando el servicio se inicie. Lo habitual es usar un temporizador (Timer) para que las acciones que queramos que haga el servicio se ejecuten cada cierto tiempo.

En el método OnStop podremos añadir el código que queramos que se ejecute cuando se detenga el servicio: cerrar conexiones con ficheros o bases de datos, escribir en log, …

Es conveniente establecer el nombre para el servicio, en nuestro caso será un servicio que escriba en Telegram, un Bot de Telegram, por lo que lo llamaremos «CB_Bot_Respuesta». Para establecer el nombre pulsaremos con el botón derecho del ratón sobre «Service1.cs» y elegiremos «Cambiar nombre»:

Crear proyecto en Visual Studio .Net 2019 con C# para servicio Windows

Introduciremos el nombre que queramos para el servicio y pulsaremos INTRO. Nos avisará de que vamos a cambiar el nombre de un archivo y si queremos cambiar todas las referencias a «Service1» del proyecto. Pulsaremos «Sí»:

Seguiremos con el establecimiento del nombre del servicio. Pulsaremos con el botón derecho del ratón sobre «CB_Bot_Respuesa.cs» (en vuestro caso sobre el nombre que le hayáis dado al fichero en el paso anterior) y elegiremos «Ver diseñador»:

Crear proyecto en Visual Studio .Net 2019 con C# para servicio Windows

Sobre la pestaña de diseño pulsaremos con el botón derecho del ratón y elegiremos «Propiedades»:

Crear proyecto en Visual Studio .Net 2019 con C# para servicio Windows

En la propiedad «ServiceName» introduciremos el nombre del servicio. Este nombre es importante porque será el que aparezca en los servicios de Windows:

Crear proyecto en Visual Studio .Net 2019 con C# para servicio Windows

A continuación mostraremos un ejemplo de una aplicación completa de servicio de Windows que usa estos eventos para su funcionamiento.

Monitorizar inicio y parada del servicio con los eventos del sistema

A continuación estableceremos, al menos, un mecanismo simple para registrar el estado del servicio. Usaremos EventLog, un componente de Visual Studio que nos permitirá interactuar con los eventos del sistema, que son consultables desde el Visor de Eventos de Windows:

Monitorizar inicio y parada del servicio con los eventos del sistema

Para ello accederemos al diseñador, como en el caso anterior, pulsando en «CB_Bot_Respuesta.cs» (o el nombre que le hayamos dado) con el botón derecho y eligiendo «Ver diseñador»:

Monitorizar inicio y parada del servicio con los eventos del sistema

Desde el diseñador podremos añadir componentes al servicio, en nuestro caso añadiremos «EventLog«, arrastrándolo a la pestaña de diseño:

Monitorizar inicio y parada del servicio con los eventos del sistema

Estableceremos sus propiedades básicas, al menos el nombre (Name), que en nuestro caso será «eventosSistema» y Log donde indicaremos en qué rama del Visor de Eventos se escribirá, en nuestro caso «Application», aunque esto último se lo estableceremos por código:

Monitorizar inicio y parada del servicio con los eventos del sistema

Para inicializar el EventLog y escribir eventos escribiremos el siguiente código en la clase del servicio y en los eventos OnStop y OnStart:

Monitorizar inicio y parada del servicio con los eventos del sistema

El código fuente completo para guardar en el visor de sucesos:

Añadir código fuente C# al servicio para su funcionamiento

Una vez creado el proyecto procederemos a «darle vida», a implementar lo que queramos que realice el servicio de Windows. En nuestro caso será un servicio que contesta en determinadas conversaciones de Telegram (un Bot de Telegram)….

Agregar clase instaladora en proyecto Visual Studio .Net para servicio Windows

Para poder probar el servicio desarrollado habrá que instalarlo en el sistema. Pero antes debemos crear la clase instaladora en Visual Studio .Net. Para ello accederemos al la vista de diseño de nuestro proyecto y pulsaremos el botón derecho del ratón, seleccionaremos «Agregar instalador»:

Agregar clase instaladora en proyecto Visual Studio .Net para servicio Windows

Nos agregará un componente de tipo ServiceProcessInstaller, con el nombre serviceProccessInstaller1. En sus propiedades podremos establecer algunas opciones para el servicio, por ejemplo el tipo de cuenta con la que se iniciará (propiedad Account):

  • LocalService: cuenta que actúa como usuario sin privilegios en el equipo local y presenta credenciales anónimas a cualquier servidor remoto.
  • NetworkService: cuenta que proporciona amplios privilegios locales y presenta credenciales del equipo a cualquier servidor remoto.
  • LocalSystem: una cuenta, utilizada por el administrador de control de servicio, que tiene amplios privilegios en el equipo local y funciona como un equipo de la red.
  • User: cuenta definida por un usuario específico en la red. Si se especifica User para el miembro ServiceProcessInstaller.Account, el sistema pide un nombre de usuario y una contraseña al instalar el servicio, a menos que se establezcan valores para las propiedades Username y Password de la instancia de ServiceProcessInstaller.

Y nos habrá creado también el componente ServiceInstaller, con el nombre serviceInstaller1. Editaremos las propiedades de este componente, que son importantes para establecer la descripción, el nombre para mostrar y el tipo de inicio del servicio, así como su nombre:

  • Description: la descripción de lo que hace el servicio, aparecerá en el listado de servicios de Windows, en la columna «Descripción».
  • DisplayName: nombre del servicio que aparecerá en la columna «Nombre» de los servicios de Windows.
  • Parent: debe tener como padre ProjectInstaller.
  • ServiceName: nombre interno del servicio, debe coincidir con el nombre que le hemos dado al crear el proyecto, en nuestro caso CB_Bot_Respuesta.
  • StartType: tipo de inicio del servicio, las opciones posibles son: Boot, System, Automatic, Manual, Disabled.

Para un mayor control añadiremos también un componente no visual, desde el Cuadro de herramientas, ServiceController:

El componente ServiceController también tiene una serie de propiedades que podemos establecer, como el nombre del servicio:

Instalar y depurar el servicio desde Visual Studio .Net

Previamente a la instalación, como es lógico, generaremos el fichero ejecutable del servicio. Para ello compilaremos la solución pulsando en «Compilar» – «Compilar solución»:

Instalar y depurar el servicio desde Visual Studio .Net

Una aplicación de servicio se comporta de forma diferente a una aplicación de escritorio normal. En el caso de la depuración y la ejecución también es diferente. Un servicio debe estar instalado en el sistema para poderse ejecutar. Podremos instalarlo de varias formas, una de ellas es mediante la línea de comandos de Visual Studio .Net. Desde el menú inicio de Windows, buscaremos «Developer Command Prompt for VS 2019», es muy importante ejecutarlo como administradores, de lo contrario puede que no tengamos permisos para instalar el servicio. Pulsaremos con el botón derecho del ratón sobre «Developer Command Prompt for VS 2019» y elegiremos «Más» – «Ejecutar como administrador»:

Instalar y depurar el servicio desde Visual Studio .Net

Accederemos a la carpeta donde se encuentre el ejecutable del servicio (que previamente habremos compilado desde Visual Studio .Net), podemos hacerlo con el comando cd, por ejemplo:

cd «C:\Users\usuario\Documents\Visual Studio 2019\Proyectos\CB_Bot_Respuesta\bin\Debug»

Y ejecutaremos el siguiente comando, escribiendo el nombre del ejecutable de nuestro servicio, en nuestro caso CB_Bot_Respuesta.exe:

installutil CB_Bot_Respuesta.exe

Instalar y depurar el servicio desde Visual Studio .Net

Dependiendo del tipo de validación que hayamos elegido, al instalar el servicio puede que nos pida las credenciales de inicio de sesión del servicio:

Instalar y depurar el servicio desde Visual Studio .Net

Si nos muestra el error:

System.ComponentModel.Win32Exception: Acceso denegado

Puede ser debido a que no hemos iniciado la consola de Visual Studio como administrador.

Si el comando se ha ejecutado correctamente el servio habrá quedado instalado en los servicios de Windows. Lo buscaremos en la lista y pulsaremos con el botón derecho sobre él, eligiendo «Propiedades», para comprobar que los datos son correctos:

Instalar y depurar el servicio desde Visual Studio .Net

En la ventana de propiedades podremos comprobar que se han aplicado todas las opciones que hemos definido para el servicio en Visual Studio:

Instalar y depurar el servicio desde Visual Studio .Net

Podremos iniciarlo para comprobar que funciona correctamente:

Instalar y depurar el servicio desde Visual Studio .Net

Y puesto que hemos monitorizado el inicio y parada del servicio, en el Visor de eventos de Windows, en la rama «Aplicación» podremos ver el resultado:

Instalar y depurar el servicio desde Visual Studio .Net

Desinstalar el servicio en Windows

Para desinstalar un servicio Windows podremos emplear el siguiente comando:

Cómo monitorizar la ejecución de un servicio

Dado que este tipo de aplicaciones no muestra ventanas de resultados e interacción con el usuario, la única forma de saber si está ejecutándose correctamente es mediante la escritura de cada acción en un fichero de log, el el monitor de eventos del sistema o bien en una base de datos.

Por lo tanto el desarrollador de un servicio debe contemplar esta opción y monitorizar las acciones que realice y su resultado en un fichero de log, en el monitor de eventos del sistema o bien en una base de datos. Debe utilizar algún mecanismo de este tipo, que se incluirá en el código del servicio, para que se pueda consultar su estado y correcta ejecución.

Un ejemplo de clase que escribe un texto en un fichero de log (de texto plano):

Un ejemplo de uso de la clase anterior, cuando queramos guardar en el fichero de log algún evento importante de nuestro servicio:

Error de permisos si no se han establecido adecuadamente

Si se produce este error al intentar monitorizar el resultado de la ejecución del servicio mediante el EventLog (Monitor de Eventos del Sistema):

System.Security.SecurityException: ‘No se encontró el origen, pero no se pudo buscar en algunos o todos los registros de eventos. Registros inaccesibles: Security.’

Error de permisos si no se han establecido adecuadamente

Significará que el servicio no se ha ejecutado con los privilegios suficientes. En ese caso tenemos varias opciones:

  • Usar impersonation para que el servicio se ejecute con los máximos privilegios. Ese método es interesante porque puede aplicarse la impersonation en una parte del código, de forma que esa parte se ejecutará con un usuario (y contraseña) con privilegios suficientes.
  • Ejecutar el servicio con un usuario administrador y establecer este usuario para el arranque del servicio.
  • Configurar el fichero manifest de la aplicación para indicar que debe ejecutarse con privilegios elevados. En el siguiente artículo explicamos cómo hacerlo:

Anexo

Resultado de la ejecución del comando installutil CB_Bot_Respuesta.exe

** Visual Studio 2019 Developer Command Prompt v16.2.5
** Copyright (c) 2019 Microsoft Corporation
C:\Windows\System32>cd «C:\Users\usuario\Documents\Visual Studio 2019\Proyectos\CB_Bot_Respuesta\bin\Debug»
C:\Users\usuario\Documents\Visual Studio 2019\Proyectos\CB_Bot_Respuesta\bin\Debug>installutil CB_Bot_Respuesta.exe
Utilidad de instalación de Microsoft (R) .NET Framework, versión 4.8.3761.0
Copyright (C) Microsoft Corporation. Todos los derechos reservados.
Ejecutando una instalación de transacción.
Iniciando la fase de instalación dentro de la instalación.
Consulte el contenido del archivo de registro sobre el progreso del ensamblado C:\Users\usuario\Documents\Visual Studio 2019\Proyectos\CB_Bot_Respuesta\bin\Debug\CB_Bot_Respuesta.exe.
El archivo está ubicado en C:\Users\usuario\Documents\Visual Studio 2019\Proyectos\CB_Bot_Respuesta\bin\Debug\CB_Bot_Respuesta.InstallLog.
Instalando ensamblado ‘C:\Users\usuario\Documents\Visual Studio 2019\Proyectos\CB_Bot_Respuesta\bin\Debug\CB_Bot_Respuesta.exe’.
Los parámetros afectados son:
logtoconsole =
logfile = C:\Users\usuario\Documents\Visual Studio 2019\Proyectos\CB_Bot_Respuesta\bin\Debug\CB_Bot_Respuesta.InstallLog
assemblypath = C:\Users\usuario\Documents\Visual Studio 2019\Proyectos\CB_Bot_Respuesta\bin\Debug\CB_Bot_Respuesta.exe
Instalando el servicio CB_Bot_Respuesta…
El servicio CB_Bot_Respuesta se ha instalado correctamente.
Creando el origen de EventLog CB_Bot_Respuesta en el registro Application…
La fase de instalación finalizó correctamente y la fase de confirmación está empezando.
Consulte el contenido del archivo de registro sobre el progreso del ensamblado C:\Users\usuario\Documents\Visual Studio 2019\Proyectos\CB_Bot_Respuesta\bin\Debug\CB_Bot_Respuesta.exe.
El archivo está ubicado en C:\Users\usuario\Documents\Visual Studio 2019\Proyectos\CB_Bot_Respuesta\bin\Debug\CB_Bot_Respuesta.InstallLog.
Confirmando ensamblado ‘C:\Users\usuario\Documents\Visual Studio 2019\Proyectos\CB_Bot_Respuesta\bin\Debug\CB_Bot_Respuesta.exe’.
Los parámetros afectados son:
logtoconsole =
logfile = C:\Users\usuario\Documents\Visual Studio 2019\Proyectos\CB_Bot_Respuesta\bin\Debug\CB_Bot_Respuesta.InstallLog
assemblypath = C:\Users\usuario\Documents\Visual Studio 2019\Proyectos\CB_Bot_Respuesta\bin\Debug\CB_Bot_Respuesta.exe
La fase de confirmación finalizó correctamente.
La instalación con transacciones ha finalizado.

Error al instalar servicio

Un error habitual al instalar un servicio puede ser el siguiente:

No se pudieron encontrar instaladores públicos con el atributo RunInstallerAttribute.Yes en el ensamblado ????.exe

Suele ser debido a que no se ha creado la clase instaladora en la aplicación de Visual Studio .Net. Explicamos cómo hacerlo en un paso anterior.