Comment déboguer les services Windows dans Visual Studio?

Est-il possible de déboguer les services Windows dans Visual Studio?

J’ai utilisé du code comme

System.Diagnostics.Debugger.Break(); 

mais cela donne une erreur de code comme:

J’ai eu deux erreurs d’événement: eventID 4096 VsJITDebugger et “Le service n’a pas répondu à la demande de démarrage ou de contrôle en temps opportun.”

Utilisez le code suivant dans la méthode de service OnStart :

 System.Diagnostics.Debugger.Launch(); 

Choisissez l’option Visual Studio dans le message contextuel.

Remarque: pour l’utiliser uniquement en mode Débogage, vous pouvez utiliser une directive de compilation #if DEBUG , comme suit. Cela empêchera un débrayage ou un débogage accidentel en mode de publication sur un serveur de production.

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

Vous pouvez également essayer ceci.

  1. Créez votre service Windows et installez et démarrez…. C’est-à-dire que les services Windows doivent être exécutés sur votre système.
  2. Pendant que votre service est en cours d’exécution, accédez au menu Déboguer , cliquez sur Joindre le processus (ou le traiter dans l’ancien Visual Studio).
  3. Recherchez votre service en cours d’exécution, puis assurez-vous que l’ option Afficher le processus de tous les utilisateurs et Afficher les processus de toutes les sessions est sélectionnée, sinon sélectionnez-la.

entrer la description de l'image ici

  1. Cliquez sur le bouton Joindre
  2. Cliquez sur OK
  3. Cliquez sur Fermer
  4. Définissez un point d’arrêt sur votre emplacement souhaité et attendez l’exécution. Il va déboguer automatiquement chaque fois que votre code atteint ce point.
  5. Rappelez-vous, placez votre point d’arrêt à un endroit accessible , si c’est onStart (), puis arrêtez et redémarrez le service

(Après beaucoup de recherches sur Google, j’ai trouvé ceci dans “Comment déboguer les services Windows dans Visual Studio”.)

Vous devez séparer tout le code qui fera les choses du projet de service dans un projet distinct, puis créer une application de test que vous pouvez exécuter et déboguer normalement.

Le projet de service ne serait que le shell nécessaire pour implémenter la partie service de celui-ci.

Soit cela comme suggéré par Lasse V. Karlsen, ou mettre en place une boucle dans votre service qui attendra un débogueur à joindre. Le plus simple est

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

De cette façon, vous pouvez démarrer le service et au sein de Visual Studio, vous choisissez “Attach to Process …” et attachez-vous à votre service, qui reprendra ensuite son exécution normale.

Étant donné que ServiceBase.OnStart a protected visibilité, j’ai parcouru le parcours de reflection pour réaliser le débogage.

 private static void Main(ssortingng[] 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); } 

Notez que Thread est, par défaut, un thread de premier plan. return de Main alors que les threads de faux-services sont en cours d’exécution ne mettra pas fin au processus.

Un article de Microsoft explique comment déboguer un service Windows ici et quel rôle chacun peut manquer s’il le débogue en se connectant à un processus.

Voici mon code de travail. J’ai suivi l’approche proposée par Microsoft.

Ajoutez ce code à program.cs :

 static void Main(ssortingng[] 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 comstackd as a Windows application { ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new ServiceMonitor() }; ServiceBase.Run(ServicesToRun); } } 

Ajoutez ce code à la classe ServiceMonitor.

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

Maintenant, allez dans Propriétés du projet , sélectionnez l’onglet “Application” et sélectionnez Type de sortie comme “Application console” lors du débogage, ou “Application Windows” lorsque vous avez terminé le débogage, recomstackz et installez votre service.

Entrez la description de l'image ici

Vous pouvez créer une application console. J’utilise cette fonction main :

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

Ma classe ImportFileService est exactement la même que dans l’application de mon service Windows, à l’exception de inheritant ( ServiceBase ).

Vous pouvez également essayer la méthode System.Diagnostics.Debugger.Launch () . Cela aide à amener le pointeur du débogueur à l’emplacement spécifié et vous pouvez ensuite déboguer votre code.

Avant cette étape , installez votre service.exe à l’aide de la ligne de commande de l’invite de commandes Visual Studio – installutil projectservice.exe

Ensuite, démarrez votre service à partir du Panneau de configuration -> Outils d’administration -> Gestion de l’ordinateur -> Service et application -> Services -> Nom du service

Je viens d’append ce code à ma classe de service afin d’appeler indirectement OnStart, similaire pour OnStop.

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

J’utilise le paramètre /Console dans le projet Visual Studio DebugOptions de démarrageArguments de ligne de commande :

 public static class Program { [STAThread] public static void Main(ssortingng[] 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(ssortingng[] 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 ssortingng[] args){ ... } ///  /// Logic to Stop Service /// Public accessibility for running as a console application in Visual Studio debugging experience ///  public virtual void StopService() {....} } 

Malheureusement, si vous essayez de déboguer quelque chose au tout début d’une opération de service Windows, la «connexion» au processus en cours ne fonctionnera pas. J’ai essayé d’utiliser Debugger.Break () dans la procédure OnStart, mais avec une application compilée Visual Studio 2010 64 bits, la commande break lance simplement une erreur comme celle-ci:

 System error 1067 has occurred. 

À ce stade, vous devez définir une option “Exécution de fichier image” dans votre registre pour votre exécutable. Cela prend cinq minutes pour mettre en place, et ça marche très bien. Voici un article Microsoft où les détails sont:

Comment: lancer le débogueur automatiquement

Essayez la propre ligne de commande d’événement de post-construction de Visual Studio.

Essayez d’append ceci en 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 

Si l’erreur de construction avec un message comme Error 1 The command "@echo off sc query "ServiceName" > nul , Ctrl + C puis Ctrl + V le message d’erreur dans le Bloc-notes et regardez la dernière phrase du message.

On pourrait dire exited with code x . Recherchez le code dans une erreur commune ici et voyez comment le résoudre.

 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. 

Plus d’informations sur les codes d’erreur ici .

Et si l’erreur de construction avec un message comme celui-ci,

 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 

Ouvrez cmd, puis essayez d’abord de le tuer avec taskkill /fi "services eq ServiceName" /f

Si tout va bien, F5 devrait être suffisant pour le déboguer.

Dans la méthode OnStart , procédez comme suit.

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

Ensuite, exécutez une invite de commande en tant qu’administrateur et entrez les éléments suivants:

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

La ligne ci-dessus créera test-xyzService dans la liste des services.

Pour démarrer le service, vous serez invité à vous connecter à Visual Studio ou non.

 c:\> sc start text-xyzService 

Pour arrêter le service:

 c:\> sc stop test-xyzService 

Pour supprimer ou désinstaller:

 c:\> sc delete text-xyzService 

J’ai trouvé cette question, mais je pense qu’il manque une réponse claire et simple.

Je ne veux pas attacher mon débogueur à un processus, mais je veux quand même pouvoir appeler le service OnStart et les méthodes OnStop . Je souhaite également qu’il fonctionne en tant qu’application console afin que je puisse enregistrer les informations de NLog sur une console.

J’ai trouvé ces guides géniaux qui font ça:

  • Débogage d’un projet de service Windows

  • Un moyen plus facile de déboguer un service Windows

Commencez par modifier le Output type projets en Console Application .

Entrez la description de l'image ici

Changez votre Program.cs pour ressembler à ceci:

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

Ajoutez ensuite la méthode suivante pour autoriser les services exécutés en mode interactif.

 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 ssortingng[] { } }); 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); } 

Déboguer un service Windows sur http (testé avec VS 2015 Update 3 et .Net FW 4.6)

Tout d’abord, vous devez créer un projet de console dans votre solution VS (Ajouter -> Nouveau projet -> Application console).

Dans le nouveau projet, créez une classe “ConsoleHost” avec ce code:

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

Et c’est le code de la classe Program.cs:

 public static class Program { [STAThread] public static void Main(ssortingng[] 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(); } } 

Les configurations telles que les chaînes de connexion doivent être copiées dans le fichier App.config du projet Console.

Pour ouvrir la console, cliquez avec le bouton droit sur Projet de console et cliquez sur Déboguer -> Démarrer une nouvelle instance.