Un moyen plus simple de déboguer un service Windows

Réponses:

271

Si je veux déboguer rapidement le service, je tombe juste Debugger.Break()dedans. Lorsque cette ligne est atteinte, elle me ramènera à VS. N'oubliez pas de supprimer cette ligne lorsque vous avez terminé.

MISE À JOUR: Comme alternative aux #if DEBUGpragmas, vous pouvez également utiliser l' Conditional("DEBUG_SERVICE")attribut.

[Conditional("DEBUG_SERVICE")]
private static void DebugMode()
{
    Debugger.Break();
}

Sur votre OnStart, appelez simplement cette méthode:

public override void OnStart()
{
     DebugMode();
     /* ... do the rest */
}

Là, le code ne sera activé que lors des builds de débogage. Pendant que vous y êtes, il peut être utile de créer une configuration de build distincte pour le débogage du service.

jop
la source
45
Ou vous pouvez utiliser Debugger.Launch (), vous devrez inclure une instruction using pour l'espace de noms Systems.Diagnostics.
Omar Kooheji
1
Votre article de blog a très bien fonctionné et m'a sauvé la journée :) mais le Debugger.Break () n'a pas fonctionné pour moi. il semble que .Net ignore la fonction DebugMode pour des raisons liées à l'optimisation.
Bizhan
3
Debugger.Launch () fonctionne pour moi quand Debugger.Break () ne fonctionne pas. (Le processus se termine avec le code 255.)
Oliver Bock
Comment faites-vous pour que ça fonctionne? Rien ne se passe. J'ai essayé Break () et Launch ().
4thSpace
13
@ 4thSpace: 1. créez un programme d'installation pour votre service, afin que vous puissiez installer votre service. 2. Ajoutez la ligne Debugger.Launch (); au début de votre Main (). 3. Créez votre code en mode débogage. 4. Remplacez les DLL installées par les DLL de débogage. 5. Démarrez le service à partir du panneau Services Windows. Une fenêtre apparaît maintenant pour vous demander de vous attacher à un débogueur. Cela a fonctionné pour moi. J'espère aussi pour vous.
ffonz
210

Je pense également que le fait d'avoir une "version" distincte pour une exécution normale et en tant que service est le chemin à parcourir, mais est-il vraiment nécessaire de dédier un commutateur de ligne de commande séparé à cet effet?

Ne pourriez-vous pas simplement faire:

public static int Main(string[] args)
{
  if (!Environment.UserInteractive)
  {
    // Startup as service.
  }
  else
  {
    // Startup as application
  }
}

Cela aurait "l'avantage", que vous pouvez simplement démarrer votre application via un double-clic (OK, si vous en avez vraiment besoin) et que vous pouvez simplement cliquer F5dans Visual Studio (sans avoir besoin de modifier les paramètres du projet pour inclure cette /consoleoption).

Techniquement, il Environment.UserInteractivevérifie si le WSF_VISIBLEdrapeau est défini pour la station Windows actuelle, mais y a-t-il une autre raison pour laquelle il reviendrait false, à part d'être exécuté en tant que service (non interactif)?

Christian.K
la source
Génial! J'ai précédemment utilisé une méthode "if #debug" pour démarrer en tant qu'application en cas de débogage, sinon un service. Cela signifie que l'application n'est pas exécutable en tant que service si vous souhaitez la déboguer, mais votre solution résout ce problème et la laisse s'exécuter dans les quatre combinaisons de service / application et de version / débogage.
Jonas
29
Si vous ne voulez pas que le programme s'exécute lorsque vous double-cliquez dessus (les utilisateurs peuvent être confus et exécuter plusieurs instances, etc.), vous pouvez utiliser à la System.Diagnostics.Debugger.IsAttachedplace de Environment.UserInteractive.
Blorgbeard est sorti
5
mais y a-t-il une autre raison pour laquelle cela retournerait faux, à part être exécuté comme un service (non interactif)? Je peux penser à un: une tâche planifiée qui ne s'attache pas à une console.
Hogan
7
j'utilise des paramètres de ligne de commande pour ce cas. --install pour installer le service, --uninstall pour désinstaller le service et --interactive pour exécuter le service en tant qu'application. j'ajoute --interactif aux options du projet (Débogage> Arguments de commande). Je peux donc facilement déboguer à partir de VS. un double-clic ne créera pas d'instance en cours d'exécution indésirable car --interactive est requis. juste mes 2 cents.
Emir Akaydın
@ EmirAkaydın Oui, en fait, j'ai aussi des paramètres de ligne de commande comme "sauvegarde". Cependant, je voulais en fait avoir une instance "interactive" lors d'un double-clic et non et un message d'erreur sur le fait qu'un service ne peut pas être démarré de cette façon. Des objectifs variables je suppose ;-)
Christian.K
123

Lorsque j'ai mis en place un nouveau projet de service il y a quelques semaines, j'ai trouvé ce message. Bien qu'il y ait beaucoup de bonnes suggestions, je n'ai toujours pas trouvé la solution que je voulais: la possibilité d'appeler les classes de service OnStartet les OnStopméthodes sans aucune modification des classes de service.

La solution que j'ai trouvée utilise le Environment.Interactivemode d'exécution sélectionné, comme suggéré par d'autres réponses à ce post.

static void Main()
{
    ServiceBase[] servicesToRun;
    servicesToRun = new ServiceBase[] 
    {
        new MyService()
    };
    if (Environment.UserInteractive)
    {
        RunInteractive(servicesToRun);
    }
    else
    {
        ServiceBase.Run(servicesToRun);
    }
}

L' RunInteractiveassistant utilise la réflexion pour appeler les méthodes protégées OnStartet OnStop:

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

C'est tout le code requis, mais j'ai également écrit une procédure pas à pas avec des explications.

Anders Abel
la source
Cela constitue une bonne méthode d'extension pour ServiceBase []. J'ai plusieurs services dans ma solution, plutôt que d'avoir une classe de base commune pour Program.cs, j'appelle simplement servicesToRun.RunInteractive (args). Belle solution @Anders!
David Keaveny
3
Grande solution en effet. J'ai créé une extension simple pour ServiceBase [] comme David l'a suggéré qui permet d'exécuter des services dans une seule ligne de code: pastebin.com/F0fhhG2R
Funbit
4
+1 Un ancien collègue à moi a créé une classe de base "EasyRunService" (qui hérite de ServiceProcess) qui fait à peu près la même chose, mais sans besoin de réflexion (car OnStart est maintenant dans la classe de base). Cela facilite vraiment le débogage d'un service Windows.
sondergard
3
@ Chazt3n Assurez-vous que le type de sortie de votre projet est défini sur "Application console". Quant à l'installation du service, peu importe le type de sortie sélectionné, le comportement est le même.
Funbit
2
Encore une excellente solution! La seule chose que j'ajouterais (comme indiqué dans le walk through) est de vous assurer d'entrer dans les propriétés du projet et de changer le type de sortie Console Applicationavant d'essayer de compiler et d'exécuter. Trouvez-le sur Project Properties -> Application -> Output type -> Console Application. De plus, pour que cela fonctionne correctement pour moi, j'ai fini par devoir exécuter l'application à l'aide de la startcommande. Ex: C:\"my app name.exe" -servicene fonctionnerait pas pour moi. Au lieu de cela, j'ai utiliséC:\start /wait "" "my app name.exe" -service
Arvo Bowen
47

Parfois, il est important d'analyser ce qui se passe lors du démarrage du service. La connexion au processus n'aide pas ici, car vous n'êtes pas assez rapide pour connecter le débogueur pendant le démarrage du service.

La réponse courte est, j'utilise les 4 lignes de code suivantes pour ce faire:

#if DEBUG
    base.RequestAdditionalTime(600000); // 600*1000ms = 10 minutes timeout
    Debugger.Launch(); // launch and attach debugger
#endif

Ceux-ci sont insérés dans la OnStartméthode du service comme suit:

protected override void OnStart(string[] args)
{
    #if DEBUG
       base.RequestAdditionalTime(600000); // 10 minutes timeout for startup
       Debugger.Launch(); // launch and attach debugger
    #endif
    MyInitOnstart(); // my individual initialization code for the service
    // allow the base class to perform any work it needs to do
    base.OnStart(args);
}

Pour ceux qui ne l'ont pas encore fait, j'ai inclus des conseils détaillés ci - dessous , car vous pouvez facilement rester coincé. Les conseils suivants se réfèrent à Windows 7x64 et Visual Studio 2010 Team Edition , mais devraient également être valables pour d'autres environnements.


Important: déployez le service en mode "manuel" (à l'aide de l' InstallUtilutilitaire à partir de l'invite de commande VS ou exécutez un projet d'installation du service que vous avez préparé). Ouvrez Visual Studio avant de démarrer le service et chargez la solution contenant le code source du service - configurez des points d'arrêt supplémentaires selon vos besoins dans Visual Studio - puis démarrez le service via le panneau de configuration du service.

En raison du Debugger.Launchcode, cela provoquera une boîte de dialogue "Une exception Microsoft .NET Framework non gérée s'est produite dans Servicename.exe ." apparaître. Cliquez comme indiqué sur la capture d'écran:Elevate Yes, debug Servicename.exe
FrameworkException

Ensuite, surtout dans Windows 7 UAC peut vous inviter à entrer les informations d'identification d'administrateur. Entrez-les et procédez avec Yes:

UACPrompt

Après cela, la fenêtre bien connue du débogueur Visual Studio Just-In-Time apparaît. Il vous demande si vous souhaitez déboguer à l'aide du débogueur supprimé. Avant de cliquer Yes, sélectionnez que vous ne voulez pas ouvrir une nouvelle instance (2e option) - une nouvelle instance ne serait pas utile ici, car le code source ne serait pas affiché. Vous sélectionnez donc à la place l'instance Visual Studio que vous avez ouverte: VSDebuggerPrompt

Après avoir cliqué Yes, après un certain temps, Visual Studio affiche la flèche jaune à droite de la ligne où se trouve l' Debugger.Launchinstruction et vous pouvez déboguer votre code (méthode MyInitOnStart, qui contient votre initialisation). VSDebuggerBreakpoint

Appuyez sur pour F5continuer l'exécution immédiatement, jusqu'à ce que le prochain point d'arrêt que vous avez préparé soit atteint.

Astuce: pour que le service continue de fonctionner, sélectionnez Déboguer -> Détacher tout . Cela vous permet d'exécuter un client communiquant avec le service une fois qu'il a démarré correctement et que vous avez terminé de déboguer le code de démarrage. Si vous appuyez sur Shift+F5 (arrêtez le débogage), cela mettra fin au service. Au lieu de cela, vous devez utiliser le Panneau de configuration du service pour l'arrêter.

Notez que

  • Si vous créez une version, le code de débogage est automatiquement supprimé et le service s'exécute normalement.

  • J'utilise Debugger.Launch(), qui démarre et attache un débogueur . J'ai également testé Debugger.Break(), ce qui n'a pas fonctionné , car il n'y a pas encore de débogueur attaché au démarrage du service (provoquant "Erreur 1067: le processus s'est terminé de manière inattendue." ).

  • RequestAdditionalTimedéfinit un délai plus long pour le démarrage du service (il ne retarde pas le code lui-même, mais poursuivra immédiatement l' Debugger.Launchinstruction). Sinon, le délai par défaut pour démarrer le service est trop court et le démarrage du service échoue si vous n'appelez pas base.Onstart(args)assez rapidement à partir du débogueur. Pratiquement, un délai de 10 minutes évite que vous voyez le message " le service n'a pas répondu ..." immédiatement après le démarrage du débogueur.

  • Une fois que vous vous y êtes habitué, cette méthode est très facile car elle vous oblige simplement à ajouter 4 lignes à un code de service existant, vous permettant ainsi de prendre rapidement le contrôle et de déboguer.

Mat
la source
1
Par curiosité, savez-vous s'il y a un délai d'attente pour l'interaction de l'utilisateur avec l'invite utilisateur Debugger.Launch ()?
Shiv
1
Comme décrit, base.RequestAdditionalTime(600000)empêchera le contrôle de service de mettre fin au service pendant 10 minutes s'il n'appelle pas base.OnStart(args)dans ce délai). En dehors de cela, je me souviens que l'UAC abandonnera également si vous n'entrez pas les informations d'identification d'administrateur après un certain temps (je ne sais pas combien de secondes exactement, mais je pense que vous devez l'entrer dans une minute, sinon UAC abandonne) , qui mettra fin à la session de débogage.
Matt
2
J'ai trouvé que c'était la meilleure méthode pour déboguer les messages CustomCommand. +1.
Justin
40

Ce que je fais habituellement, c'est encapsuler la logique du service dans une classe distincte et commencer celle-ci à partir d'une classe 'runner'. Cette classe de runner peut être le service réel ou simplement une application console. Votre solution a donc (au moins) 3 projets:

/ConsoleRunner
   /....
/ServiceRunner
   /....
/ApplicationLogic
   /....
Paul van Brenk
la source
1
J'avais l'habitude d'utiliser cette approche aussi, mais je pense qu'une combinaison de cela et de la réponse ci-dessus fonctionne comme un régal.
RobS
27

Cette vidéo YouTube de Fabio Scopel explique très bien comment déboguer un service Windows ... la méthode actuelle de le faire commence à 04h45 dans la vidéo ...

Voici le code expliqué dans la vidéo ... dans votre fichier Program.cs, ajoutez les trucs pour la section Debug ...

namespace YourNamespace
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main()
        {
#if DEBUG
            Service1 myService = new Service1();
            myService.OnDebug();
            System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
#else
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]
            {
                new Service1()
            };
            ServiceBase.Run(ServicesToRun);
#endif

        }
    }
}

Dans votre fichier Service1.cs, ajoutez la méthode OnDebug () ...

    public Service1()
    {
        InitializeComponent();
    }

    public void OnDebug()
    {
        OnStart(null);
    }

    protected override void OnStart(string[] args)
    {
        // your code to do something
    }

    protected override void OnStop()
    {
    }

Comment ça fonctionne

Fondamentalement, vous devez créer un public void OnDebug()qui appelle le OnStart(string[] args)car il est protégé et n'est pas accessible à l'extérieur. Le void Main()programme est ajouté avec un #ifpréprocesseur avec #DEBUG.

Visual Studio définit DEBUGsi le projet est compilé en mode débogage, ce qui permettra à la section de débogage (ci-dessous) de s'exécuter lorsque la condition est vraie

Service1 myService = new Service1();
myService.OnDebug();
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);

Et il fonctionnera comme une application console, une fois que les choses vont bien, vous pouvez changer le mode Releaseet la elsesection régulière déclenchera la logique

Jason Miller
la source
Je cherchais cette réponse, je ne sais pas pourquoi elle était classée si bas. Expliqué le code pour aider les autres ou éventuellement plus de commentaires;)
Vinod Srivastav
14

METTRE À JOUR

Cette approche est de loin la plus simple:

http://www.codeproject.com/KB/dotnet/DebugWinServices.aspx

Je laisse ma réponse originale ci-dessous pour la postérité.


Mes services ont tendance à avoir une classe qui encapsule une minuterie car je veux que le service vérifie à intervalles réguliers s'il y a du travail à faire.

Nous renouvelons la classe et appelons StartEventLoop () pendant le démarrage du service. (Cette classe peut également être facilement utilisée à partir d'une application console.)

L'effet secondaire agréable de cette conception est que les arguments avec lesquels vous configurez le minuteur peuvent être utilisés pour avoir un délai avant que le service ne commence réellement à fonctionner, de sorte que vous ayez le temps d'attacher un débogueur manuellement.

ps Comment attacher le débogueur manuellement à un processus en cours ...?

using System;
using System.Threading;
using System.Configuration;    

public class ServiceEventHandler
{
    Timer _timer;
    public ServiceEventHandler()
    {
        // get configuration etc.
        _timer = new Timer(
            new TimerCallback(EventTimerCallback)
            , null
            , Timeout.Infinite
            , Timeout.Infinite);
    }

    private void EventTimerCallback(object state)
    {
        // do something
    }

    public void StartEventLoop()
    {
        // wait a minute, then run every 30 minutes
        _timer.Change(TimeSpan.Parse("00:01:00"), TimeSpan.Parse("00:30:00");
    }
}

J'ai aussi l'habitude de faire ce qui suit (déjà mentionné dans les réponses précédentes mais avec les drapeaux du compilateur conditionnel [#if] pour éviter qu'il ne se déclenche dans une version Release).

J'ai arrêté de le faire de cette façon parce que parfois nous oublions de compiler Release et d'avoir une pause de débogage dans une application fonctionnant sur une démo client (embarrassante!).

#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
    System.Diagnostics.Debugger.Break();
}
#endif
rohancragg
la source
Que se passe-t-il lorsque le processus // do somethingprend plus de 30 minutes?
Vinod Srivastav
13

static void Main()
{
#if DEBUG
                // Run as interactive exe in debug mode to allow easy
                // debugging.

                var service = new MyService();
                service.OnStart(null);

                // Sleep the main thread indefinitely while the service code
                // runs in .OnStart

                Thread.Sleep(Timeout.Infinite);
#else
                // Run normally as service in release mode.

                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[]{ new MyService() };
                ServiceBase.Run(ServicesToRun);
#endif
}
Thomas Bratt
la source
[Désolé, aucune explication concernant les problèmes de code - démarquage] Doit s'exécuter normalement à partir de MS Visual Studio (F5) dans les versions de débogage. Fonctionne toujours comme un service normal dans les versions.
Thomas Bratt
Combinez cela avec la solution ci-dessus de Christian K. pour utiliser la propriété "Environment.UserInteractive" et la solution est vraiment propre et simple.
Ben Robbins
OnStartest protectedet vous ne pouvez pas modifier le niveau d'accès :(
Eduard Luca
10

Vous pouvez également démarrer le service via l'invite de commande (sc.exe).

Personnellement, j'exécuterais le code en tant que programme autonome dans la phase de débogage, et lorsque la plupart des bogues sont résolus, passez à l'exécution en tant que service.

akauppi
la source
10

Ce que j'avais l'habitude de faire était d'avoir un commutateur de ligne de commande qui démarrerait le programme en tant que service ou en tant qu'application régulière. Ensuite, dans mon IDE, je réglais le commutateur pour pouvoir parcourir mon code.

Avec certaines langues, vous pouvez réellement détecter s'il s'exécute dans un IDE et effectuer ce changement automatiquement.

Quelle langue utilisez-vous?

RB.
la source
9

Utilisez le TopShelf bibliothèque .

Créez une application console puis configurez la configuration dans votre Main

class Program
    {
        static void Main(string[] args)
        {
            HostFactory.Run(x =>
            {

                // setup service start and stop.
                x.Service<Controller>(s =>
                {
                    s.ConstructUsing(name => new Controller());
                    s.WhenStarted(controller => controller.Start());
                    s.WhenStopped(controller => controller.Stop());
                });

                // setup recovery here
                x.EnableServiceRecovery(rc =>
                {
                    rc.RestartService(delayInMinutes: 0);
                    rc.SetResetPeriod(days: 0);
                });

                x.RunAsLocalSystem();
            });
        }
}

public class Controller
    {
        public void Start()
        {

        }

        public void Stop()
        {

        }
    }

Pour déboguer votre service, appuyez simplement sur F5 dans Visual Studio.

Pour installer le service, tapez cmd "install console.exe"

Vous pouvez ensuite démarrer et arrêter le service dans le gestionnaire de services Windows.

Misterhex
la source
Leur licence était trop déroutante pour comprendre
l --''''''----------------- '' '' '' '' '' '15
Ils utilisent la licence Apache afaik. Topshelf est le moyen le plus simple que j'ai utilisé pour développer et déboguer des services Windows. Super facile à utiliser. Développez comme une application console. Installer en tant que service avec un commutateur de ligne de commande. Hautement recommandé.
vole le
TopShelf m'a fait gagner beaucoup de temps. Thx
L_7337
8

Je pense que cela dépend du système d'exploitation que vous utilisez, Vista est beaucoup plus difficile à attacher aux services, en raison de la séparation entre les sessions.

Les deux options que j'ai utilisées dans le passé sont:

  • Utilisez GFlags (dans les outils de débogage pour Windows) pour configurer un débogueur permanent pour un processus. Cela existe dans la clé de registre "Image File Execution Options" et est incroyablement utile. Je pense que vous devrez modifier les paramètres du service pour activer "Interagir avec le bureau". Je l'utilise pour tous les types de débogage, pas seulement pour les services.
  • L'autre option consiste à séparer un peu le code, afin que la partie service soit interchangeable avec un démarrage normal de l'application. De cette façon, vous pouvez utiliser un simple indicateur de ligne de commande et lancer en tant que processus (plutôt qu'en tant que service), ce qui facilite le débogage.

J'espère que cela t'aides.

RichS
la source
+1 pour GFlags. Ceci est particulièrement utile si vous ne pouvez pas modifier le code source (ou si vous ne l'avez pas).
Chris Gillum
6

J'aime pouvoir déboguer tous les aspects de mon service, y compris toute initialisation dans OnStart (), tout en l'exécutant avec un comportement de service complet dans le cadre du SCM ... pas de mode "console" ou "application".

Je le fais en créant un deuxième service, dans le même projet, à utiliser pour le débogage. Le service de débogage, lorsqu'il est démarré comme d'habitude (c'est-à-dire dans le plugin MMC de services), crée le processus hôte de service. Cela vous donne un processus auquel attacher le débogueur même si vous n'avez pas encore démarré votre véritable service. Après avoir attaché le débogueur au processus, démarrez votre véritable service et vous pouvez le pénétrer n'importe où dans le cycle de vie du service, y compris OnStart ().

Parce qu'il nécessite une intrusion de code très minimale, le service de débogage peut facilement être inclus dans votre projet de configuration de service et est facilement supprimé de votre version de production en commentant une seule ligne de code et en supprimant un seul programme d'installation de projet.

Détails:

1) En supposant que vous implémentez MyService, créez également MyServiceDebug. Ajoutez les deux au ServiceBasetableau Program.cscomme ceci:

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] 
        { 
            new MyService(),
            new MyServiceDebug()
        };
        ServiceBase.Run(ServicesToRun);
    }

2) Ajoutez le service réel ET le service de débogage au programme d'installation du projet pour le projet de service:

entrez la description de l'image ici

Les deux services (réel et débogage) sont inclus lorsque vous ajoutez la sortie du projet de service au projet d'installation du service. Après l'installation, les deux services apparaîtront dans le plugin MMC service.msc.

3) Démarrez le service de débogage dans MMC.

4) Dans Visual Studio, attachez le débogueur au processus démarré par le service de débogage.

5) Démarrez le vrai service et profitez du débogage.

BitMask777
la source
5

Lorsque j'écris un service, je mets toute la logique du service dans un projet dll et crée deux "hôtes" qui appellent dans cette dll, l'un est un service Windows et l'autre est une application en ligne de commande.

J'utilise l'application de ligne de commande pour le débogage et attache le débogueur au service réel uniquement pour les bogues que je ne peux pas reproduire dans l'application de ligne de commande.

Si vous utilisez cette approche, souvenez-vous simplement que vous devez tester tout le code lors de l'exécution dans un service réel, tandis que l'outil de ligne de commande est une bonne aide au débogage, c'est un environnement différent et il ne se comporte pas exactement comme un vrai service.

Nir
la source
4

Lors du développement et du débogage d'un service Windows, je l'exécute généralement en tant qu'application console en ajoutant un paramètre de démarrage / console et en le vérifiant. Rend la vie beaucoup plus facile.

static void Main(string[] args) {
    if (Console.In != StreamReader.Null) {
        if (args.Length > 0 && args[0] == "/console") {
            // Start your service work.
        }
    }
}
Maurice
la source
Jusqu'à ce que vous deviez déboguer des problèmes spécifiques au service.
leppie
Certes, vous devez ensuite attacher le débogueur au processus de service réel. Mais dans la plupart des cas, les bogues vont apparaître dans les deux sens et le développement est beaucoup plus facile.
Maurice
4

Que diriez-vous de Debugger.Break () sur la première ligne?

leppie
la source
2

Pour déboguer les services Windows, je combine GFlags et un fichier .reg créé par regedit.

  1. Exécutez GFlags, en spécifiant le nom exe et vsjitdebugger
  2. Exécutez regedit et accédez à l'emplacement où GFlags définit ses options
  3. Choisissez "Exporter la clé" dans le menu fichier
  4. Enregistrez ce fichier quelque part avec l'extension .reg
  5. Chaque fois que vous souhaitez déboguer le service: double-cliquez sur le fichier .reg
  6. Si vous souhaitez arrêter le débogage, double-cliquez sur le deuxième fichier .reg

Ou enregistrez les extraits de code suivants et remplacez servicename.exe par le nom de l'exécutable souhaité.


debugon.reg:

Éditeur de registre Windows version 5.00

[HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Image File Execution Options \ servicename.exe]
"GlobalFlag" = "0x00000000"
"Debugger" = "vsjitdebugger.exe"

debugoff.reg:

Éditeur de registre Windows version 5.00

[HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Image File Execution Options \ servicename.exe]
"GlobalFlag" = "0x00000000"

la source
Est-ce que cela fonctionne toujours sur Win 7 / Win 2008? C'est l'approche de support.microsoft.com/kb/824344 mais elle s'appuie sur des services interactifs, et je pensais qu'ils avaient été tués? C'était toujours mon option préférée (car des problèmes de démarrage peuvent survenir en production, où l'insertion d'un Debugger.Break () dans le code peut ne pas être une option).
piers7
1

Pour la programmation de petites choses de routine, j'ai fait une astuce très simple pour déboguer facilement mon service:

Au démarrage du service, je vérifie un paramètre de ligne de commande "/ debug". Si le service est appelé avec ce paramètre, je ne fais pas le démarrage habituel du service, mais plutôt démarre tous les écouteurs et affiche simplement une boîte de message "Débogage en cours, appuyez sur ok pour terminer".

Donc, si mon service est démarré de la manière habituelle, il démarrera en tant que service, s'il est démarré avec le paramètre / debug de la ligne de commande, il agira comme un programme normal.

Dans VS, je vais simplement ajouter / debug comme paramètre de débogage et démarrer directement le programme de service.

De cette façon, je peux facilement déboguer pour la plupart des petits problèmes. Bien sûr, certaines choses devront encore être déboguées en tant que service, mais pour 99%, cela suffit.

Sam
la source
1
#if DEBUG
    System.Diagnostics.Debugger.Break();
#endif
Ervin Ter
la source
1

J'utilise une variante de la réponse de JOP. À l'aide des paramètres de ligne de commande, vous pouvez définir le mode de débogage dans l'EDI avec les propriétés du projet ou via le gestionnaire de services Windows.

protected override void OnStart(string[] args)
{
  if (args.Contains<string>("DEBUG_SERVICE"))
  {
    Debugger.Break();
  }
  ...
}
nh99
la source
1

Placez votre déjeuner de débogueur n'importe où et attachez Visualstudio au démarrage

#if DEBUG
    Debugger.Launch();
#endif

Vous devez également démarrer VS en tant qu'Administrateur et vous devez autoriser qu'un processus puisse être débogué automatiquement par un utilisateur différent (comme expliqué ici ):

reg add "HKCR\AppID{E62A7A31-6025-408E-87F6-81AEB0DC9347}" /v AppIDFlags /t REG_DWORD /d 8 /f
wotanii
la source
1

Utilisez le projet de modèle de service Windows C # pour créer une nouvelle application de service https://github.com/HarpyWar/windows-service-template

Le mode console / service est détecté automatiquement, l'installateur / désinstallateur automatique de votre service et plusieurs des fonctionnalités les plus utilisées sont inclus.

HarpyWar
la source
1

Voici la méthode simple que j'ai utilisée pour tester le service, sans aucune méthode supplémentaire de «débogage» et avec des tests unitaires VS intégrés.

[TestMethod]
public void TestMyService()
{
    MyService fs = new MyService();

    var OnStart = fs.GetType().BaseType.GetMethod("OnStart", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

    OnStart.Invoke(fs, new object[] { null });
}

// As an extension method
public static void Start(this ServiceBase service, List<string> parameters)
{
     string[] par = parameters == null ? null : parameters.ToArray();

     var OnStart = service.GetType().GetMethod("OnStart", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

     OnStart.Invoke(service, new object[] { par });
}
MisterDr
la source
1
static class Program
{
    static void Main()
    {
        #if DEBUG

        // TODO: Add code to start application here

        //    //If the mode is in debugging
        //    //create a new service instance
        Service1 myService = new Service1();

        //    //call the start method - this will start the Timer.
        myService.Start();

        //    //Set the Thread to sleep
        Thread.Sleep(300000);

        //    //Call the Stop method-this will stop the Timer.
        myService.Stop();

         #else
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] 
        { 
            new Service1() 
        };

        ServiceBase.Run(ServicesToRun);
         #endif
    }
}
Mansoor
la source
c'est plus simple. changez simplement le paramètre de configuration de la solution en débogage, exécutez le projet / la solution, ajoutez des points d'arrêt au fur et à mesure.
Bahamut
0

Vous avez deux options pour effectuer le débogage.

  1. créer un fichier journal: Personnellement, je préfère un fichier journal séparé comme un fichier texte plutôt que d'utiliser le journal d'application ou le journal des événements, mais cela vous coûtera beaucoup au nom du temps, car il est toujours difficile de comprendre où se trouve l'emplacement exact de l'erreur
  2. Convertir l'application en application console: cela vous permettra, tous les outils de débogage que nous pouvons utiliser dans VS.

Veuillez consulter CET article de blog que j'ai créé pour le sujet.

Sandaru
la source
0

Il suffit de coller

Debugger.Break();

n'importe où dans votre code.

Par exemple ,

internal static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        private static void Main()
        {
            Debugger.Break();
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]
            {
                new Service1()
            };
            ServiceBase.Run(ServicesToRun);
        }
    }

Il frappera Debugger.Break();lorsque vous exécuterez votre programme.

Chutipong Roobklom
la source
0

La meilleure option est d'utiliser le ' System.Diagnostics espace de noms ».

Insérez votre code dans le bloc if else pour le mode de débogage et le mode de libération comme indiqué ci-dessous pour basculer entre le mode de débogage et le mode de libération dans Visual Studio,

#if DEBUG  // for debug mode
       **Debugger.Launch();**  //debugger will hit here
       foreach (var job in JobFactory.GetJobs())
            {
                //do something 
            }

#else    // for release mode
      **Debugger.Launch();**  //debugger will hit here
     // write code here to do something in Release mode.

#endif
Amey P Naik
la source