¿Cómo depuro los servicios de Windows en Visual Studio?

¿Es posible depurar los servicios de Windows en Visual Studio?

Utilicé código como

System.Diagnostics.Debugger.Break(); 

pero está dando algún error de código como:

Recibí dos errores de evento: eventID 4096 VsJITDebugger y “El servicio no respondió a la solicitud de inicio o control de manera oportuna”.

Use el siguiente código en el método de OnStart servicio:

 System.Diagnostics.Debugger.Launch(); 

Elija la opción de Visual Studio del mensaje emergente.

Nota: Para usarlo solo en el modo de depuración, se puede usar una directiva #if DEBUG compiler, de la siguiente manera. Esto evitará la eliminación accidental o la depuración en modo de lanzamiento en un servidor de producción.

 #if DEBUG System.Diagnostics.Debugger.Launch(); #endif 

También puedes probar esto.

  1. Crea tu servicio de Windows e instala e inicia … Es decir, los servicios de Windows deben estar ejecutándose en su sistema.
  2. Mientras se ejecuta su servicio, vaya al menú Depurar , haga clic en Adjuntar proceso (o procese en Visual Studio antiguo)
  3. Encuentre su servicio en ejecución y luego asegúrese de que el proceso Mostrar de todos los usuarios y los procesos de Mostrar en todas las sesiones esté seleccionado, de lo contrario, selecciónelo.

enter image description here

  1. Haga clic en el botón Adjuntar
  2. Click OK
  3. Click Cerrar
  4. Establezca un punto de interrupción en su ubicación deseada y espere a que se ejecute. Depurará automáticamente cada vez que su código llegue a ese punto.
  5. Recuerde, ponga su punto de interrupción en un lugar accesible , si está en Inicio (), luego deténgalo e inicie de nuevo el servicio

(Después de buscar en Google, encontré esto en “Cómo depurar los servicios de Windows en Visual Studio”.)

Debe separar todo el código que hará las cosas del proyecto de servicio en un proyecto separado, y luego crear una aplicación de prueba que pueda ejecutar y depurar normalmente.

El proyecto de servicio sería solo el caparazón necesario para implementar el servicio.

O bien, como lo sugirió Lasse V. Karlsen, o establecer un ciclo en su servicio que esperará a que se adjunte un depurador. El más simple es

 while (!Debugger.IsAttached) { Thread.Sleep(1000); } ... continue with code 

De esta forma, puede iniciar el servicio y dentro de Visual Studio puede elegir “Adjuntar para procesar …” y adjuntarlo a su servicio, que luego reanudará la extracción normal.

Dado que ServiceBase.OnStart ha protected visibilidad, bajé por la ruta de reflexión para lograr la depuración.

 private static void Main(string[] args) { var serviceBases = new ServiceBase[] {new Service() /* ... */ }; #if DEBUG if (Environment.UserInteractive) { const BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic; foreach (var serviceBase in serviceBases) { var serviceType = serviceBase.GetType(); var methodInfo = serviceType.GetMethod("OnStart", bindingFlags); new Thread(service => methodInfo.Invoke(service, new object[] {args})).Start(serviceBase); } return; } #endif ServiceBase.Run(serviceBases); } 

Tenga en cuenta que Thread es, por defecto, un hilo de primer plano. return desde Main mientras los subprocesos de faux-service se están ejecutando no terminará el proceso.

Un artículo de Microsoft explica cómo depurar aquí un servicio de Windows y qué parte se puede perder si lo depuran uniéndolo a un proceso.

A continuación está mi código de trabajo. He seguido el enfoque sugerido por Microsoft.

Agregue este código a program.cs :

 static void Main(string[] args) { // 'If' block will execute when launched through Visual Studio if (Environment.UserInteractive) { ServiceMonitor serviceRequest = new ServiceMonitor(); serviceRequest.TestOnStartAndOnStop(args); } else // This block will execute when code is compiled as a Windows application { ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new ServiceMonitor() }; ServiceBase.Run(ServicesToRun); } } 

Agregue este código a la clase ServiceMonitor.

 internal void TestOnStartAndOnStop(string[] args) { this.OnStart(args); Console.ReadLine(); this.OnStop(); } 

Ahora vaya a Propiedades del proyecto , seleccione la pestaña “Aplicación” y seleccione Tipo de salida como “Aplicación de consola” al depurar, o “Aplicación de Windows” cuando termine la depuración, vuelva a comstackr e instale su servicio.

Ingrese la descripción de la imagen aquí

Puede hacer una aplicación de consola. Yo uso esta función main :

  static void Main(string[] args) { ImportFileService ws = new ImportFileService(); ws.OnStart(args); while (true) { ConsoleKeyInfo key = System.Console.ReadKey(); if (key.Key == ConsoleKey.Escape) break; } ws.OnStop(); } 

Mi clase ImportFileService es exactamente la misma que en la aplicación de mi servicio de Windows, excepto el heredero ( ServiceBase ).

También puede probar el método System.Diagnostics.Debugger.Launch () . Ayuda a tomar el puntero del depurador a la ubicación especificada y luego puede depurar el código.

Antes de este paso , instale su service.exe utilizando la línea de comandos del símbolo del sistema de Visual Studio – installutil projectservice.exe

Luego, inicie su servicio desde el Panel de control -> Herramientas administrativas -> Administración de equipos -> Servicio y aplicación -> Servicios -> Su nombre de servicio

Acabo de agregar este código a mi clase de servicio para poder llamar indirectamente a OnStart, similar para OnStop.

  public void MyOnStart(string[] args) { OnStart(args); } 

Estoy usando el parámetro /Console en el proyecto de Visual Studio DepuraciónOpciones de inicioArgumentos de línea de comando :

 public static class Program { [STAThread] public static void Main(string[] args) { var runMode = args.Contains(@"/Console") ? WindowsService.RunMode.Console : WindowsService.RunMode.WindowsService; new WinodwsService().Run(runMode); } } public class WindowsService : ServiceBase { public enum RunMode { Console, WindowsService } public void Run(RunMode runMode) { if (runMode.Equals(RunMode.Console)) { this.StartService(); Console.WriteLine("Press  to stop service..."); Console.ReadLine(); this.StopService(); Console.WriteLine("Press  to exit."); Console.ReadLine(); } else if (runMode.Equals(RunMode.WindowsService)) { ServiceBase.Run(new[] { this }); } } protected override void OnStart(string[] args) { StartService(args); } protected override void OnStop() { StopService(); } ///  /// Logic to Start Service /// Public accessibility for running as a console application in Visual Studio debugging experience ///  public virtual void StartService(params string[] args){ ... } ///  /// Logic to Stop Service /// Public accessibility for running as a console application in Visual Studio debugging experience ///  public virtual void StopService() {....} } 

Desafortunadamente, si intenta depurar algo al comienzo de una operación de servicio de Windows, “adjuntar” al proceso en ejecución no funcionará. Intenté usar Debugger.Break () dentro del procedimiento OnStart, pero con una aplicación comstackda de Visual Studio 2010 de 64 bits, el comando break arroja un error como este:

 System error 1067 has occurred. 

En ese momento, debe configurar una opción de “Ejecución de archivo de imagen” en su registro para su ejecutable. Lleva cinco minutos configurarlo y funciona muy bien. Aquí hay un artículo de Microsoft donde los detalles son:

Cómo iniciar el depurador automáticamente

Pruebe la línea de comandos de eventos posteriores a la construcción de Visual Studio.

Intenta agregar esto en la construcción posterior:

 @echo off sc query "ServiceName" > nul if errorlevel 1060 goto install goto stop :delete echo delete sc delete "ServiceName" > nul echo %errorlevel% goto install :install echo install sc create "ServiceName" displayname= "Service Display Name" binpath= "$(TargetPath)" start= auto > nul echo %errorlevel% goto start :start echo start sc start "ServiceName" > nul echo %errorlevel% goto end :stop echo stop sc stop "ServiceName" > nul echo %errorlevel% goto delete :end 

Si el error de comstackción aparece con un mensaje como Error 1 The command "@echo off sc query "ServiceName" > nul etc., Ctrl + C, luego Ctrl + V el mensaje de error en el Bloc de notas y observe la última frase del mensaje.

Podría decirse que exited with code x . Busque el código en algún error común aquí y vea cómo resolverlo.

 1072 -- Marked for deletion → Close all applications that maybe using the service including services.msc and Windows event log. 1058 -- Can't be started because disabled or has no enabled associated devices → just delete it. 1060 -- Doesn't exist → just delete it. 1062 -- Has not been started → just delete it. 1053 -- Didn't respond to start or control → see event log (if logged to event log). It may be the service itself throwing an exception. 1056 -- Service is already running → stop the service, and then delete. 

Más sobre los códigos de error aquí .

Y si el error de comstackción con un mensaje como este,

 Error 11 Could not copy "obj\x86\Debug\ServiceName.exe" to "bin\Debug\ServiceName.exe". Exceeded retry count of 10. Failed. ServiceName Error 12 Unable to copy file "obj\x86\Debug\ServiceName.exe" to "bin\Debug\ServiceName.exe". The process cannot access the file 'bin\Debug\ServiceName.exe' because it is being used by another process. ServiceName 

abra cmd y luego intente eliminarlo primero con taskkill /fi "services eq ServiceName" /f

Si todo está bien, F5 debería ser suficiente para depurarlo.

En el método OnStart , haz lo siguiente.

 protected override void OnStart(string[] args) { try { RequestAdditionalTime(600000); System.Diagnostics.Debugger.Launch(); // Put breakpoint here. .... Your code } catch (Exception ex) { .... Your exception code } } 

A continuación, ejecute un símbolo del sistema como administrador y coloque lo siguiente:

 c:\> sc create test-xyzService binPath= \bin\debug\service.exe type= own start= demand 

La línea anterior creará test-xyzService en la lista de servicios.

Para iniciar el servicio, esto le pedirá que se una al debut en Visual Studio o no.

 c:\> sc start text-xyzService 

Para detener el servicio:

 c:\> sc stop test-xyzService 

Para eliminar o desinstalar:

 c:\> sc delete text-xyzService 

Encontré esta pregunta, pero creo que falta una respuesta clara y simple.

No quiero adjuntar mi depurador a un proceso, pero aún quiero poder llamar al servicio en los métodos OnStart y OnStop . También quiero que se ejecute como una aplicación de consola para que pueda registrar información de NLog a una consola.

Encontré estas guías shinys que hacen esto:

  • Depuración de un proyecto de servicio de Windows

  • Manera más fácil de depurar un servicio de Windows

Comience por cambiar el Output type proyectos a la Console Application .

Ingrese la descripción de la imagen aquí

Cambie su Program.cs para que se vea así:

 static class Program { ///  /// The main entry point for the application. ///  static void Main() { // Startup as service. ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new Service1() }; if (Environment.UserInteractive) { RunInteractive(ServicesToRun); } else { ServiceBase.Run(ServicesToRun); } } } 

A continuación, agregue el siguiente método para permitir que los servicios se ejecuten en modo interactivo.

 static void RunInteractive(ServiceBase[] servicesToRun) { Console.WriteLine("Services running in interactive mode."); Console.WriteLine(); MethodInfo onStartMethod = typeof(ServiceBase).GetMethod("OnStart", BindingFlags.Instance | BindingFlags.NonPublic); foreach (ServiceBase service in servicesToRun) { Console.Write("Starting {0}...", service.ServiceName); onStartMethod.Invoke(service, new object[] { new string[] { } }); Console.Write("Started"); } Console.WriteLine(); Console.WriteLine(); Console.WriteLine( "Press any key to stop the services and end the process..."); Console.ReadKey(); Console.WriteLine(); MethodInfo onStopMethod = typeof(ServiceBase).GetMethod("OnStop", BindingFlags.Instance | BindingFlags.NonPublic); foreach (ServiceBase service in servicesToRun) { Console.Write("Stopping {0}...", service.ServiceName); onStopMethod.Invoke(service, null); Console.WriteLine("Stopped"); } Console.WriteLine("All services stopped."); // Keep the console alive for a second to allow the user to see the message. Thread.Sleep(1000); } 

Depure un servicio de Windows a través de http (probado con VS 2015 Update 3 y .Net FW 4.6)

En primer lugar, debe crear un proyecto de consola dentro de su solución VS (Agregar -> Nuevo proyecto -> Aplicación de consola).

Dentro del nuevo proyecto, crea una clase “ConsoleHost” con ese código:

 class ConsoleHost : IDisposable { public static Uri BaseAddress = new Uri(http://localhost:8161/MyService/mex); private ServiceHost host; public void Start(Uri baseAddress) { if (host != null) return; host = new ServiceHost(typeof(MyService), baseAddress ?? BaseAddress); //binding var binding = new BasicHttpBinding() { Name = "MyService", MessageEncoding = WSMessageEncoding.Text, TextEncoding = Encoding.UTF8, MaxBufferPoolSize = 2147483647, MaxBufferSize = 2147483647, MaxReceivedMessageSize = 2147483647 }; host.Description.Endpoints.Clear(); host.AddServiceEndpoint(typeof(IMyService), binding, baseAddress ?? BaseAddress); // Enable metadata publishing. var smb = new ServiceMetadataBehavior { HttpGetEnabled = true, MetadataExporter = { PolicyVersion = PolicyVersion.Policy15 }, }; host.Description.Behaviors.Add(smb); var defaultBehaviour = host.Description.Behaviors.OfType().FirstOrDefault(); if (defaultBehaviour != null) { defaultBehaviour.IncludeExceptionDetailInFaults = true; } host.Open(); } public void Stop() { if (host == null) return; host.Close(); host = null; } public void Dispose() { this.Stop(); } } 

Y este es el código para la clase Program.cs:

 public static class Program { [STAThread] public static void Main(string[] args) { var baseAddress = new Uri(http://localhost:8161/MyService); var host = new ConsoleHost(); host.Start(null); Console.WriteLine("The service is ready at {0}", baseAddress); Console.WriteLine("Press  to stop the service."); Console.ReadLine(); host.Stop(); } } 

Las configuraciones como las conexiones se deben copiar en el archivo App.config del proyecto de la consola.

Para instalar la consola, haga clic derecho en el proyecto de la consola y haga clic en Depurar -> Iniciar nueva instancia.