48 votes

Comment déboguer un service Windows ?

J'ai lu l'article de MSDN sur le sujet. Je cite :

Parce qu'un service doit être exécuté à partir de dans le contexte de l'interface Services plutôt qu'à partir de Visual Studio, le débogage d'un service dans Visual Studio, le débogage d'un n'est pas aussi simple que le débogage d'un autre service de Visual Studio. [ ] de Visual Studio. Pour déboguer un service, vous devez démarrer le service, puis attacher un débogueur au processus dans lequel dans lequel il s'exécute. Vous pouvez ensuite déboguer votre application en utilisant toutes les fonctionnalités de débogage standard de Visual Studio.

Mon problème est que mon service ne démarre pas. Tout d'abord, il se bloque et dit :

Une exception non gérée (System.Runtime.InteropServices.COMException) s'est produite dans NomService.exe[3596])

et me propose de le déboguer (l'instance de débogage se bloque instantanément lorsque je choisis l'une d'entre elles). Ensuite, le message suivant apparaît

C sur l'ordinateur local. Erreur 1053 : Le service n'a pas répondu à à la demande de démarrage ou de contrôle en temps voulu

Alors, comment puis-je rechercher/déboguer la raison pour laquelle mon service ne démarre pas ? Le problème est que j'ai créé une application console qui fait EXACTEMENT ce que le service fait et cela fonctionne bien. (Je veux dire que j'ai juste copié le fichier OnStart () et le contenu de la boucle principale à main).

Toute aide serait appréciée.

Le service est écrit en C# avec une forte utilisation de l'interopérabilité. J'utilise VS2008

2 votes

Avez-vous consulté l'observateur d'événements ?

0 votes

Pouvez-vous afficher le code source de votre méthode OnStart() ?

0 votes

Cela peut s'avérer utile (selon le type de service) : codeproject.com/KB/dotnet/DebugWinServices.aspx

39voto

BrokenGlass Points 91618

Vous pouvez utiliser un paramètre pour permettre à votre application de décider si elle doit démarrer en tant que service ou en tant qu'application normale (c'est-à-dire, dans ce cas, afficher un formulaire ou démarrer le service) :

static void Main(string[] args)
{
    if ((1 == args.Length) && ("-runAsApp" == args[0]))
    {
        Application.Run(new application_form());
    }
    else
    {
        System.ServiceProcess.ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] { new MyService() };
        System.ServiceProcess.ServiceBase.Run(ServicesToRun);
    }
}

Maintenant si vous passez le paramètre "-runAsApp" vous pouvez déboguer l'application normalement - le SCM ne passera pas ce paramètre, donc vous pouvez aussi l'utiliser comme service sans aucun changement de code (à condition que vous dériviez de ServiceBase )

Editer :

L'autre différence avec les services Windows est l'identité (cela peut être particulièrement important avec InterOp) - vous devez vous assurer que vous testez sous la même identité en mode "app" et en mode service.

Pour ce faire, vous pouvez utiliser l'impersonnation (je peux poster un wrapper C# si cela vous aide, mais cela peut être facilement trouvé sur Google) en mode application pour utiliser la même identité que celle sous laquelle votre service Windows fonctionnera, c'est-à-dire généralement LocalService ou NetworkService.

Si une autre identité est nécessaire, vous pouvez ajouter des paramètres à l'app.config qui vous permettent de décider d'utiliser ou non des informations d'identification et, le cas échéant, de choisir l'utilisateur à personnifier - ces paramètres seraient actifs lors de l'exécution de l'app, mais désactivés pour le service Windows (puisque le service est déjà exécuté sous l'identité souhaitée) :

  <appSettings>
    <add key="useCredentials" value="false"/>
    <add key="user" value="Foo"/>
    <add key="password" value="Bar"/>
  </appSettings>

0 votes

C'est très intéressant, je ne savais pas qu'il était possible de faire cela.

0 votes

Je suis sur ce pourboire comme un gars sur un pourboire. Joli !

0 votes

Nous utilisons la même approche pour les services de mon cabinet.

21voto

Davido Points 2056

J'ai l'habitude de définir manuellement un point d'arrêt, puis de le faire pointer sur le projet actuellement ouvert en c#. Le code pour définir un point d'arrêt est le suivant :

System.Diagnostics.Debugger.Break();

Cela devrait vous permettre de commencer, puis de parcourir votre code et de voir ce qui se passe réellement.

0 votes

Comment peut-il faire cela s'il ne démarre pas ?

0 votes

En effet, si vous placez un point d'arrêt manuel dans votre code, vous pourrez observer ce qui se passe pendant le démarrage, ce qui vous permettra de comprendre pourquoi il échoue.

0 votes

Votre débogueur ne doit-il pas être connecté pour que cette ligne de code fasse quelque chose ?

14voto

KeithS Points 36130

J'ai volé cela à C. Lawrence Wenham, donc je ne peux pas vraiment m'en attribuer le mérite, mais vous pouvez attacher de manière programmatique un débogueur à un service, SANS interrompre l'exécution à ce moment-là, avec le code suivant :

System.Diagnostics.Debugger.Launch();

Placez ceci dans la méthode OnStart() de votre service, comme première ligne, et il vous demandera de choisir une instance de VS pour y attacher son débogueur. A partir de là, le système s'arrêtera aux points d'arrêt que vous aurez définis, et aux exceptions lancées. Je mettrais un #if DEBUG autour du code afin qu'une version de compilation ne l'inclue pas ; ou vous pouvez simplement le supprimer après avoir trouvé le problème.

0 votes

Belle explication de #if DEBUG .

9voto

floyd73 Points 800

Vous pouvez utiliser WinDbg/NTSD (un autre débogueur du paquet "Debugging tools for Windows") pour lancer un débogueur en même temps que votre service.

Pour ce faire, ouvrez "gflags" (également disponible dans le paquet mentionné ci-dessus) dans l'onglet "Image file" et définissez le chemin d'accès à l'exécutable du débogueur pour votre fichier image (service) ;

Si votre service est marqué comme interactif (ce qui n'est possible que s'il est exécuté sous le compte SYSTEM), vous pouvez directement lancer WinDbg, en définissant le débogueur comme suit "PATH_TO_WINDBG \windbg.exe -g -G" (les -g / -G sont nécessaires pour que le débogueur n'interrompe pas l'exécution au démarrage ou à la fin de l'application, ce qui est le comportement par défaut). Maintenant, lorsque vous démarrez votre service, la fenêtre windbg devrait s'ouvrir et capturer toute exception non gérée.

Si votre service n'est pas interactif, vous pouvez lancer le débogueur NTSD (un débogueur en ligne de commande) en mode distant et vous y connecter à partir de WinDbg (qui peut même être exécuté sur un autre PC). Pour ce faire, définissez le débogueur dans gflags à quelque chose comme "PATH_TO_NTSD \ntsd -remote tcp:port=6666,server=localhost" . Ensuite, connectez-vous au débogueur distant en démarrant windbg avec quelque chose comme "windbg -remote tcp:port=6666,server=localhost" et vous devriez avoir un contrôle total sur l'autre session de débogage.

Pour ce qui est de trouver la source de l'exception elle-même, un tutoriel sur windbg est trop long à lire ici, mais pour commencer, essayez d'exécuter la commande "!analyze -v" après que l'exception a été capturée - avec un peu de chance, c'est toute l'information dont vous aurez besoin .

Nota: C'est peut-être exagéré pour votre cas, mais avec cette approche, vous pouvez même déboguer les services pendant le démarrage du système (j'ai eu une fois un problème de synchronisation avec un service qui n'avait de problème qu'au premier démarrage du système).

7voto

Phil Points 3695

Une chose que je fais (qui est peut-être une sorte de piratage) est de mettre un Thread.Sleep(10000) dès le début de mon OnStart() méthode. Cela me donne une fenêtre de 10 secondes pour attacher mon débogueur au service avant qu'il ne fasse quoi que ce soit d'autre.

Bien entendu, j'enlève le Thread.Sleep() lorsque j'ai terminé le débogage.

Une autre chose que vous pouvez faire est la suivante :

public override void OnStart()
{
    try
    {
        // all your OnStart() logic here
    }
    catch(Exception ex)
    {
        // Log ex.Message
        if (!EventLog.SourceExists("MyApplication"))
            EventLog.CreateEventSource("MyApplication", "Application");

        EventLog.WriteEntry("MyApplication", "Failed to start: " + ex.Message);
        throw;
    }
}

Lorsque vous vous connectez ex.Message vous pouvez obtenir un message d'erreur plus détaillé. En outre, vous pouvez simplement enregistrer ex.ToString() pour obtenir toute la trace de la pile, et si vos fichiers .pdb se trouvent dans le même répertoire que votre exécutable, il vous dira même sur quelle ligne l'exception s'est produite.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X