En .NET, il arrive encore régulièrement d’avoir à développer des services Windows pour réaliser des traitements. Du coup, se pose rapidement la question du débogage et de ses problèmes… Il est en effet nécessaire de régulièrement démarrer/arrêter le service, ainsi que de l’installer/désinstaller, et ces opérations demandent de passer par différents outils en ligne de commande (installutil, net start…). Cela devient très vide rébarbatif et est chronophage, mais heureusement, il existe quelques astuces très simples pour résoudre ces soucis !

Méthode originale…

Voici la méthode “originale” pour déboguer un service Windows :

  • Installation du service (via “InstallUtil.exe”)
  • Démarrage du service (via “net start” ou le gestionnaire de services services.msc)
  • Démarrage de Visual Studio avec les droits d’administrateur
  • Attacher le débogueur au processus du service

En cas de modification du code, il est nécessaire d’arrêter le service, de recompiler et de refaire la procédure. En plus de l’aspect rébarbatif de la chose, il est compliqué de déboguer la phase de démarrage du service. En effet, le temps de s’attacher au processus est difficilement compressible et il est fort probable que vous soyez plus lent que votre ordinateur :)  

Méthode améliorée !

Jusqu’à maintenant, la solution que j’utilisais était de créer un nouveau projet console qui se chargeait de simuler l’exécution de mon service dans une console. C’est très pratique, mais cela implique de créer un projet de plus dans la solution. J’ai récemment trouvé une bien meilleure solution, qui fonctionnement plus ou moins sur le même principe : simuler le service dans une console. Cette fois-ci, au lieu de passer par un projet tiers, tout se passe dans le corps de la méthode Main de notre service. Plusieurs étapes sont à suivre :

  • Modifier le type de projet :
    • A partir des propriétés du projet, modifier le type de sortie pour “Application Console” (à la place d’“Application Windows” par défaut)
  • Déterminer le mode d’exécution :
    • Pour cela, on va passer par 2 propriétés :
      • Environment.UserInteractive
      • System.Diagnstics.Debugger.IsAttached
    • Si ces 2 propriétés valent “true” alors on peut considérer que l’on se trouve dans une situation de debug et qu’il faut donc lancer notre service dans une console. Dans le cas contraire, on peut démarrer le service de manière “normale”
  • Dans le cas du debug, on doit démarrer manuellement le service :
static void RunServiceInDebugMode(ServiceBase[] services)
{
    Console.WriteLine("Démarrage des services en mode intéractif...");
    Console.WriteLine();

    // Récupération de la méthode de démarrage de chaque service
    var onStartMethod = typeof(ServiceBase).GetMethod("OnStart", BindingFlags.Instance | BindingFlags.NonPublic);
 
    foreach (var service in services)
    {
        Console.Write("Démarrage de {0} ... ", service.ServiceName);
        onStartMethod.Invoke(service, new object[] { new string[] { } });
        Console.WriteLine("Démarré");
    }
 
    // Attente de la fin
    Console.WriteLine();
    Console.WriteLine("Appuyer sur une touche pour arrêter les services...");
    Console.ReadKey();
    Console.WriteLine();
 
    // Récupération de la méthode d'arrêt de chaque service
    var onStopMethod = typeof(ServiceBase).GetMethod("OnStop", BindingFlags.Instance | BindingFlags.NonPublic);
 
    // Boucle d'arrêt
    foreach (var service in services)
    {
        Console.Write("Arrêt de {0} ... ", service.ServiceName);
        onStopMethod.Invoke(service, null);
        Console.WriteLine("Arrêté");
    }
 
    Console.WriteLine();
    Console.Write("=== Appuyer sur une touche pour quitter ===");
    Console.ReadKey();
}

L’énorme avantage de cette solution est de pouvoir simplifier grandement le débogage de notre service. Il est possible d’analyser l’ensemble de la chaine de démarrage et d’ajouter des logs qui s’afficheront directement dans la console. Le gain de temps est non négligeable ! Par contre, la problématique des droits administrateur est toujours présente, et il est toujours nécessaire de démarrer votre Visual Studio en mode administrateur si vous avez besoin de ces droits pour votre service. De plus, la mécanique de log dans les journaux Windows n’est plus présente en mode interactif. A vous de bien outiller votre service pour pallier à ce cas.

Pour résumer, l’usage de cette solution simplifie grandement les tests d’un service Windows. Mais attention, cela n’affranchit pas de réaliser des tests “à l’ancienne” en mode service pour valider le bon comportement dans des conditions réelles.