Comment déboguer les services Windows dans Visual Studio?

85

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."

PawanS
la source

Réponses:

124

Utilisez le code suivant dans la OnStartméthode de service :

System.Diagnostics.Debugger.Launch();

Choisissez l'option Visual Studio dans le message contextuel.

Remarque: Pour l'utiliser uniquement en mode débogage, une #if DEBUGdirective de compilation peut être utilisée, comme suit. Cela empêchera le débogage accidentel ou en mode version sur un serveur de production.

#if DEBUG
    System.Diagnostics.Debugger.Launch();
#endif
Chirag
la source
9
N'oubliez pas d'exécuter VS en tant qu'administrateur. Ensuite, il sera disponible sur la liste.
Michael
1
Quelqu'un peut-il clarifier ce que signifie le message contextuel? Quand / comment cela apparaît-il?
Mike
@Mike, installez et démarrez le service, il apparaîtra.
Harshit le
@Mike, c'est une boîte de dialogue Windows qui apparaît sur le bureau interactif (connecté) et vous demande si vous souhaitez choisir une application avec laquelle déboguer le processus. Si vous choisissez VS, il lancera le débogueur et s'attachera au processus
Chris Johnson
63

Vous pouvez également essayer ceci.

  1. Créez votre service Windows et installez et démarrez…. Autrement dit, les services Windows doivent être exécutés sur votre système.
  2. Pendant que votre service est en cours d'exécution, allez dans le menu Déboguer , cliquez sur Joindre un processus (ou processus dans l'ancien Visual Studio)
  3. Recherchez votre service en cours d'exécution, puis assurez-vous que le processus Afficher de tous les utilisateurs et Afficher les processus dans toutes les sessions est sélectionné, sinon sélectionnez-le.

entrez 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 à votre emplacement souhaité et attendez l'exécution. Il déboguera automatiquement chaque fois que votre code atteindra ce point.
  5. Rappelez-vous, mettez votre point d'arrêt à un endroit accessible , s'il s'agit de onStart (), puis arrêtez et redémarrez le service

(Après de nombreuses recherches sur Google, j'ai trouvé ceci dans "Comment déboguer les services Windows dans Visual Studio".)

PawanS
la source
2
Impossible de voir cette option dans la mise à jour 5 de
VS2013
1
Mais vous devez exécuter votre Vs-2017 en tant qu'administrateur
chozha rajan
1
J'ai essayé ça. Cela fonctionnait avec un point d'arrêt dans onStop mais pas sur onStart, car lorsque le service s'arrête, le débogueur est
détaché
22

Vous devez séparer tout le code qui ne choses du projet de service dans un projet distinct, puis faire 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.

Lasse V. Karlsen
la source
OOW !! ctrl + C puis ctrl + V, je veux dire pour un nouveau projet. Ya ce que je fais seulement. N'est-il pas possible d'attacher un processus au débogage ou toute autre option plutôt qu'un projet séparé.?
PawanS
1
Bien sûr, c'est possible, mais il est beaucoup plus facile de développer un service Windows si vous retirez la partie service pendant le développement.
Lasse V.Karlsen
hmmm ... c'est un bon moyen, mais cela double simplement le travail. Je pensais que tout autre moyen existerait.
PawanS
9
Je ne vois pas comment cela «doublerait» le travail. Bien sûr, cela ajoutera un peu de surcharge pour créer le projet supplémentaire et séparer le code du service en un troisième projet, mais à part cela, vous ne feriez pas une copie du code, vous le déplaceriez, et faites référence à ce projet.
Lasse V. Karlsen
3
^ + 1. Cela double le travail de gestion du service, qui est quasiment nul en termes de temps de développement, et vous ne le faites qu'une seule fois. Déboguer un serice est DOULOUREUX - faites plutôt un double démarrage en ligne de commande. Vérifiez google pour les classes wrapper prédéfinies permettant de le faire (ils utilisent la réflexion pour simuler le démarrage / l'arrêt de la classe de service). une heure de travail, des tonnes d'économies, une perte nette: négative - vous gagnez du temps.
TomTom
14

Soit cela comme suggéré par Lasse V. Karlsen, soit mettre en place une boucle dans votre service qui attendra qu'un débogueur se connecte. Le plus simple est

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

... continue with code

De cette façon, vous pouvez démarrer le service et à l'intérieur de Visual Studio, vous choisissez «Attach to Process ...» et attachez-vous à votre service qui reprendra alors l'exécution normale.

Pauli Østerø
la source
où dois-je mettre le code ci-dessus ... et dans le processus d'attachement, je reçois mon service nommé disable
PawanS
3
nous avons également utilisé un code comme celui-ci if (Environment.UserInteractive) { InteractiveRun(args); } else { Service instance = new Service(); ServiceBase[] servicesToRun = new ServiceBase[] { instance }; ServiceBase.Run(servicesToRun); }
Kirill Kovalenko
ce code doit être le plus tôt possible avant que tout code que vous souhaitez déboguer ne puisse s'exécuter.
Pauli Østerø
@Pawan: In Start/ OnStart()Je suppose
abatishchev
@Kirill: Utilisez des tildes pour mettre en évidence le code à l'intérieur des commentaires, par exemplefoo(bar)
abatishchev
7

Étant donné que cela ServiceBase.OnStarta de la protectedvisibilité, j'ai emprunté la voie de la réflexion pour effectuer le débogage.

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

Notez qu'il Threads'agit, par défaut, d'un fil de discussion au premier plan. returnle fait Mainque les threads de faux-service s'exécutent ne mettra pas fin au processus.

ta.speot.is
la source
Comme OnStart est censé revenir rapidement, vous ne devriez pas avoir besoin de le faire dans un autre thread. Cependant, si le service ne démarre pas un autre thread, votre processus se fermera immédiatement.
Matt Connolly
@MattConnolly Sur ce dernier: si nécessaire, je change le code ci-dessus pour démarrer un thread de premier plan qui dort indéfiniment (avant le traitement normal).
ta.speot.is
Cela devrait être une vraie réponse. Fonctionne à merveille!
lentyai
4

Un article Microsoft explique comment déboguer un service Windows ici et quelle partie n'importe qui peut manquer s'il le débogage en se connectant à un processus.

Voici mon code de travail. J'ai suivi l'approche suggérée par Microsoft.

Ajoutez ce code à 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);
    }
}

Ajoutez ce code à la classe ServiceMonitor.

internal void TestOnStartAndOnStop(string[] 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, recompilez et installez votre service.

Entrez la description de l'image ici

Kumar Chandraketu
la source
existe-t-il un moyen de définir la sortie sur l'application console dans l'application Debug and Window dans la version?
kofifus
3

Vous pouvez créer une application console. J'utilise cette mainfonction:

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

Ma ImportFileServiceclasse est exactement la même que dans l'application de mon service Windows, à l'exception de l'héritante ( ServiceBase).

kerrubin
la source
est-ce sur le même projet ou j'ai un autre projet pour cette application console
PawanS
C'est 2 projets différents avec des classes similaires. Dans mon cas, c'est un service simple avec uniquement la classe ImportFileService qui est dupliquée. Quand je veux développer / tester, j'utilise consoleapp, puis je copie / colle. Comme l'a dit Lasse V. Karlsen, c'est un programme de débogage, toute la logique (métier) est sur un troisième projet.
kerrubin
OnStart n'est-il pas protégé?
Joe Phillips
Oui, c'est. C'est pourquoi j'ai dit "sauf l'héritier ( ServiceBase).". Je trouve plus facile de déboguer dans une application console, mais je comprends si cela ne convainc pas tout le monde.
kerrubin
3

J'utilise un excellent package Nuget appelé ServiceProcess.Helpers.

Et je cite ...

Il aide le débogage des services Windows en créant une interface utilisateur lecture / arrêt / pause lors de l'exécution avec un débogueur attaché, mais permet également au service d'être installé et exécuté par l'environnement de serveur Windows.

Tout cela avec une seule ligne de code.

http://windowsservicehelper.codeplex.com/

Une fois installé et câblé, tout ce que vous avez à faire est de définir votre projet de service Windows comme projet de démarrage et de cliquer sur démarrer sur votre débogueur.

Christophe Chang
la source
Merci d'avoir partagé! C'était de loin la solution la plus simple!
Wellington Zanelli le
2

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, veuillez installer votre service.exe à l'aide de la ligne de commande de l'invite de commande 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 -> Votre nom de service

Abhishek Srivastava
la source
2

Je viens d'ajouter ce code à ma classe de service pour pouvoir appeler indirectement OnStart, similaire pour OnStop.

    public void MyOnStart(string[] args)
    {
        OnStart(args);
    }
RichardHowells
la source
2

J'utilise le /Consoleparamètre dans le projet Visual Studio DebugOptions de démarrageArguments de ligne de commande :

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 <ENTER> to stop service...");
            Console.ReadLine();

            this.StopService();
            Console.WriteLine("Press <ENTER> 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();
    }

    /// <summary>
    /// Logic to Start Service
    /// Public accessibility for running as a console application in Visual Studio debugging experience
    /// </summary>
    public virtual void StartService(params string[] args){ ... }

    /// <summary>
    /// Logic to Stop Service
    /// Public accessibility for running as a console application in Visual Studio debugging experience
    /// </summary>
    public virtual void StopService() {....}
}
Sean M
la source
2

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 toujours pouvoir appeler le service OnStartet les OnStopméthodes. Je veux également qu'il s'exécute en tant qu'application console afin que je puisse enregistrer les informations de NLog vers une console.

J'ai trouvé ces guides brillants qui font cela:

Commencez par changer les projets Output typeen Console Application.

Entrez la description de l'image ici

Changez votre Program.cspour ressembler à ceci:

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    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 s'exécutant 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 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);
}
Ogglas
la source
Excellent code! Simple, efficace. +1. Mais tout aussi simple, j'ai créé une application Forms. Je déteste vraiment les applications de console. En outre, vous pouvez facilement implémenter un bouton Forms pour chaque événement de service.
Roland
1

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 lève simplement une erreur comme celle-ci:

System error 1067 has occurred.

À ce stade, vous devez configurer une option «Exécution de fichier image» dans votre registre pour votre exécutable. La configuration prend cinq minutes et fonctionne très bien. Voici un article Microsoft dont les détails sont:

Comment: lancer le débogueur automatiquement

Brian
la source
1

Essayez la ligne de commande d'événement post-build de Visual Studio .

Essayez d'ajouter ceci dans la post-construction:

@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 celui-ci Error 1 The command "@echo off sc query "ServiceName" > nul, Ctrl+ Cpuis Ctrl+ Vle message d'erreur dans le Bloc-notes et regardez la dernière phrase du message.

Ça pourrait être dire exited with code x. Recherchez le code dans une erreur courante 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 de le tuer d'abord avec taskkill /fi "services eq ServiceName" /f

Si tout va bien, cela F5devrait suffire à le déboguer.

asakura89
la source
0

Dans la OnStartméthode, procédez comme suit.

protected override void OnStart(string[] 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 ce qui suit:

c:\> sc create test-xyzService binPath= <ProjectPath>\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, cela vous demandera de vous attacher pour faire ses débuts dans 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
user3942119
la source
0

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<ServiceDebugBehavior>().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 voici le code de la classe 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 <Enter> 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 renforcer la console, cliquez avec le bouton droit sur le projet de console et cliquez sur Déboguer -> Démarrer une nouvelle instance.

mggSoft
la source
0

Ajoutez simplement un constructeur à votre classe de service (si vous ne l'avez pas déjà). Ci-dessous, vous pouvez consulter un exemple pour Visual Basic .net.

Public Sub New()
   OnStart(Nothing) 
End Sub

Ensuite, faites un clic droit sur le projet et sélectionnez " Déboguer -> Démarrer une nouvelle instance ".

Pedro Martín
la source