.Net 2.0 et les services Windows
Date de publication : 23/11/2006
Par
Ronald VASSEUR (autres articles)

De nombreuses applications reposent sur des services Windows, ils sont un élément essentiel
de Windows, et à ce titre, il est important de connaître leurs spécificités et leur
fonctionnement. L'environnement .Net, et son Framework en version 2.0, nous offre de
nombreuses classes pour interagir et gérer des services Windows très facilement. Il
ne faut donc pas se priver de toutes ces fonctionnalités. Enfin, il est également
possible de créer ses propres services Windows avec .Net, j'espère vous montrer au
cours de cet article à quel point ces différentes tâches sont aisées.
Introduction
1. Présentation des services Windows
1.1. Qu'est-ce qu'un service Windows ?
1.2. Gestion des services sous Windows
2. Gérer les services Windows depuis .Net
2.1. Quelle interaction avec les services Windows ?
2.2. Gestion de services par le code
2.2.1. Interactions de base avec les services Windows
2.2.2. Autres interactions avec les services Windows
3. Créer un service Windows avec .Net
3.1. Création d'un service Windows avec Visual Studio 2005
3.1.1. Création du service
3.1.2. Installation du service avec InstallUtil
3.1.3. Installation du service avec un package MSI
3.2. Déboguer un service Windows avec Visual Studio 2005
Ressources
Conclusion
Introduction
Les services Windows sont souvent, à tord, un peu oubliés, et l'on n'y
songe pas toujours lorsque se présentent des besoins spécifiques qui pourraient
être satisfait par leur emploi. De plus, pensant être confronté à une
complexité trop grande, de nombreux développeurs n'utilisent pas ou peu
les services Windows ; que ce soit pour de simples interactions, et
encore plus lorsqu'il s'agit de réaliser un service complet en .Net.
Au cours de cet article, je vais essayer de vous présenter ce que sont
les services, comment ils s'intègrent à Windows, puis par la suite
comment interagir avec eux depuis une application .Net. Nous terminerons
enfin, par le développement et l'intégration dans Windows d'un service
développé en .Net avec Visual Studio 2005.
1. Présentation des services Windows
1.1. Qu'est-ce qu'un service Windows ?
Un service Windows, n'est ni plus ni moins qu'une application
qui est lancée au démarrage de Windows, et qui fonctionne en tâche
de fond sans intervention de l'utilisateur. On peut donc caractériser
un service de la manière suivante :
- Une application qui démarre en même temps que Windows
- Une application qui fonctionne en arrière plan sans intervention de l'utilisateur
Par ailleurs, de nombreux services sont en fait des composants de
Windows, qui sont configurés lors de l'installation du système d'exploitation.
Un service démarre généralement en même temps que Windows, mais pour être
exact, il existe trois types de démarrage :
- Automatique : le service démarre en même temps que Windows
- Manuel : le service démarre suite à l'action d'un utilisateur ou d'une application
- Désactivé : le service est désactivé et ne peut pas démarrer
Un service, qui en fait est une application, le plus souvent dépourvue
d'interface graphique, peut fonctionner de manière autonome, mais peut
également dépendre d'autres services, on parle dans cette situation de
"dépendances". Maintenant que nous avons défini rapidement ce qu'est
un service, voyons comment il est possible de les gérer sous Windows.
1.2. Gestion des services sous Windows
Pour illustrer mes propos j'utiliserai Windows XP Pro SP2, mais sachez
que, quelque soit votre version (depuis la version 2000 Pro/Server) de
Windows, le principe sera plus ou moins le même.
Tout d'abord Windows possède une console dédiée à la gestion des services,
elle est accessible depuis les outils d'administration du panneau de
configuration, ou depuis l'invite de commandes en tapant "services.msc"
(MSC, pour Microsoft Console, qui est en fait la console contenant tous
les logiciels enfichables, tels que par exemple Services, Gestionnaire
d'événements ou encore Gestion de l'ordinateur. Voici une capture
d'écran de cette console :
La console Services.msc a principalement une fonction de gestion des services,
c'est-à-dire gérer leur exécution, les configurer (compte utilisé, paramètre
de récupération…) et de voir leur dépendance vis-à-vis d'autres services.
Nous allons donc voir comment gérer un service simplement en étudiant de près ses
caractéristiques, du moins celles accessibles et modifiables depuis la console
Services.msc
Statut d'un service : il s'agît en fait de l'état dans lequel se trouve actuellement un service,
il peut être démarré (en cours d'exécution), arrêté ou suspendu. Le statut d'un service est
définit par défaut, mais l'utilisateur (administrateur de la machine évidemment) peut à tout
moment changer le statut d'un service, en démarrant, arrêtant ou suspendant son exécution. Il
va sans dire qu'un service arrêté ou suspendu n'est plus en état d'assurer la fonction qui est
la sienne.
Type de démarrage : le type de démarrage est en fait la façon dont va démarrer un service, il en
existe trois différents. Tout d'abord "automatique", c'est-à-dire qu'il sera lancé automatiquement
par Windows lors du démarrage de ce dernier ; ensuite il y a "manuel", le service est par défaut
arrêté et nécessitera l'intervention d'un utilisateur ou d'un programme ayant des permissions
administratives sur la machine, et enfin, le dernier type de démarrage est "désactivé", en
l'état un service ne peut pas être démarré que ce soit manuellement ou automatiquement.
Compte utilisé : un service s'exécutant sur une machine Windows doit tout naturellement
disposer de droits pour cela, ainsi il va employer un compte (identifiant et mot de passe)
ayant les autorisations requises. Majoritairement les services utilisent le compte système
local, pour faire simple, celui-ci dispose de droit équivalent au compte administrateur, mais
il ne peut pas être utilisé pour ouvrir une session Windows classique, il est utilisé "en
interne" par Windows. D'ailleurs, ce compte n'apparaît pas dans la console "Groupes et
Utilisateurs".
Dépendances du service : un service peut fonctionner seul de manière autonome, ou alors
nécessiter aussi d'autres services, ainsi lorsqu'un service dépend d'un autre, on appelle
cela une dépendance. Un service peut d'ailleurs avoir besoin de plusieurs autres services,
cela n'est pas limité à une unité. L'onglet " dépendances " affiche les services dont
dépend le service concerné, mais aussi les services dépendant de ce service, voici l'exemple
du service "Gestionnaire de comptes de sécurité " qui illustre ce propos :
Voilà, j'espère qu'au travers de cette rapide présentation des services
Windows vous avez pu mieux appréhender leur utilité et leur fonctionnement,
ainsi de comment ils peuvent être gérés par un administrateur Windows.
Passons maintenant au cœur du sujet qui nous intéresse, les interactions
avec les services depuis .Net.
2. Gérer les services Windows depuis .Net
2.1. Quelle interaction avec les services Windows ?
Les interactions depuis du code .Net avec des services sont relativement
simples, elles se limitent principalement à récupérer l'état d'un service,
l'installer, le démarrer, l'arrêter, le suspendre ou encore lui passer une
commande. On voit donc que cela n'est en soit pas très complexe, mais l'on ne
souhaite généralement pas interagir plus avec un service.
Un Namespace est dédié aux interactions avec les services Windows, il ne nomme
System.ServiceProcess, il contient en tout 10 classes. La classe que nous
utiliserons le plus au cours de cet article est sans conteste ServiceController,
c'est elle en effet qui permet, comme son nom l'indique, de contrôler
un service.
 |
Remarque : les exemples de code que je donnerai ici sont en Visual Basic 2005, ils s'appuieront
sur le Framework .Net 2.0, mais devraient pouvoir fonctionner avec le Framework .Net
1.1 sans grande adaptation.
|
Passons, sans plus tarder, à des exemples concrets de gestion de services
Windows depuis une application .Net 2.0.
2.2. Gestion de services par le code
 |
Information : tout d'abord pour utiliser le Namespace System.ServiceProcess il faut ajouter
une référence dans votre projet Visual Studio pour l'utiliser. Pour cela,
faites un clique droit sur votre projet dans l'explorateur de solution, puis
"ajouter une référence", dans l'onglet ".Net" choisissez System.ServiceProcess.
Une fois cela, ajoutez dans l'entête de votre fichier de code
"Imports System.ServicePorcess".
|
2.2.1. Interactions de base avec les services Windows
Arrêter un service Windows
Pour arrêter un service, rien de compliqué, il faut tout d'abord
instancier un objet de type ServiceController, lui indiquer le nom du service
souhaité, et utiliser la méthode Stop(). Voyons cela concrètement :
Arrêter un service Windows |
Private Sub StopService()
Dim myService As New ServiceController()
myService.MachineName = "OSCAR"
myService.ServiceName = "VMAuthdService"
If myService.CanStop Then
myService.Stop()
End If
End Sub |
Vous voyez que tout d'abord nous créons une instance de ServiceController,
puis nous définissons les deux propriétés qui respectivement représentent
le nom de la machine où s'exécute le service, et le nom du service lui-même.
Une surcharge du constructeur vous permet d'y passer directement ces deux
propriétés, mais j'ai préféré, pour des raisons de clarté, découper cela en
deux étapes.
La propriété MachineName est une chaîne de caractère, qui peut être un "."
pour représenter l'ordinateur local où s'exécute l'application, le nom NetBIOS
de la machine, ou encore son nom DNS complet si vous êtes dans un domaine
Active Directory.
La propriété ServiceName est le nom du service, il ne faut pas le confondre
avec le nom complet.
La propriété CanStop est en lecture seule, elle définit si un service peut être
arrêté une fois qu'il a été démarré, ce n'est pas toujours le cas, un simple If
permet d'avoir un code plus robuste, en effet, pourquoi essayer d'arrêter un
service qui ne peut pas l'être !
Ce que nous venons de voir pour arrêter un service va être à peu de chose près le
code que nous allons utiliser pour les autres interactions de base, à l'exception
de la méthode utilisée.
 |
Remarque : l'état d'un service étant par définition passager et appelé à évoluer
dans le temps, je vous indique qu'il existe la méthode .Refresh() qui permet de
rafraichir l'état d'un service, c'est-à-dire être sur de bien avoir l'état
actuel d'un service et non l'état détecté il y a 5 minutes et qui depuis a
peut-être changé.
|
Démarrer un service
Ici rien de particulier, sauf que l'on vérifie si le service n'est pas déjà démarré avant
de vouloir le faire, ce qui somme toute semble logique.
Démarrer un service |
Private Sub StartService()
Dim myService As New ServiceController("OSCAR", "VMAuthdService")
If Not myService.Status = ServiceControllerStatus.Running Then
myService.Start()
End If
End Sub |
Nous utilisons ici la propriété Statuts pour vérifier que le service n'est pas
déjà démarré, nous l'utiliserons encore, notamment pour récupérer l'état d'un service.
Suspendre un service
Suspendre un service revient à mettre en pause un service, à suspendre son
exécution provisoirement dans l'optique de la reprendre par la suite. La méthode
.Pause() permet de réaliser cela. Ici, j'ai employé un Try/Catch pour varier le
code par rapport aux deux exemples précédents.
Suspendre un service |
Private Sub SuspendService()
Dim myService As New ServiceController("OSCAR", "VMAuthdService")
Try
myService.Pause()
Catch ex As Exception
MessageBox.Show("Impossible de suspendre le service")
End Try
End Sub |
Récupérer l'état d'un service
Comme nous l'avons vu au début de cet article, un service peut être démarré,
arrêté ou suspendu, voyons maintenant comment depuis du code .Net détecter
l'état d'un service donné. Avec .Net rien de plus facile, voici un exemple :
Récupérer l'état d'un service |
Private Function GetServiceState() As ServiceProcess.ServiceControllerStatus
Dim myService As New ServiceController()
myService.MachineName = "OSCAR"
myService.ServiceName = "VMAuthdService"
Return myService.Status
End Function |
Un service peut se trouver dans 7 états différents, qui sont les suivants :
Nom de membre |
Description |
ContinuePending |
Le service est en attente. |
Paused |
Le service est suspendu. |
PausePending |
La suspension du service est en attente. |
Running |
Le service est en cours d'exécution. |
StartPending |
Le service est en cours de démarrage. |
Stopped |
Le service n'est pas en cours d'exécution. |
StopPending |
Le service est en cours d'arrêt. |
Ce tableau est extrait de la MSDN Library.
Après avoir vu des interactions de base avec un service Windows
depuis du code .Net, voyons d'autres types d'interactions, elles
ne sont pas spécialement plus complexes, mais elles sortent du
cadre du simple démarrer / arrêter comme nous l'avons vu
précédemment.
2.2.2. Autres interactions avec les services Windows
Nous allons ici employer d'autres classes et quelques processus légèrement
plus compliqués, mais rien d'insurmontable, évidemment puisque nous sommes en .Net :)
Lister les services présents
Une opération bien utile dans de nombreuses situations peut être de
récupérer la liste des services présents sur une machine donnée, par
exemple pour établir un inventaire ou même réaliser un petit gestionnaire
de services "fait maison".
Lister les services présents |
Private Function ListServices() As ServiceController()
Dim myServices() As ServiceController
myServices = ServiceController.GetServices()
Return myServices
End Function |
La méthode .GetServices() nous renvoie donc un tableau contenant tous les
services présents sur la machine locale. Un simple For Each permet donc de
parcourir le tableau et de récupérer les services. L'exemple suivant
affiche sur la sortie du débogueur le nom complet (pas le nom du service
qui nous sert pour interagir avec un service) de tous les services. (Évidemment
afficher cette liste à cet endroit n'est pas vraiment utile… cela n'est valable
que dans le cadre de ma démonstration).
Afficher la liste des services |
Private Sub DisplayServicesList()
For Each Service As ServiceController In ListServices()
Debug.WriteLine(Service.DisplayName)
Next
End Sub |
 |
Info : si l'on ne passe pas d'argument à la méthode .GetServices() alors
la liste retournée est celle de la machine locale, pour récupérer la liste
des services d'une machine distante il faut passer le nom de cette dernière
directement dans la méthode.
|
Services dépendants d'un service
Nous allons voir ici qu'il est possible de récupérer la liste éventuelle des
services dépendants d'un service donné. Cette information peut être notamment
très utile lorsqu'il s'agit d'arrêter un service proprement ou encore d'en
comprendre le fonctionnement et l'organisation. Une propriété est spécialement
dédiée à cette tâche, il s'agît de DependentServices qui contient un tableau
d'instances de ServiceController, dont chacune représente en fait un service
dépendant. On peut, à partir de ces instances, récupérer des
informations sur chaque service dépendant, comme par exemple son nom, ou même
encore, les services dépendants de ce service dépendant (relisez la phrase
doucement, vous verrez, ce n'est pas si confus que cela !).
Voici un exemple permettant de lister les services dépendants, en quelques
lignes seulement :
Services dépendants d'un service |
Private Function GetDependentServices() As ServiceController()
Dim myService As New ServiceController()
myService.MachineName = "OSCAR"
myService.ServiceName = "winmgmt"
Return myService.DependentServices
End Function |
Et voici comment en afficher les noms dans la sortie du débogueur :
Afficher les services dépendants |
Public Sub DisplayDependentServices()
For Each Service As ServiceController In GetDependentServices()
Debug.WriteLine(Service.DisplayName)
Next
End Sub |
Après avoir vu comment interagir avec des services Windows existants, sur
une machine locale, ou distante, nous allons voir comment créer un service
Windows en .Net avec Visual Studio 2005.
3. Créer un service Windows avec .Net
Nous allons voir qu'il est possible de créer très facilement un service depuis Visual Studio
2005, nous verrons également qu'ils sont différents des applications Windows classiques.
3.1. Création d'un service Windows avec Visual Studio 2005
3.1.1. Création du service
Pour créer un service Windows avec Visual Studio 2005, vous allez voir que
c'est un jeu d'enfant, en effet notre IDE préféré intègre déjà un type
d'application "Service Windows" qui va en fait créer les bases d'un service,
avec le squelette de code nécessaire. Après avoir lancé Visual Studio 2005,
allez dans le menu "Fichier", "Nouveau", "Projet", puis dans la fenêtre
qui s'ouvre colone de gauche dans " Types de projets", "Visual Basic",
"Windows", sélectionnez "Service Windows" dans la colonne de droite, et
enfin saisissez le nom du projet que vous souhaitez. L'image ci-dessous
correspond à ce que vous devez avoir à l'écran :
Visual Studio crée alors un certain nombre de fichiers, dont le fichier Service1.vb
qui va contenir le code de notre service Windows. Voici le squelette par
défaut de ce fichier :
Service1.vb |
Public Class Service1
Protected Overrides Sub OnStart(ByVal args() As String)
End Sub
Protected Overrides Sub OnStop()
End Sub
End Class |
Vous voyez que par défaut il y à deux méthodes, OnStop() et OnStart() qui, vous
l'aurez compris, seront exécutées respectivement à l'arrêt et au démarrage
du service.
Le but de cet article n'étant pas de créer un service complexe, mais simplement de
voir comment en créer, notre service effectuera une tache simple, il écrira la date
et l'heure dans un fichier à chaque fois qu'il démarrera ou s'arrêtera. Pour atteindre
cet objectif hautement technique et complexe, nous allons ajouter une méthode à
notre classe Service1, il s'agit de la méthode WriteToFile(), en voici le code :
Méthode pour écrire dans un fichier. |
Private Sub WriteToFile(ByVal FileFolder As String, _
ByVal FileName As String, _
ByVal LineToAdd As String)
Dim FullFilePath As String = FileFolder & "\" & FileName
My.Computer.FileSystem.WriteAllText(FullFilePath, LineToAdd, True)
End Sub |
Il suffit d'appeler WriteToFile dans les méthodes OnStart() et OnStop(), et
à chaque fois que notre service démarrera et s'arrêtera, il écrira dans le
fichier texte, la date et l'heure. Voici les paramètres à passer à notre
méthode :
Dans OnStart():
Méthode OnStart() |
WriteToFile(Environment.SpecialFolder.Desktop.ToString, "MonPremierService.txt", "Démarrage :_
" & Date.Now.ToShortDateString & " -- " & Date.Today.ToShortTimeString) |
Dans OnStop():
Méthode OnStop() |
WriteToFile(Environment.SpecialFolder.Desktop.ToString, "MonPremierService.txt", "Arrêt :_
" & Date.Now.ToShortDateString & " -- " & Date.Today.ToShortTimeString) |
Le code complet de votre classe Service1.vb doit ressembler à ce qui suit :
Service1.vb |
Public Class Service1
Protected Overrides Sub OnStart(ByVal args() As String)
WriteToFile(Environment.SpecialFolder.Desktop.ToString, "MonPremierService.txt", "Démarrage :_
" & Date.Now.ToShortDateString & " -- " & Date.Today.ToShortTimeString)
End Sub
Protected Overrides Sub OnStop()
WriteToFile(Environment.SpecialFolder.Desktop.ToString, "MonPremierService.txt", "Arrêt :_
" & Date.Now.ToShortDateString & " -- " & Date.Today.ToShortTimeString)
End Sub
Private Sub WriteToFile(ByVal FileFolder As String, ByVal FileName As String, ByVal LineToAdd As String)
Dim FullFilePath As String = FileFolder & "\" & FileName
My.Computer.FileSystem.WriteAllText(FullFilePath, LineToAdd, True)
End Sub
End Class |
Ici, l'écriture du code du service à proprement parler est terminée,
il ne reste plus qu'à le "compiler". Mais nous allons voir qu'à ce
stade notre service n'est pas utilisable en l'état, il va falloir encore
ajouter un élément pour installer ce service sur une machine. Voyons
comment procéder.
3.1.2. Installation du service avec InstallUtil
Une fois de plus Visual Studio ne nous laisse pas tomber, et prévoit
le scénario relativement fréquent où, quand un développeur crée un service,
il souhaite l'installer. Il va falloir ajouter une autre classe à notre
service qui va contenir les données et paramètres d'installation.
Pour générer automatiquement une partie de ce code, il faut se
rendre dans le designer de Visual Studio 2005 pour notre fichier Service1.vb
(Visual Studio 2005 affiche alors dans l'onglet Service1.vb [Design]),
puis faire un clique droit dans la zone du designer, et sélectionner
"Ajouter le programme d'installation" comme nous le montre l'image
ci-dessous :
A ce stade, de nouveaux fichiers sont ajoutés à votre solution Visual Studio,
on voit apparaître ProjectInstaller.vb dans l'explorateur de solutions. Si
l'on affiche le code de fichier, voici ce que vous devez voir :
ProjectInstaller.vb |
Imports System.ComponentModel
Imports System.Configuration.Install
Public Class ProjectInstaller
Public Sub New()
MyBase.New()
InitializeComponent()
End Sub
End Class |
Vous pouvez voir que deux composants ont été déposé par le designer de Visual
Studio 2005, il s'agit d'un composant ServiceProcessInstaller et d'un composant
ServiceInstaller. Nous allons ici devoir ajouter un certain nombre d'informations
au code de la classe ProjectInstaller ci-dessus.
 |
Remarque : le code que nous allons ajouter ici pourrait être généré par Visual Studio,
car en fait il va s'agir de renseigner des propriétés des deux composants déposés par
le designer, mais pour que cela soit plus ludique nous allons le
"faire à la main".
|
Les informations que nous souhaitons ajouter sont en grande partie des informations
sur le service, comme par exemple celles qui s'affichent pour n'importe quel service
dans la console Services.msc que nous avons vu au début de cet article. Sans plus
attendre, le code :
Configuration du service |
ServiceProcessInstaller1.Account = ServiceProcess.ServiceAccount.LocalSystem
ServiceInstaller1.Description = "Service exemple, créer un fichier MonPremierService.txt sur le bureau."
ServiceInstaller1.DisplayName = "Developpez.com"
ServiceInstaller1.ServiceName = "DvpCom"
ServiceInstaller1.StartType = ServiceProcess.ServiceStartMode.Manual |
Si l'exécution de notre service dépend d'autres services on l'indique également
lors de l'installation, pour cela il existe la propriété ServicesDependedOn. On
lui attribue un tableau de String contenant les noms des services dont
dépend le service que nous souhaitons installer. Voici ci-dessous l'exemple d'un
service dépendant de deux services : Service1 et Service2.
Dépendances du service |
Dim serviceDependantOn() As String = Nothing
serviceDependantOn(0) = "Service1"
serviceDependantOn(1) = "Service2"
ServiceInstaller1.ServicesDependedOn = serviceDependantOn |
 |
Remarque : si lors de l'installation et du démarrage de notre service, les
services contenus dans le tableau de chaînes ne sont pas démarrés ils vont
être lancés, même s'ils sont en démarrage manuel.
|
Le code de votre classe ProjectInstaller doit donc ressembler à cela :
ProjectInstaller.vb |
Public Class ProjectInstaller
Public Sub New()
MyBase.New()
InitializeComponent()
ServiceProcessInstaller1.Account = ServiceProcess.ServiceAccount.LocalSystem
ServiceInstaller1.Description = "Service exemple, créer un fichier MonPremierService.txt sur le bureau."
ServiceInstaller1.DisplayName = "Developpez.com"
ServiceInstaller1.ServiceName = "DvpCom"
ServiceInstaller1.StartType = ServiceProcess.ServiceStartMode.Manual
End Sub
End Class |
Les propriétés du code un peu plus en détails :
ServiceAccount : c'est le compte qui va être utilisé pour exécuter le service.
Il existe quatre possibilités offrant une sécurité et des privilèges variables en
fonction des besoins. Vous pouvez même spécifier directement l'utilisateur que vous
souhaitez en fournissant son identifiant et son mot de passe. Voici un tableau issu
de la documentation MSDN Library vous montrant les possibilités qui vous sont
offertes :
Nom de membre |
Description |
LocalService |
Compte qui agit en tant qu'utilisateur non privilégié sur l'ordinateur local et présente des informations d'identification anonymes à n'importe quel serveur distant. |
LocalSystem |
Compte, utilisé par le Gestionnaire de contrôle des services, qui a des privilèges étendus sur l'ordinateur local et agit comme l'ordinateur sur le réseau. |
NetworkService |
Compte qui fournit des privilèges locaux extensifs et présente les informations d'identification de l'ordinateur à n'importe quel serveur distant. |
User |
Compte défini par un utilisateur spécifique sur le réseau. Lorsque User est spécifié pour le membre ServiceProcessInstaller.Account, le système demande un nom d'utilisateur et un mot de passe valides lorsque le service est installé, à moins que vous ne définissiez des valeurs pour les propriétés Username et Password de votre instance de ServiceProcessInstaller. |
Description : il s'agît de la description du service, ce texte apparaitra dans la
console services.msc lorsqu'un utilisateur regardera les propriétés de votre service.
Attention : cette propriété est une nouveauté du Framework .Net 2.0. pour avoir une
finalité équivalente avec des versions antérieures de .Net vous devez directement
éditer la bonne clé dans la base de registre.
DisplayName : il s'agit du nom complet du service, choisissez un nom explicite.
Il peut comporter des espaces si nécessaire.
ServiceName : le nom de service est le nom employé par Windows en interne, il doit
être simple et explicite.
StartType : c'est le type de démarrage du service : manuel, automatique ou désactivé.
Une fois tout cela terminé vous devez générer votre projet dans Visual Studio 2005 en mode
Release (puisque le développement de votre service est terminé). L'exécutable de votre
service se trouve donc dans le répertoire \Bin\Release\ de votre projet de service
Windows. Déplacez alors cet exécutable vers son emplacement définitif, il ne reste alors
plus qu'à employer l'utilitaire InstallUtil pour installer correctement votre service
Windows.
En effet, le Framework .Net (1.1 ou 2.0) fournit un petit utilitaire nommé InstallUtil
qui se charge de créer les clés nécessaires dans le registre pour faire en sorte que
votre application devienne un véritable service Windows qui apparaîtra dans la console
Services.msc. Cet utilitaire est en ligne de commande, il faut procéder comme suit
pour installer un service Windows :
Démarrer l'invite de commande de Visual Studio 2005, ou alors avec l'invite de commande
Windows déplacez vous jusqu'au répertoire C:\WINDOWS\Microsoft.NET\Framework\votre_Framework
où se trouve InstallUtil.exe. Saisissez alors la ligne suivante :
InstallUtil chemin_complet_de_l'exécutable_du_service
Par exemple : InstallUtil c:\monServiceDeveloppez.exe
Si tout se passe bien vous devez voir s'afficher les informations suivantes
dans votre invite de commande :
Invite de commande |
Microsoft (R) .NET Framework Installation utility Version 2.0.50727.42
Copyright (c) Microsoft Corporation. Tous droits réservés.
Exécution d'une installation traitée avec transaction.
Début de la phase d'installation de l'installation.
Consultez le contenu du fichier journal pour l'avancement de l'assembly c:\monServiceDeveloppez.exe.
Le fichier se trouve à c:\monServiceDeveloppez.InstallLog.
Installation de l'assembly 'c:\monServiceDeveloppez.exe'.
Les paramètres affectés sont :
logtoconsole =
assemblypath = c:\monServiceDeveloppez.exe
logfile = c:\monServiceDeveloppez.InstallLog
Installation du service DvpCom en cours...
Le service DvpCom a été installé avec succès.
Création d'une source EventLog DvpCom dans le journal Application...
La phase d'installation est terminée et la phase de validation a commencé.
Consultez le contenu du fichier journal pour l'avancement de l'assembly c:\monServiceDeveloppez.exe.
Le fichier se trouve à c:\monServiceDeveloppez.InstallLog.
Validation de l'assembly 'c:\monServiceDeveloppez.exe'.
Les paramètres affectés sont :
logtoconsole =
assemblypath = c:\monServiceDeveloppez.exe
logfile = c:\monServiceDeveloppez.InstallLog
La phase de validation est terminée.
L'installation traitée avec transaction est terminée. |
L'installation de votre service Windows est terminée, et vous le voyez désormais
apparaître dans la console Services.msc, essayez de le démarrer et de l'arrêter
quelque fois, puis allez voir le contenu du fichier MonServiceDeveloppez.txt à
la racine du disque C, il doit contenir des lignes indiquant le démarrage et
l'arrêt du service.
 |
Remarque : le processus d'installation d'un service Windows consiste en fait
simplement en la création de clé dans le registre contenant les informations
et paramètres du service, ainsi si vous le souhaitez vous pouvez vous passer
de l'utilitaire InstallUtil et faire tout vous-même, mais pourquoi vouloir
absolument réinventer la roue ? Les clés créées dans le registre Windows se
trouvent à cet emplacement HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\nom_du_service.
|
Pour désinstaller le service, l'utilitaire InstallUtil est également votre allié,
il suffit de saisir la commande suivante :
InstallUtil /U chemin_complet_de_l'exécutable_du_service
Par exemple : InstallUtil /U c:\monServiceDeveloppez.exe
Comme lors de l'installation, des informations d'installation sont affichées dans
l'invite de commande et vous confirment la désinstallation du service. Attention
cependant, même après la désinstallation du service, l'exécutable de votre service
se trouve toujours à son emplacement sur le disque dur, la désinstallation correspond
à la désinscription de l'application en tant que service Windows.
 |
Remarque : le fait de devoir utiliser l'invite de commande de Visual Studio ou de Windows
n'est pas à la portée de l'utilisateur standard, et peut en rebuter plus d'un. Pour
contourner ce problème n'oubliez pas que vous pouvez créer des fichiers batch contenant
les commandes nécessaires pour installez (on devrait plutôt dire "enregistrer") votre
application en tant que service Windows. Ci-dessous, un exemple simpliste et non
robuste (pas de gestion des erreurs, pas de détection de la version du Framework)
d'un fichier bat :
|
Pour installer un service
Installation d'un service |
ECHO Installation du service Developpez.com...
ECHO OFF
SET ExeService="C:\monServiceDeveloppez.exe"
SET ToolPath=%WINDIR%"\Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe"
ECHO Installation du service %ExeService% en cours...
%ToolPath% %ExeService% |
Pour désinstaller un service
Désinstallation d'un service |
ECHO Suppression du service Developpez.com...
ECHO OFF
SET ExeService="C:\monServiceDeveloppez.exe"
SET ToolPath=%WINDIR%"\Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe"
%ToolPath% /U %ExeService% |
 |
Remarque : créez deux fichiers (install.bat et uninstall.bat par exemple)
que vous exécuterez respectivement lors de l'installation et de la
désinstallation du service. Ainsi, vous automatiserez la configuration du service
Windows. De plus, je vous rappelle aussi la commande Net Start nom_du_service
qui permet de démarrer un service Windows depuis la ligne de commande, cela
pourrait s'avérer utile si vous souhaitez démarrer le service Windows sans
intervention de l'utilisateur. La commande Net Stop nom_du_service quand à
elle permet d'arrêter un service Windows.
|
Voilà, j'espère vous avoir fait découvrir suffisamment en détails comment créer
et installer un service Windows avec .Net et InstallUtil. Passons maintenant à une technique beaucoup
plus professionnelle dans son fonctionnement, je veux parler des packages Windows Installer (MSI).
3.1.3. Installation du service avec un package MSI
La solution précédente peut convenir dans de nombreux cas, mais dans des situations
de déploiements à grande échelle, par GPO (Active Directory), où même lorsque les utilisateurs
doivent réaliser cette tâche eux-mêmes, la relative complexité peut bloquer certains personnes.
Il faut aussi dire que la solution proposée ci-dessous n'est pas forcément très esthétique et dans
les règles de l'art. C'est pour cela que nous allons voir ici comment créer un projet d'installation
et de déploiement dans Visual Studio pour générer un installer MSI.
Tout d'abord, il est important à mon avis de décrire rapidement ce qu'est un package MSI, c'est loin d'être un simple
exécutable pour installer une application, un service ou autre... C'est un package qui possède des fonctions
avancées : installation, réparation, modification et suppression; et, qui plus est, est entièrement paramètrable et personnalisable,
permet de génerer des logs d'installation, de réaliser tout cela de manière interactive ou totalement silencieuse,
ce qui en fait le partenaire idéal des déploiements automatisés et/ou à grande échelle. La technologie MSI est
présente en standard dans les OS de Microsoft depuis la version Millenium de Windows. Elle s'appuie sur un service
nommé "Windows Installer" dont l'exécutable Msiexec.exe va gérer l'action des packages MSI. Il faut donc voir un fichier
MSI comme un conteneur, qui va empaqueter une application, et qui va entièrement gérer les grandes étapes de "sa vie" que
sont l'installation, la modification, la réparation et la suppresion. Je ne pense pas qu'il soit utile d'entrer plus avant
dans la description des packages MSI, même si bien trop de développeurs sous-estiment fortement leur potentiel, qui est d'ailleurs
l'objet de nombreux ouvrages.
Nous allons voir maintenant comment générer ce fameux package MSI. Tout d'abord, je tiens à préciser que de nombreux logiciels
permettent de créer des installer MSI, et que cela va de l'outil en ligne de commande, à l'atelier complet de repackaging de
logiciel tel "Wise Studio" pour n'en citer qu'un des plus connu. Ici, j'utiliserai l'outil qui nous semble évident dans le cadre de
ce tutoriel, je veux bien évidemment parler de Visual Studio 2005, qui possède en la matière des possibilités très importantes, et
qui couvriront nombres de scénarios parmis les plus fréquents.
Pour créer notre Installer nous allons repartir de la fin de "3.1.1 Création du service", cela signifie que notre service est terminé,
et que nous devons maintenant le paramètrer de sorte qu'il puisse être exécuté par Windows. Tout d'abord, il faut se rendre dans le
designer de Visual Studio 2005 pour notre fichier Service1.vb (Visual Studio 2005 affiche alors dans l'onglet Service1.vb [Design]),
puis faire un clique droit dans la zone du designer, et sélectionner "Ajouter le programme d'installation" comme nous le montre
l'image ci-dessous :
Comme signalé un peu plus haut dans cet article, cette action ajoute de nouveaux fichiers à la solution, et celui qui nous
intéresse maintenant se nomme ProjectInstaller.vb, c'est ici que nous allons paramétrer notre service. Comme j'ai déjà expliqué
tout cela plus haut je n'entrerai pas dans les détails, voici donc à quoi doit ressembler le contenu de ce fichier :
ProjectInstaller.vb |
Public Class ProjectInstaller
Public Sub New()
MyBase.New()
InitializeComponent()
ServiceProcessInstaller1.Account = ServiceProcess.ServiceAccount.LocalSystem
ServiceInstaller1.Description = "Service exemple, créer un fichier MonPremierService.txt sur le bureau."
ServiceInstaller1.DisplayName = "Developpez.com"
ServiceInstaller1.ServiceName = "DvpCom"
ServiceInstaller1.StartType = ServiceProcess.ServiceStartMode.Manual
End Sub
End Class |
C'est maintenant que nous allons à proprement parler faire en sorte de générer un installer MSI. Pour cela, placez
vous sur la solution courante dans l'explorateur de solutions, puis faites "Ajouter", "Nouveau projet", "Configuration
et déploiement" et enfin "Projet d'installation", ici je lui donnerai le nom "MonSetup".
Cliquez sur Ok, un nouveau projet est ajouté à votre solution. A ce moment là, l'éditeur "Système de fichiers" s'ouvre.
En sélectionnant MonSetup dans l'explorateur de solution, vous avez accès à de nombreuses propriétés que évidemment
vous pouvez configurer depuis la fenêtre du même nom. Je vous laisse parcourir et découvrir par vous-même ces propriétés.
Retournez maintenant dans l'éditeur "Système de fichiers" qui s'est ouvert lors de l'ajout du projet d'installation à
notre solution. Il est comporte deux colonnes, et dans celle de gauche se trouve trois dossiers. Sélectionnez le deuxième
"Dossier de l'application", puis, faitez un clique droit, "Ajouter" et enfin "Sortie de projet". Normalement vous n'avez rien
à modifier dans la fenêtre qui s'ouvre, elle doit être conforme à l'image ci-dessous :

Cliquez sur "Ok". Nous venons ici de signaler à notre package MSI qu'il devra contenir l'application "MonServiceDevelopez",
ainsi elle sera gérée par cet Installer. On voir apparaître dans l'explorateur de solution la sortie principale qui correspond
au service à installer, et de plus nous voyons qu'une dépendance a été détectée, celle au Framework .Net, puisque évidemment notre
service est codé en .Net il faudra que le Framework soit installé pour qu'il puisse s'éxécuter. Cette dépendance signifie que
l'installeur vérifiera la présence du Framework avant de procéder à l'installation du service. La version du Framework à détecter, ainsi
que l'action à effectuer en fonction de cette condition sont paramètrables depuis l'éditeur de "Coniditions de lancement" disponible
depuis "Afficher", "Editeur". Par défaut la version minimale du Framework est bien évidemment celle qui a été utilisée pour créer
notre service. Sur ma machine cette version est la 2.0.50727, et le MSI est paramétré pour faire en sorte de n'accepter que cette
version précise, ce qui est logique pour une application .Net.
En l'état notre service va s'installer sur la machine, mais ne sera pas exécuté et paramétré. Pour cela, il faut ajouter une
action personnalisée (ou "Custom action") à notre projet de déploiement. C'est d'ailleurs à partir de ces actions
que nous pouvons très finement personnaliser un installer MSI, mais ici rien de cela, nous voulons juste un installer "de base".
Allez dans le menu "Affichage", "Editeur", "Actions personalisées". S'affiche alors l'éditeur d'actions personalisées qui contient
les quatres grandes étapes qui vont être gérée. En haut, dans le menu "Action", sélectionnez "Ajouter une action personnalisée...",
puis dans la liste "Regardez dans", choisissez "Dossier de l'application", sélectionnez "Sortie principale de MonServiceDeveloppez
(Actif)". Cliquez sur "Ok". Dans l'éditeur d'actions personnalisées, vous voyez qu'une action a été ajoutée pour chacune des quatres
étapes.
Voilà, notre Installer est prêt, enfin presque, il ne reste plus qu'à le générer, pour cela il faut aller
dans le menu du même nom, puis choisir "Générer MonSetup". La génération démarre, et dans le dossier release ou
setup, en fonction du mode dans lequel vous êtes, va être créé un fichier MonSetup.msi et un fichier Setup.exe.
Lancez le Setup.exe, qui exécutera alors le package MSI MonSetup.msi. Vous seront alors demandés un certain nombre de renseignements,
comme par exemple, pour qui installer l'application, et à quel emplacement. Une fois l'installation terminée, lancez la console "services.msc" depuis "Démarrer", "Exécuter", notre
service Developpez.com a été correctement installé et paramétré. Exécutez de nouveau Setup.exe, cette fois-ci, il vous propose
de réparer ou de supprimer le service.

J'espère vous avoir montré à quel point installer un service est aisé avec cette méthode. Bien évidemment nous avons
à peine survolé l'utilisation des packages MSI, mais gardez à l'esprit qu'ils sont très puissants et permettent de
couvrir de nombreux scénarios, ainsi que de personnaliser fortement un processus d'installation.
3.2. Déboguer un service Windows avec Visual Studio 2005
Une chose qui, à première vue, peut sembler complexe est le débogage de
service Windows. Mais vous allez voir que cela est beaucoup plus simple qu'il
n'y parait. Tout d'abord, pour pouvoir déboguer votre service Windows, vous devez
l'avoir installé comme je l'ai expliqué un peu plus haut. De plus, lors de la
génération du service, vous devez être en mode Debug, sans cela,
déboguer une application générée en mode Release n'a pas de sens en soi.
Voici comment déboguer un service Windows grâce à Visual Studio 2005.
- Ouvrez la solution contenant votre projet dans Visual Studio, de manière à disposer des sources de votre service.
- Placez maintenant, ou plus tard les points d'arrêt et les autres éléments vous permettant d'effectuer votre débogage.
- Vérifiez que le service est en cours d'éxecution sur votre machine.
- Dans le menu Déboguer de Visual Studio, cliquez sur " Attacher au processus ". Notre service utilisant le compte local système, il est normal qu'il n'apparaisse pas dans la liste des processus utilisant votre compte utilisateur, pensez donc, si besoin est, à cocher la case " Afficher les processus de tous les utilisateurs ", puis à cliquer sur le bouton " Actualiser ". Dès lors, le processus monServiceDeveloppez.exe correspondant à notre service se trouve dans la liste. Sélectionnez-le, puis cliquez sur le bouton " Attacher "
Vous pouvez maintenant librement profiter des capacités de débogage de Visual Studio 2005,
même sur un service Windows que vous venez de créer. Pour faciliter le débogage, n'oubliez
pas d'utiliser la console services.msc pour forcer le démarrage, l'arrêt, la
pause ou tout autre évènement sur votre service à déboguer.
 |
Remarque : en procédant de la sorte vous devez gardez à l'esprit que vous ne pouvez pas déboguer
le code se trouvant dans le OnStart() puisque, pour attacher le processus du service, celui-ci
doit être déjà démarré et, par conséquent, avoir exécuté le code du OnStart(). Pour contourner ce problème il existe
plusieurs manières de procéder, mais qui sont un peu du "bricolage", qui consiste à faire ré-exécuter
le code du OnStart() une fois le processus attaché, ou encore à déplacer le code du OnStart()
pour avoir le temps d'attacher le processus au débogueur. Une autre solution un peu plus "propre" est de placer
le code du service dans un thread séparé, puis d'appeller ce thread depuis le OnStart() du service, il suffit
alors d'appeller le débogueur depuis le code (avec la classe Debugger du namespace Systeme.Diagnostics), et il
faut aussi penser à positionner un point d'arrêt dans le code, sinon l'exécution du code ne sera pas suspendue.
Merci à Abelman pour cette dernière solution.
|
Vous l'aurez compris, ce qui pose problème dans le débug des services c'est le code du OnStart(), mais il y
a toujours moyen de ruser pour y arriver. Quand au reste du code, une fois le débogueur lancé, cela devient du
débogage standard.
Ressources
Conclusion
Au cours de cet article, nous avons vu que gérer un service Windows avec .Net et Visual Studio
2005 ne présente pas de difficulté particulière, que ce soit en local ou même en distant. De
plus, la création de services Windows est un jeu d'enfant grâce aux modèles d'applications
intégrés dans notre IDE préféré. Le débogage et l'installation de services réalisés avec .Net
sont facilités, respectivement par les capacités de VS 2005 et la simplicité de l'utilitaire
InstallUtil du Framework .Net ou même la création d'
Installer Windows (MSI). J'espère qu'au
travers de cet article vous aurez compris le fonctionnement et la spécificité des services
Windows, et que désormais, vous n'hésiterez plus à les mettre en œuvre avec vos
applications .Net.
Un grand merci à
Khany pour la relecture de cet article, ainsi
qu'à l'équipe .Net pour son aide.


Les sources présentées sur cette page sont libres de droits
et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation
constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ©
2006 Ronald Vasseur. Aucune reproduction, même partielle, ne peut être faite
de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation
expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et
jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.