¿Es posible depurar los servicios de Windows en Visual Studio?
Usé código como
System.Diagnostics.Debugger.Break();
pero está dando algún error de código como:
Recibí dos errores de eventos: eventID 4096 VsJITDebugger y "El servicio no respondió a la solicitud de inicio o control de manera oportuna".
También puedes probar esto.
(Después de mucho buscar en Google, encontré esto en "Cómo depurar los servicios de Windows en Visual Studio").
fuente
Usted debe separar todo el código que va a hacer las cosas desde el proyecto de servicio en un proyecto separado, y luego hacer una aplicación de prueba que se puede ejecutar y depurar normalmente.
El proyecto de servicio sería solo el shell necesario para implementar la parte de servicio del mismo.
fuente
O eso, como lo sugirió Lasse V. Karlsen, o configure un bucle en su servicio que esperará a que se conecte un depurador. El mas simple es
while (!Debugger.IsAttached) { Thread.Sleep(1000); } ... continue with code
De esa manera puede iniciar el servicio y dentro de Visual Studio, elija "Adjuntar al proceso ..." y adjuntarlo a su servicio, que luego reanudará la ejecución normal.
fuente
if (Environment.UserInteractive) { InteractiveRun(args); } else { Service instance = new Service(); ServiceBase[] servicesToRun = new ServiceBase[] { instance }; ServiceBase.Run(servicesToRun); }
Start
/OnStart()
supongofoo(bar)
Dado que
ServiceBase.OnStart
tieneprotected
visibilidad, fui 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
, de forma predeterminada, es un hilo de primer plano.return
ingMain
mientras se ejecutan los subprocesos del servicio falso no terminará el proceso.fuente
Un artículo de Microsoft explica cómo depurar un servicio de Windows aquí y qué parte se puede perder si lo depura al adjuntarlo a un proceso.
A continuación se muestra 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" cuando realice la depuración, o "Aplicación de Windows" cuando haya terminado con la depuración, vuelva a compilar e instale su servicio.
fuente
Puede crear una aplicación de consola. Yo uso esta
main
función: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
ImportFileService
clase es exactamente la misma que en la aplicación de mi servicio de Windows, excepto la herencia (ServiceBase
).fuente
ServiceBase
).". Me resulta más fácil depurar en una aplicación de consola, pero entiendo si eso no convence a todos.Utilizo un gran paquete de Nuget llamado ServiceProcess.Helpers.
Y cito...
Ayuda a la depuración de los servicios de Windows mediante la creación de una interfaz de usuario de reproducción / parada / pausa cuando se ejecuta con un depurador adjunto, pero también permite que el entorno del servidor de Windows instale y ejecute el servicio.
Todo esto con una línea de código.
http://windowsservicehelper.codeplex.com/
Una vez instalado y conectado, todo lo que necesita hacer es configurar su proyecto de servicio de Windows como el proyecto de inicio y hacer clic en iniciar en su depurador.
fuente
También puede probar el método System.Diagnostics.Debugger.Launch () . Ayuda a llevar el puntero del depurador a la ubicación especificada y luego puede depurar su código.
Antes de este paso , instale su service.exe usando la línea de comando 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 computadoras -> Servicio y aplicación -> Servicios -> Nombre de su servicio
fuente
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); }
fuente
Estoy usando el
/Console
parámetro en el proyecto de Visual Studio Depurar → Opciones de inicio → Argumentos de la 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 <ENTER> to stop service..."); Console.ReadLine(); this.StopService(); Console.WriteLine("Press <ENTER> 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(); } /// <summary> /// Logic to Start Service /// Public accessibility for running as a console application in Visual Studio debugging experience /// </summary> public virtual void StartService(params string[] args){ ... } /// <summary> /// Logic to Stop Service /// Public accessibility for running as a console application in Visual Studio debugging experience /// </summary> public virtual void StopService() {....} }
fuente
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
OnStart
y a losOnStop
métodos. También quiero que se ejecute como una aplicación de consola para poder registrar información de NLog en una consola.Encontré estas guías brillantes que hacen esto:
Depurar un proyecto de servicio de Windows
Una forma más sencilla de depurar un servicio de Windows
Empiece por cambiar los proyectos
Output type
aConsole Application
.Cambia tu
Program.cs
para que se vea así:static class Program { /// <summary> /// The main entry point for the application. /// </summary> static void Main() { // Startup as service. ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new Service1() }; if (Environment.UserInteractive) { RunInteractive(ServicesToRun); } else { ServiceBase.Run(ServicesToRun); } } }
Luego 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); }
fuente
Desafortunadamente, si está tratando de 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 de OnStart, pero con una aplicación compilada de Visual Studio 2010 de 64 bits, el comando de interrupción 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. Se necesitan cinco minutos para configurarlo y funciona muy bien. Aquí hay un artículo de Microsoft donde están los detalles:
Cómo: iniciar el depurador automáticamente
fuente
Pruebe la propia línea de comandos de eventos posteriores a la compilación de Visual Studio .
Intente agregar esto en la post-construcción:
@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 compilación con un mensaje como
Error 1 The command "@echo off sc query "ServiceName" > nul
así sucesivamente, Ctrl+ CentoncesCtrl + Vel mensaje de error en el Bloc de notas y mira la última oración del mensaje.Podría estar diciendo
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 códigos de error aquí .
Y si el error de compilació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
abre cmd y luego intenta matarlo primero con
taskkill /fi "services eq ServiceName" /f
Si todo va bien, F5debería bastar con depurarlo.
fuente
En el
OnStart
método, haga 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 } }
Luego, ejecute un símbolo del sistema como administrador e ingrese lo siguiente:
La línea anterior creará test-xyzService en la lista de servicios.
Para iniciar el servicio, esto le pedirá que se adjunte para debutar en Visual Studio o no.
Para detener el servicio:
Para eliminar o desinstalar:
fuente
Depurar 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, cree 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<ServiceDebugBehavior>().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 <Enter> to stop the service."); Console.ReadLine(); host.Stop(); } }
Las configuraciones como las cadenas de conexión deben copiarse en el archivo App.config del proyecto de la consola.
Para instalar la consola, haga clic con el botón derecho en el proyecto de la consola y haga clic en Depurar -> Iniciar nueva instancia.
fuente
Simplemente agregue un constructor a su clase de servicio (si aún no lo tiene). A continuación, puede consultar un ejemplo de visual basic .net.
Public Sub New() OnStart(Nothing) End Sub
Después de eso, haga clic derecho en el proyecto y seleccione " Depurar -> Iniciar una nueva instancia ".
fuente