GERBELOTBARILLON.COM

Parce qu'il faut toujours un commencement...

Ecrire un service Windows en C#

Utilisation de Microsoft Visual Studio Express

L'écriture d'un service pour Windows ne doit plus être réservée aux possesseurs de Visual Studio Professionnel. Il est tout à fait envisageable d'écrire un service sans forcément requérir à un template trouvable uniquement dans un produit onéreux...

Il y a deux idées principales à suivre dans la création d'un service :

InstallUtil ne fait que regarder dans prog_service.exe les classes qui disposent de l'attribut [RunInstaller(true)] pour mettre en place le service dans l'environnement Windows.

Exemple de service simple

Nous allons commencer par créer un projet vide depuis Visual Studio Express. Nous lui donnons le nom de SimpleService pour ne pas faire trop compliqué...

Ajoutez ensuite une feuille de code en cliquant droit sur le projet SimpleService et en faisant Ajouter > Nouvel élément..., vous choisissez Code > Feuille de code.

Donnez-lui le nom que vous souhaitez. Pour ma part ce sera Program.cs.

Afin d'utiliser les différentes méthodes requises pour la mise en place d'un service, il faut ajouter des références au projet et les inclure dans le programme. Pour cela, commencer par faire un clic droit sur Project > Ajouter une référence...

Ajoutez les références System, System.ServiceProcess et System.Configuration.Install. Dans le code Program.cs, ajoutez les directives d'inclusion suivantes :

using System;
using System.ServiceProcess;

Un service est conceptuellement relativement simple. Il dispose de méthodes permettant de réagir :

Ce sont des méthodes mises à disposition par la classe mère ServiceBase qu'il faudra surcharger comme suit :
protected override void OnStart(string[] args)
{
// Le code vient ici
}
protected override void OnStop()
{
}
...
Ces méthodes sont appelées par le gestionnaire de services (le Service Control Manager ou SCM).

Nous avons vu précédemment qu'un service dérive de la classe ServiceBase. Nous allons pouvoir simplement définir quelques propriétés également lors de l'exécution du constructeur de la classe, notamment son nom, tel qu'il va apparaître dans la liste des services de Windows, ainsi que le comportement possible autorisé (arrêt, démarrage, pause, ...). Enfin, le seul point d'entrée du service pour son son exécution est une simple méthode Main() qui va utiliser la méthode Run() de sa classe parente pour créer le lancement du service. Ci-dessous un exemple complet de service de base.

using System;
using System.ServiceProcess;

namespace SimpleService
{
    public class MyService : ServiceBase
    {
        // Constructeur de la classe de service dans lequel nous précisons des propriétés
        // immédiates du comportement du service
        public MyService()
        {
            // Le nom du service tel qu'il va être affiché dans la liste des services Windows
            this.ServiceName = "SimpleService";
            // Le service peut être arrêté une fois démarré
            this.CanStop = true;
            // Le service ne peut pas être mis en pause
            this.CanPauseAndContinue = false;
            // Affiche les messages de changement d'état du service dans le journal des événements Windows
            this.AutoLog = true;
        }

        protected override void OnStart(string[] args)
        {
            base.OnStart(args);
        }

        protected override void OnStop()
        {
            base.OnStop();
        }

        // La classe Main est la seule classe requise pour démarrer le service. Il faut se rappeler
        // qu'un service ne dispose généralement pas d'une interface graphique mais est plutôt orienté
        // pour un usage purement console.
        public static void Main(string[] args)
        {
            ServiceBase.Run(new MyService());
        }
    }
}

Installer le service

Nous venons de terminer la conception du service mais ne pouvons encore pas nous en servir car il faut l'installer dans le système. Pour cela, nous devons ajouter une classe au projet qui va être lue par le programme InstallUtil disponible dans le dossier du Framework sous Windows.

Afin de pouvoir utiliser la direction d'installation, incluez l'assembly contenant RunInstaller en faisant using System.ComponentModel avec les autres déclarations.

// Il est important que la classe qui sert de base à l'installeur soit publique
// Elle doit dériver de la classe ServiceInstaller.
[RunInstaller(true)]
public class MyInstaller : Installer
{
	private ServiceProcessInstaller processInstaller;
	private ServiceInstaller serviceInstaller;

	public MyInstaller()
	{
		processInstaller = new ServiceProcessInstaller();
		serviceInstaller = new ServiceInstaller();

		// Affectation des privilèges d'installation
		processInstaller.Account = ServiceAccount.LocalSystem;

		serviceInstaller.DisplayName = "MyService";
		serviceInstaller.StartType = ServiceStartMode.Manual;

		// Le nom défini ici doit être le même que celui déclaré dans le corps du service
		serviceInstaller.ServiceName = "MyService";

		this.Installers.Add(processInstaller);
		this.Installers.Add(serviceInstaller);
	}
}

C'est le strict minimum à faire pour gérer les options d'installation du service. Les deux classes ServiceProcessInstaller et ServiceInstaller sont responsables de l'installation du service. ServiceProcessInstaller prend en charge l'information commune à tous les services. ServiceInstaller met en place les informations spécifiques à ce service.

La propriété Account indique le niveau de privilèges utilisés pour l'installation et le fonctionnement du service. Par défaut, c'est User (Utilisateur) mais dans ce cas, nous devons préciser un nom et un mot de passe pour valider le fonctionnement. En choisissant LocalSystem, nous utilisons un compte avec beaucoup (trop ?) de privilèges pour le fonctionnement du service.

Les services sont identifiés par un nom. C'est pourquoi il est important que les deux classes - le service et l'installeur - disposent du même nom. Ainsi la valeur de ServiceInstaller.ServiceName doit correspondre à celle de this.DisplayName définie dans le constructeur de la classe du service.

Il ne reste plus alors qu'à ajouter nos deux installeurs à la liste des installeurs de la collection Installers.

Un service est un mécanisme particulier de fonctionnement d'un programme puisqu'il ne peut pas être exécuté depuis l'IDE Visual Studio. Il est fortement conseillé de générer l'exécutable du projet en mode Release mais pas en mode Debug.

Pour installer le service, l'outil InstallUtil doit être utilisé. Il est normalement disponible pour toutes les versions du Framework.NET. Par expérience, utilisez la version qui se trouve dans le dossier de la version majeure du .NET que vous utilisez. En d'autres termes, si vous utilisez une version 4.x du Framework .NET, utilisez le programme InstallUtil.exe qui se trouve dans le dossier \Windows\Microsoft.NET\Framework\V4.x.y.

La syntaxe d'InstallUtil.exe est relativement simple et correspond à InstallUtil.exe [/u] nom_du_service.exe. Le commutateur /u est utilisé pour désinstaller le service. Il semble très important de lancer l'installation du service depuis une commande MS-DOS en mode Administrateur.

Une certaine quantité d'information devrait vous être retournée par la sortie du programme InstallUtil.exe. Ci-après un exemple de ce que vous pourriez obtenir.


Running a transacted installation.

Beginning the Install phase of the installation.
See the contents of the log file for the C:\SimpleService\SimpleService\bin\Release\SimpleService.exe assembly's progress.
The file is located at C:\SimpleService\SimpleService\bin\Release\SimpleService.InstallLog.

An exception occurred during the Install phase.
System.Security.SecurityException: The source was not found, but some or all event logs could not be searched.  Inaccessible logs: Security.

The Rollback phase of the installation is beginning.
See the contents of the log file for the C:\SimpleService\SimpleService\bin\Release\SimpleService.exe assembly's progress.
The file is located at C:\SimpleService\SimpleService\bin\Release\SimpleService.InstallLog.

The Rollback phase completed successfully.

The transacted install has completed.

Running a transacted installation.

Beginning the Install phase of the installation.
See the contents of the log file for the C:\SimpleService\SimpleService\bin\Release\SimpleService.exe assembly's progress.
The file is located at C:\SimpleService\SimpleService\bin\Release\SimpleService.InstallLog.

The Install phase completed successfully, and the Commit phase is beginning.
See the contents of the log file for the C:\SimpleService\SimpleService\bin\Release\SimpleService.exe assembly's progress.
The file is located at C:\SimpleService\SimpleService\bin\Release\SimpleService.InstallLog.

The Commit phase completed successfully.

The transacted install has completed.
			

Une fois le service installé, il est disponible dans la liste des services de Windows accessibles depuis le gestionnaire services.msc

Ressources