web-dev-qa-db-fra.com

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 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 Dans les meilleurs délais."

72
PawanS

Vous pouvez aussi essayer ceci.

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

enter image description here

  1. Cliquez sur le bouton Joindre 
  2. Cliquez OK 
  3. Cliquez Fermer 
  4. Définissez un point d'arrêt à l'emplacement souhaité et attendez l'exécution. Il déboguera automatiquement chaque fois que votre code atteindra ce point.
  5. Rappelez-vous, placez votre point d'arrêt à la position accessible, s'il s'agit de 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".)

58
PawanS

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

System.Diagnostics.Debugger.Launch();

Choisissez l'option Visual Studio dans le message contextuel.

Remarque: Pour l'utiliser en mode Debug uniquement, une directive de compilation #if DEBUG peut être utilisée, comme suit. Cela empêchera les accidents ou le débogage en mode de publication sur un serveur de production.

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

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

Le projet de service serait simplement le shell nécessaire pour implémenter la partie service.

20

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

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

... continue with code

De cette façon, vous pouvez démarrer le service et dans Visual Studio, choisissez "Attacher au processus ..." et attachez-le à votre service, qui reprendra ensuite son exécution normale.

14
Pauli Østerø

Étant donné que ServiceBase.OnStart a une visibilité protected, 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 que Thread est, par défaut, un fil de premier plan. returning from Main alors que les threads de faux service sont en cours d'exécution ne terminera pas le processus.

6
ta.speot.is

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

    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 classe ImportFileService est exactement la même que dans l'application de mon service Windows, à l'exception de l'héritant (ServiceBase).

3
kerrubin

Un article de Microsoft explique comment déboguer un service Windows ici / et quelle partie une personne peut manquer si elle le débogue en la rattachant à un processus.

Ci-dessous, mon code de travail. J'ai suivi l'approche proposé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 à 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.

 Enter image description here

3
kumar chandraketu

Vous pouvez également essayer System.Diagnostics.Debugger.Launch () method. Il est utile de placer le pointeur de débogage à l'emplacement spécifié et vous pouvez ensuite déboguer votre code.

Avant cette étape veuillez installer votre service.exe en utilisant la ligne de commande de la commande Visual Studio Invite - installutil projectservice.exe  

Puis démarrez votre service à partir du Panneau de configuration -> Outils d’administration -> Gestion de l’ordinateur -> Service et application -> Services -> Votre nom de service

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

J'utilise le paramètre /Console dans le projet Visual Studio Debug Options de démarrage Arguments de la 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() {....}
}
2
Sean M

Malheureusement, si vous essayez de déboguer quelque chose au tout début d'une opération de service Windows, la "liaison" 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 renvoie 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. Cela prend cinq minutes et tout fonctionne très bien. Voici un article de Microsoft où les détails sont:

Comment: lancer le débogueur automatiquement

1
Brian

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

Essayez d'ajouter ceci dans 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 du type Error 1 The command "@echo off sc query "ServiceName" > nul etc., Ctrl + C puis Ctrl + V le message d'erreur dans le bloc-notes et regardez la dernière phrase du message.

Il pourrait s'agir de 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 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.

1
asakura89

Dans la méthode OnStart, 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
    }
}

Exécutez ensuite une invite de commande en tant qu'administrateur et insérez 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 de services.

Pour démarrer le service, cela vous invitera à vous joindre au début 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
0
user3942119

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 de console.

Pour installer la console, cliquez avec le bouton droit sur le projet Console, puis cliquez sur Déboguer -> Démarrer une nouvelle instance.

0
mggSoft

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 les méthodes service OnStart et OnStop. Je souhaite également qu'il s'exécute en tant qu'application console pour pouvoir consigner les informations de NLog sur une console.

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

Commencez par changer les projets Output type en Console Application.

 Enter image description here

Changez votre Program.cs pour 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);
}
0
Ogglas

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 l'installation et l'exécution du service par l'environnement du serveur Windows.

Tout cela avec une seule ligne de code.

http://windowsservicehelper.codeplex.com/

Une fois installé et connecté, tout ce que vous avez à faire est de définir votre projet de service Windows en tant que projet de démarrage et de cliquer sur Démarrer dans votre débogueur.

0
Christophe Chang