Aplicación de consola .NET como servicio de Windows

Tengo una aplicación de consola y me gustaría ejecutarla como servicio de Windows. VS2010 tiene una plantilla de proyecto que permite adjuntar proyecto de consola y construir el servicio de Windows. Me gustaría no agregar un proyecto de servicio separado y si es posible integrar el código de servicio en la aplicación de consola para mantener la aplicación de consola como un proyecto que podría ejecutarse como aplicación de consola o como servicio de Windows si se ejecuta, por ejemplo, desde la línea de comando usando conmutadores.

¿Tal vez alguien podría sugerir una biblioteca de clases o un fragmento de código que podría transformar rápida y fácilmente la aplicación c # console al servicio?

Usualmente uso el siguiente techinque para ejecutar la misma aplicación como una aplicación de consola o como un servicio:

public static class Program { #region Nested classes to support running as service public const string ServiceName = "MyService"; public class Service : ServiceBase { public Service() { ServiceName = Program.ServiceName; } protected override void OnStart(string[] args) { Program.Start(args); } protected override void OnStop() { Program.Stop(); } } #endregion static void Main(string[] args) { if (!Environment.UserInteractive) // running as service using (var service = new Service()) ServiceBase.Run(service); else { // running as console app Start(args); Console.WriteLine("Press any key to stop..."); Console.ReadKey(true); Stop(); } } private static void Start(string[] args) { // onstart code here } private static void Stop() { // onstop code here } } 

Environment.UserInteractive es normalmente cierto para la aplicación de consola y falso para un servicio. Técnicamente, es posible ejecutar un servicio en el modo interactivo del usuario, por lo que podría verificar un cambio de línea de comando.

He tenido un gran éxito con TopShelf .

TopShelf es un paquete Nuget diseñado para facilitar la creación de aplicaciones .NET Windows que se pueden ejecutar como aplicaciones de consola o como servicios de Windows. Puede conectar rápidamente eventos como los eventos Start y Stop del servicio, configurar mediante código, por ejemplo, para configurar la cuenta en la que se ejecuta, configurar las dependencias de otros servicios y configurar cómo se recupera de los errores.

Desde la consola del administrador de paquetes (Nuget):

Install-Package Topshelf

Consulte las muestras de código para comenzar.

Ejemplo:

 HostFactory.Run(x => { x.Service(s => { s.ConstructUsing(name=> new TownCrier()); s.WhenStarted(tc => tc.Start()); s.WhenStopped(tc => tc.Stop()); }); x.RunAsLocalSystem(); x.SetDescription("Sample Topshelf Host"); x.SetDisplayName("Stuff"); x.SetServiceName("stuff"); }); 

TopShelf también se ocupa de la instalación del servicio, lo que puede ahorrarle mucho tiempo y elimina el código repetitivo de su solución. Para instalar su .exe como un servicio, simplemente ejecute lo siguiente desde el símbolo del sistema:

 myservice.exe install -servicename "MyService" -displayname "My Service" -description "This is my service." 

No necesita conectar un instalador de servicio y todo eso: TopShelf lo hace todo por usted.

Así que aquí está el tutorial completo:

  1. Crear un nuevo proyecto de aplicación de consola (p. Ej. MyService)
  2. Agregue dos referencias de biblioteca: System.ServiceProcess y System.Configuration.Install
  3. Agregue los tres archivos impresos a continuación
  4. Cree el proyecto y ejecute “InstallUtil.exe c: \ path \ to \ MyService.exe”
  5. Ahora debería ver MyService en la lista de servicios (ejecute services.msc)

* InstallUtil.exe generalmente se puede encontrar aquí: C: \ windows \ Microsoft.NET \ Framework \ v4.0.30319 \ InstallUtil.ex e

Program.cs

 using System; using System.IO; using System.ServiceProcess; namespace MyService { class Program { public const string ServiceName = "MyService"; static void Main(string[] args) { if (Environment.UserInteractive) { // running as console app Start(args); Console.WriteLine("Press any key to stop..."); Console.ReadKey(true); Stop(); } else { // running as service using (var service = new Service()) { ServiceBase.Run(service); } } } public static void Start(string[] args) { File.AppendAllText(@"c:\temp\MyService.txt", String.Format("{0} started{1}", DateTime.Now, Environment.NewLine)); } public static void Stop() { File.AppendAllText(@"c:\temp\MyService.txt", String.Format("{0} stopped{1}", DateTime.Now, Environment.NewLine)); } } } 

MyService.cs

 using System.ServiceProcess; namespace MyService { class Service : ServiceBase { public Service() { ServiceName = Program.ServiceName; } protected override void OnStart(string[] args) { Program.Start(args); } protected override void OnStop() { Program.Stop(); } } } 

MyServiceInstaller.cs

 using System.ComponentModel; using System.Configuration.Install; using System.ServiceProcess; namespace MyService { [RunInstaller(true)] public class MyServiceInstaller : Installer { public MyServiceInstaller() { var spi = new ServiceProcessInstaller(); var si = new ServiceInstaller(); spi.Account = ServiceAccount.LocalSystem; spi.Username = null; spi.Password = null; si.DisplayName = Program.ServiceName; si.ServiceName = Program.ServiceName; si.StartType = ServiceStartMode.Automatic; Installers.Add(spi); Installers.Add(si); } } } 

Puedes usar

 reg add HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run /v ServiceName /d "c:\path\to\service\file\exe" 

Y aparecerá en la lista de servicios. No sé, si eso funciona correctamente sin embargo. Un servicio generalmente tiene que escuchar varios eventos.

Sin embargo, hay varias envolturas de servicios que pueden ejecutar cualquier aplicación como un servicio real. Por ejemplo, Microsofts SrvAny del kit de recursos Win2003

Escuché tu punto de querer que un ensamblaje pare el código repetido, pero sería lo más simple y reducir la repetición del código y facilitar la reutilización de tu código de otras formas en el futuro si … lo rompes en 3 ensamblajes.

  1. Una biblioteca que hace todo el trabajo. Entonces tenga dos proyectos muy, muy delgados / simples:
  2. uno que es la línea de comando
  3. uno que es el servicio de Windows.

Primero incrusto la solución de la aplicación de consola en la solución de servicio de Windows y la referencia.

Luego hago pública la clase de progtwig de la aplicación de consola

 ///  /// Hybrid service/console application ///  public class Program { } 

Luego creo dos funciones dentro de la aplicación de la consola

  ///  /// Used to start as a service ///  public void Start() { Main(); } ///  /// Used to stop the service ///  public void Stop() { if (Application.MessageLoop) Application.Exit(); //windows app else Environment.Exit(1); //console app } 

Luego, dentro del servicio de Windows, instalé el progtwig y llamé a las funciones Start y Stop agregadas dentro de OnStart y OnStop. Vea abajo

 class WinService : ServiceBase { readonly Program _application = new Program(); ///  /// The main entry point for the application. ///  static void Main() { ServiceBase[] servicesToRun = { new WinService() }; Run(servicesToRun); } ///  /// Set things in motion so your service can do its work. ///  protected override void OnStart(string[] args) { Thread thread = new Thread(() => _application.Start()); thread.Start(); } ///  /// Stop this service. ///  protected override void OnStop() { Thread thread = new Thread(() => _application.Stop()); thread.Start(); } } 

Este enfoque también se puede usar para una aplicación de Windows / híbrido de servicio de Windows

Tal vez deba definir lo que necesita, hasta donde yo sé, no puede ejecutar su aplicación como consola o servicio con línea de comando, al mismo tiempo. Recuerde que el servicio está instalado y debe iniciarlo en el Administrador de Servicios, puede crear una nueva aplicación que inicie el servicio o inicie un nuevo proceso ejecutando su aplicación de consola. Pero como escribiste

“mantener la aplicación de consola como un solo proyecto”

Una vez, estaba en su posición, convirtiendo una aplicación de consola en un servicio. Primero necesita la plantilla, en caso de que esté trabajando con VS Express Edition. Aquí hay un enlace donde puede tener sus primeros pasos: C # Windows Service , esto fue muy útil para mí. Luego, usando esa plantilla, agregue su código a los eventos deseados del servicio.

Para mejorar su servicio, hay otra cosa que puede hacer, pero esto no es rápido y / o fácil, está usando appdomains y está creando dlls para cargar / descargar. En uno puede comenzar un nuevo proceso con la aplicación de la consola, y en otro dll puede simplemente poner la funcionalidad que el servicio tiene que hacer.

Buena suerte.

Necesita separar la funcionalidad en una clase o clases y lanzarla a través de uno de los dos stubs. El talón de la consola o el talón de servicio.

Como es fácil de ver, cuando se ejecutan ventanas, la miríada de servicios que componen la infraestructura no presenta (y no puede) directamente las ventanas de la consola al usuario. El servicio necesita comunicarse con el usuario de una manera no gráfica: a través del SCM; en el registro de eventos, en algún archivo de registro, etc. El servicio también deberá comunicarse con Windows a través del SCM; de lo contrario, se cerrará.

Obviamente, sería aceptable tener alguna aplicación de consola que pueda comunicarse con el servicio, pero el servicio debe ejecutarse de manera independiente sin un requisito de interacción con la GUI.

El código auxiliar de la consola puede ser muy útil para depurar el comportamiento del servicio, pero no debe usarse en un entorno “producido” que, después de todo, es el propósito de crear un servicio.

No lo he leído completamente pero este artículo parece estar en la dirección correcta.