Come faccio a eseguire il debug dei servizi Windows in Visual Studio?

È ansible eseguire il debug dei servizi Windows in Visual Studio?

Ho usato il codice come

System.Diagnostics.Debugger.Break(); 

ma sta dando qualche errore di codice come:

Ho ricevuto due errori evento: eventID 4096 VsJITDebugger e “Il servizio non ha risposto alla richiesta di avvio o controllo in modo tempestivo.”

Utilizzare il seguente codice nel metodo OnStart servizio:

 System.Diagnostics.Debugger.Launch(); 

Scegli l’opzione Visual Studio dal messaggio a comparsa.

Nota: per utilizzarlo solo in modalità Debug, è ansible utilizzare una direttiva del compilatore #if DEBUG , come indicato di seguito. Ciò impedirà accidentale o debug nella modalità di rilascio su un server di produzione.

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

Puoi anche provare questo.

  1. Crea il tuo servizio Windows e installa e avvia …. Cioè, i servizi Windows devono essere in esecuzione nel tuo sistema.
  2. Mentre il tuo servizio è in esecuzione, vai al menu Debug , fai clic su Attach Process (o processa in vecchio Visual Studio)
  3. Trova il tuo servizio in esecuzione, quindi assicurati che sia selezionato Mostra processi da tutti gli utenti e Mostra processi in tutte le sessioni , in caso contrario selezionalo.

inserisci la descrizione dell'immagine qui

  1. Fai clic sul pulsante Allega
  2. Clicca OK
  3. Clicca Chiudi
  4. Impostare un punto di interruzione nella posizione desiderata e attendere l’esecuzione. Effettua il debug automatico ogni volta che il tuo codice raggiunge quel punto.
  5. Ricorda, posiziona il punto di interruzione in posizione raggiungibile , se è onStart (), quindi interrompi e riavvia il servizio

(Dopo un sacco di googling, ho trovato questo in “Come eseguire il debug dei servizi di Windows in Visual Studio”.)

È necessario separare tutto il codice che farà cose dal progetto di servizio in un progetto separato, quindi creare un’applicazione di test che sia ansible eseguire ed eseguire il debug normalmente.

Il progetto di servizio sarebbe solo la shell necessaria per implementare la parte di servizio di esso.

O come suggerito da Lasse V. Karlsen, o crea un ciclo nel tuo servizio che attenderà il collegamento di un debugger. Il più semplice è

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

In questo modo è ansible avviare il servizio e all’interno di Visual Studio si sceglie “Allega a processo …” e si collega al servizio che riprenderà la normale esecuzione.

Dato che ServiceBase.OnStart ha una visibilità protected , sono andato lungo il percorso di riflessione per ottenere il debug.

 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); } 

Si noti che Thread è, per impostazione predefinita, un thread in primo piano. return da Main mentre i thread del falso servizio sono in esecuzione non terminerà il processo.

Un articolo di Microsoft spiega come eseguire il debug di un servizio di Windows qui e quale parte può mancare a qualcuno se esegue il debug collegandosi a un processo.

Di seguito è riportato il mio codice di lavoro. Ho seguito l’approccio suggerito da Microsoft.

Aggiungi questo codice 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); } } 

Aggiungi questo codice alla class ServiceMonitor.

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

Ora vai su Proprietà progetto , seleziona la scheda “Applicazione” e seleziona Tipo di output come “Applicazione console” quando esegui il debug, o “Applicazione Windows” quando fai il debug, ricompila e installa il tuo servizio.

Inserisci qui la descrizione dell'immagine

È ansible creare un’applicazione per console. Io uso questa funzione 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(); } 

La mia class ImportFileService è esattamente la stessa dell’applicazione del servizio Windows, ad eccezione dell’ereditarietà ( ServiceBase ).

Puoi anche provare il metodo System.Diagnostics.Debugger.Launch () . Aiuta a portare il puntatore del debugger nella posizione specificata e puoi quindi eseguire il debug del tuo codice.

Prima di questo passaggio , installa il tuo service.exe utilizzando la riga di comando del prompt dei comandi di Visual Studio – installutil projectservice.exe

Quindi avviare il servizio dal Pannello di controllo -> Strumenti di amministrazione -> Gestione computer -> Servizio e applicazione -> Servizi -> Nome servizio

Ho appena aggiunto questo codice alla mia class di servizio in modo da poter chiamare indirettamente OnStart, simile a OnStop.

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

Sto usando il parametro /Console nel progetto Visual Studio DebugOpzioni di avvioArgomenti della riga di 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() {....} } 

Sfortunatamente, se stai cercando di eseguire il debug di qualcosa all’inizio di un’operazione di servizio di Windows, il “collegamento” al processo in esecuzione non funzionerà. Ho provato a utilizzare Debugger.Break () all’interno del processo di avvio di OnStart, ma con un’applicazione compilata di Visual Studio 2010 a 64 bit, il comando break genera solo un errore simile al seguente:

 System error 1067 has occurred. 

A quel punto, devi impostare l’opzione “Esecuzione file immagine” nel tuo registro per il tuo eseguibile. Ci vogliono cinque minuti per l’installazione, e funziona molto bene. Ecco un articolo di Microsoft in cui i dettagli sono:

Procedura: avviare automaticamente il debugger

Prova la riga di comando degli eventi post-compilazione di Visual Studio.

Prova ad aggiungere questo nel post-build:

 @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 

Se l’errore di compilazione con un messaggio come Error 1 The command "@echo off sc query "ServiceName" > nul così via, Ctrl + C quindi Ctrl + V il messaggio di errore in Blocco note e guarda l’ultima frase del messaggio.

Potrebbe essere detto exited with code x . Cerca il codice in qualche errore comune qui e vedi come risolverlo.

 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. 

Maggiori informazioni sui codici di errore qui .

E se l’errore di compilazione con un messaggio come questo,

 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 

apri cmd, quindi prova ad ucciderlo prima con i servizi taskkill /fi "services eq ServiceName" /f

Se tutto va bene, F5 dovrebbe essere sufficiente per eseguirne il debug.

Nel metodo OnStart , attenere alla seguente procedura.

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

Quindi eseguire un prompt dei comandi come amministratore e inserire quanto segue:

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

La riga sopra creerà test-xyzService nell’elenco dei servizi.

Per avviare il servizio, questo ti richiederà di albind al debutto in Visual Studio o meno.

 c:\> sc start text-xyzService 

Per interrompere il servizio:

 c:\> sc stop test-xyzService 

Per eliminare o disinstallare:

 c:\> sc delete text-xyzService 

Ho trovato questa domanda, ma penso che manchi una risposta chiara e semplice.

Non desidero colbind il mio debugger a un processo, ma desidero comunque poter chiamare i metodi OnStart e OnStop del servizio. Voglio anche che venga eseguito come applicazione per console in modo che possa registrare le informazioni da NLog su una console.

Ho trovato queste guide geniali che fanno questo:

  • Debug di un progetto di servizio di Windows

  • Un modo più facile per eseguire il debug di un servizio di Windows

Inizia cambiando il Output type progetti Console Application .

Inserisci qui la descrizione dell'immagine

Cambia il tuo Program.cs per assomigliare a questo:

 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); } } } 

Quindi aggiungere il seguente metodo per consentire i servizi in esecuzione in modalità intertriggers.

 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); } 

Eseguire il debug di un servizio Windows su http (testato con VS 2015 Update 3 e .Net FW 4.6)

In primo luogo, è necessario creare un progetto console all’interno della soluzione VS (Aggiungi -> Nuovo progetto -> Applicazione console).

All’interno del nuovo progetto, crea una class “ConsoleHost” con quel codice:

 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(); } } 

E questo è il codice per la class 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(); } } 

Le configurazioni come le stringhe di connessione devono essere copiate nel file App.config del progetto Console.

Per aumentare la potenza della console, fai clic con il pulsante destro del mouse sul progetto Console e fai clic su Debug -> Avvia nuova istanza.