Existe-t-il un moyen plus simple de parcourir le code que de démarrer le service via le Gestionnaire de contrôle des services Windows puis de connecter le débogueur au thread? C'est un peu lourd et je me demande s'il existe une approche plus simple.
Si je veux déboguer rapidement le service, je viens de déposer une Debugger.Break()
dedans. Lorsque cette ligne est atteinte, cela me ramène à VS. N'oubliez pas de supprimer cette ligne lorsque vous avez terminé.
UPDATE: Au lieu des pragmas #if DEBUG
, vous pouvez également utiliser l'attribut Conditional("DEBUG_SERVICE")
.
[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 pendant les générations Debug. Pendant que vous y êtes, il peut être utile de créer une configuration de construction distincte pour le débogage du service.
Je pense aussi que le meilleur choix est d'avoir une "version" distincte pour une exécution normale et en tant que service, mais est-il vraiment nécessaire de dédier un commutateur de ligne de commande séparé à cette fin?
Ne pourriez-vous pas simplement faire:
public static int Main(string[] args)
{
if (!Environment.UserInteractive)
{
// Startup as service.
}
else
{
// Startup as application
}
}
Cela aurait le "bénéfice", que vous pouvez simplement démarrer votre application via un double clic (OK, si vous en avez vraiment besoin) et que vous pouvez simplement appuyer sur F5 dans Visual Studio (sans qu'il soit nécessaire de modifier les paramètres du projet pour inclure cette option /console
).
Techniquement, le Environment.UserInteractive
vérifie si le drapeau WSF_VISIBLE
est défini pour le poste de fenêtre actuel, mais existe-t-il une autre raison pour laquelle il renverrait false
, mis à part son exécution en tant que service (non interactif)?
Lorsque j'ai mis en place un nouveau projet de service il y a quelques semaines, j'ai trouvé ce post. Bien qu'il y ait beaucoup d'excellentes suggestions, je n'ai toujours pas trouvé la solution que je cherchais: la possibilité d'appeler les méthodes OnStart
et OnStop
des classes de service sans aucune modification des classes de service.
La solution que j'ai proposée utilise le Environment.Interactive
le mode de fonctionnement sélectionné, comme suggéré par d'autres réponses à ce message.
static void Main()
{
ServiceBase[] servicesToRun;
servicesToRun = new ServiceBase[]
{
new MyService()
};
if (Environment.UserInteractive)
{
RunInteractive(servicesToRun);
}
else
{
ServiceBase.Run(servicesToRun);
}
}
L'assistant RunInteractive
utilise la réflexion pour appeler les méthodes OnStart
et OnStop
protégées:
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 aussi écrit procédure pas à pas avec des explications.
Parfois, il est important d’analyser ce qui se passe lors du démarrage du service. L’attachement au processus n’aide pas ici, car vous n’êtes pas assez rapide pour attacher le débogueur le service démarre.
La réponse courte est que j'utilise ce qui suit 4 lignes de code pour le 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 méthode OnStart
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 astuces détaillées ci-dessous , car vous pouvez facilement rester bloqué. Les astuces suivantes font référence à Windows 7x64 et Visual Studio 2010 Team Edition, mais doivent également être valides pour d'autres environnements.
Important: Déployez le service en mode "manuel" (à l'aide de l'utilitaire InstallUtil
de l'invite de commande VS ou exécutez un projet d'installation de service que vous avez préparé). . Ouvrez Visual Studio avant vous démarrez 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 - Panneau de configuration du service.
En raison du code Debugger.Launch
, cela provoquera une boîte de dialogue "Une exception non gérée de Microsoft .NET Framework s'est produite dans Nom du fichier.exe." apparaître. Cliquez sur Oui, déboguer NomService.exe comme indiqué dans la capture d'écran:
Ensuite, dans Windows 7, le contrôle de compte d'utilisateur peut vous inviter à saisir les informations d'identification de l'administrateur. Entrez-les et continuez avec Yes:
Après cela, la fenêtre bien connue La fenêtre du débogueur Just-In-Time Visual Studio apparaît. Il vous demande si vous souhaitez déboguer à l'aide du débogueur sélectionné. Avant de cliquer sur 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 ne pas être affiché. Vous sélectionnez donc l'instance de Visual Studio que vous avez précédemment ouverte:
Après avoir cliqué sur Yes, après un certain temps, Visual Studio affiche la flèche jaune à droite de la ligne où se trouve l'instruction Debugger.Launch
et vous permet de déboguer votre code (méthode MyInitOnStart
, qui contient votre initialisation).
Pressing F5 continue immédiatement l'exécution, jusqu'à atteindre le prochain point d'arrêt préparé.
Indice: Pour que le service fonctionne, sélectionnez Debug -> Detach all . Cela vous permet d’exécuter un client qui communique avec le service après le démarrage correct de celui-ci et le débogage du code de démarrage. Si vous appuyez sur Shift+F5 (arrêter 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.
Note que
Si vous créez une version Release, , le code de débogage est automatiquement supprimé et le service s'exécute normalement.
J'utilise Debugger.Launch()
, lequel démarre et joint un débogueur . J'ai testé Debugger.Break()
également, lequel ne fonctionnait pas , car aucun débogueur n'est encore connecté au démarrage du service ( provoquant la "Erreur 1067: le processus s'est terminé de manière inattendue.").
RequestAdditionalTime
définit un délai plus long pour le démarrage du service (c'est pas retarder le code lui-même, mais poursuivra immédiatement avec l'instruction Debugger.Launch
). Sinon, le délai d'attente par défaut pour le démarrage du 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. En pratique, un délai d'attente de 10 minutes permet d'éviter l'affichage du 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 simple car elle vous oblige simplement à ajouter 4 lignes à un code de service existant, ce qui vous permet de prendre rapidement le contrôle et le débogage.
Ce que je fais habituellement, c’est d’encapsuler la logique du service dans une classe distincte et de la démarrer à partir d’une classe 'coureur' Cette classe de runner peut être le service réel ou simplement une application console. Donc, votre solution a (au moins) 3 projets:
/ConsoleRunner
/....
/ServiceRunner
/....
/ApplicationLogic
/....
Cette vidéo YouTube de Fabio Scopel explique comment bien déboguer un service Windows ... la méthode de le débuter commence à 4:45 dans la vidéo ...
Voici le code expliqué dans la vidéo ... dans votre fichier Program.cs, ajoutez le contenu de la section Débogage ...
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 marche
En gros, vous devez créer une public void OnDebug()
qui appelle la OnStart(string[] args)
car elle est protégée et inaccessible à l'extérieur. Le programme void Main()
est ajouté avec le préprocesseur #if
avec le #DEBUG
.
Visual Studio définit DEBUG
si le projet est compilé en mode débogage. Cela 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 tout ira bien, vous pourrez changer le mode Release
et la section normale else
déclenchera la logique.
METTRE &AGRAVE; 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 () lors du démarrage du service. (Cette classe pourrait également être facilement utilisée à partir d'une application console.)
L'effet Nice de cette conception est que les arguments avec lesquels vous configurez le minuteur peuvent être utilisés pour retarder le fonctionnement du service, afin que vous ayez le temps d'attacher un débogueur manuellement.
p.s. Comment attacher le débogueur manuellement à un processus en cours d'exécution ...?
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'avais aussi l'habitude de faire ce qui suit (déjà mentionné dans les réponses précédentes, mais avec les drapeaux conditionnels du compilateur [#if] pour éviter qu'il ne se déclenche dans une version Release).
J'ai arrêté de le faire de cette façon, car nous oublions parfois de construire Release et de laisser un débogueur briser une application en cours d'exécution sur une démo client (embarrassant!).
#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Break();
}
#endif
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
}
Vous pouvez également démarrer le service via la commande Prompt (sc.exe).
Personnellement, j’exécutais le code en tant que programme autonome lors de la phase de débogage et, lorsque la plupart des bogues étaient réglés, passait à l’exécution en tant que service.
Auparavant, je utilisais un commutateur de ligne de commande pour démarrer le programme en tant que service ou en tant qu'application standard. Ensuite, dans mon IDE, je mettrais le commutateur de manière à 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?
Je pense que cela dépend du système d'exploitation que vous utilisez. Vista est beaucoup plus difficile à connecter aux services, en raison de la séparation entre les sessions.
Les deux options que j'ai utilisées par le passé sont:
J'espère que cela t'aides.
Utilisez la bibliothèque TopShelf .
Créez une application console puis configurez la configuration dans votre menu principal.
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 "console.exe install"
Vous pouvez ensuite démarrer et arrêter le service dans le gestionnaire de services Windows.
Lorsque j'écris un service, je mets toute la logique de service dans un projet dll et crée deux "hôtes" qui appellent cette dll: l'un est un service Windows et l'autre, une application en ligne de commande.
J'utilise l'application en 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 en ligne de commande.
Si vous utilisez cette approche, rappelez-vous que vous devez tester tout le code lorsque vous exécutez un service réel, tandis que l'outil en ligne de commande est une aide au débogage de Nice. Il s'agit d'un environnement différent qui ne se comporte pas exactement comme un service réel.
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 ... sans 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 plug-in de services MMC), crée le processus Hôte du service. Cela vous donne un processus pour attacher le débogueur même si vous n'avez pas encore démarré votre vrai service. Après avoir associé le débogueur au processus, démarrez votre service réel et vous pourrez y accéder n'importe où dans le cycle de vie du service, y compris OnStart ().
Parce qu'il nécessite très peu d'intrusion de code, 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.
Détails:
1) En supposant que vous implémentiez MyService
, créez également MyServiceDebug
. Ajoutez les deux au tableau ServiceBase
dans Program.cs
comme suit:
/// <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 de service:
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 service.msc MMC.
3) Démarrez le service de débogage dans MMC.
4) Dans Visual Studio, attachez le débogueur au processus lancé par le service de débogage.
5) Démarrer le service réel et profiter du débogage.
Lors du développement et du débogage d'un service Windows, je l'exécute généralement comme une 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.
}
}
}
Que diriez-vous de Debugger.Break () dans la première ligne?
Pour déboguer les services Windows, je combine GFlags et un fichier .reg créé par regedit.
Ou enregistrez les extraits suivants et remplacez nom_service.exe par le nom de l'exécutable souhaité.
debugon.reg:
Éditeur du registre Windows, version 5.00 [HKEY_LOCAL_MACHINE\LOGICIEL\Microsoft\Windows NT\CurrentVersion\Image Options d’exécution de fichier\servicename.exe] .__ "GlobalFlag" = "0x00000000". vsjitdebugger.exe "
debugoff.reg:
Éditeur du registre Windows, version 5.00 [HKEY_LOCAL_MACHINE\LOGICIEL\Microsoft\Windows NT\CurrentVersion\Image Options d’exécution de fichier\nomservice.exe] .__ "GlobalFlag" = "0x00000000"
Ceci est un peu après la question, mais pourrait être utile pour référence future.
Si vous le pouvez, je suggérerais d'utiliser l'excellent TopShelf project. Cela facilite beaucoup le développement et le débogage d'un service Windows et facilite également le déploiement.
Découvrez-le: http://topshelf-project.com/
#if DEBUG
System.Diagnostics.Debugger.Break();
#endif
J'utilise une variante de la réponse de JOP. En utilisant les paramètres de ligne de commande, vous pouvez définir le mode de débogage dans IDE 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();
}
...
}
Pour dépanner le programme de service Windows existant, utilisez 'Debugger.Break ()' comme l'ont suggéré d'autres personnes.
Pour le nouveau programme de service Windows, je suggérerais d'utiliser la méthode de James Michael Hare http://geekswithblogs.net/BlackRabbitCoder/archive/2011/03/01/c-toolbox-debug-able-self-installable-windows-service- template-redux.aspx
Pour la programmation de routine de petites choses, j'ai fait une astuce très simple pour déboguer facilement mon service:
Au démarrage du service, je recherche 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 démarre tous les écouteurs et affiche simplement une boîte de message "Débogage en cours, appuyez sur ok pour terminer".
Ainsi, 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 de ligne de commande/debug, il agira comme un programme normal.
Dans VS, je vais simplement ajouter/debug en tant que paramètre de débogage et lancer directement le programme de service.
De cette façon, je peux facilement déboguer pour la plupart des problèmes mineurs. Bien sûr, certains éléments devront encore être débogués en tant que service, mais pour 99%, cela suffit.
Mettez simplement 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 autoriser le fait 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
Utilisez le projet 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 automatiquement détecté, le programme d'installation/de désinstallation automatique de votre service et plusieurs fonctionnalités les plus utilisées sont incluses.
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
}
}
Voici la méthode simple que j'ai utilisée pour tester le service, sans aucune méthode "Debug" supplémentaire et avec les tests d'unité 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 });
}
La meilleure option consiste à utiliser l'espace de noms ' System.Diagnostics '.
Incluez votre code dans if else bloquer pour le mode débogage et le mode libération comme indiqué ci-dessous pour passer du mode débogage au mode publication 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
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 touchera Debugger.Break();
lorsque vous exécuterez votre programme.
Vous avez deux options pour effectuer le débogage.
S'il vous plaît se référer THIS blog que j'ai créé pour le sujet.