jeudi 15 juillet 2010

Contrôler l'arrêt d'un service Windows perso (.NET)

Lorsqu'on développe un service Windows, il est essentiel de contrôler le moment où le service s'arrêtera surtout lorsqu'il s'agit d'un traitement en lot. Vous ne voudriez probablement pas que votre service s'arrête en plein traitement, car un utilisateur à lancer la commande net stop votre_service.

Prenons par exemple un service qui roule en continu et qui vérifie toutes les 15 secondes les enregistrements qui ont été ajoutés à une base de données afin de pouvoir les traiter. Dans cet exemple, le service ne doit pas s'arrêter tant qu'il n'a pas fini de traiter les nouveaux enregistrements.

Seul le code servant au contrôle du service sera présenté dans cette publication

Voici le code du service ServiceData (ServiceData.cs)


public partial class ServiceData : ServiceBase
{
Thread dataProcessingThread = null;

public ServiceData()
{
InitializeComponent();
}

protected override void OnStart(string[] args)
{
dataProcessingThread = new Thread(DataProcessing.ProcessPendingData);
dataProcessingThread.Start();
}

protected override void OnStop()
{
DataProcessing.ResetEvent.Set();
while (dataProcessingThread.IsAlive)
{
this.RequestAdditionalTime(5000);
Thread.Sleep(5000);
}
}
}


Lors du démarrage du service, nous lançons, dans l'évènement OnStart, un thread qui va s'exécuter tout au long du service. Nous verrons la classe DataProcessing un peu plus loin... Regardons tout d'abord l'arrêt du service. Dans la fonction OnStop la commande
DataProcessing.ResetEvent.Set();
sert à déclencher l'évènement d'arrêt du service. La boucle while sert ensuite à attendre que les enregistrements restant à traiter se terminent. La commande suivante
this.RequestAdditionalTime(5000);
sert à avertir le Service Control Manager (SCM) que le service requiert 5 secondes de plus pour s'arrêter. Si nous omettons cette commande, le SCM croira que le service ne répond plus. Cette boucle vérifie en fait à tout les 5 secondes si le travail du thread est terminé.


Voici le code de la classe DataProcessing (DataProcessing.cs)


public static class DataProcessing
{
private static ManualResetEvent resetEvent = new ManualResetEvent(false);
public static ManualResetEvent ResetEvent
{
get { return resetEvent; }
set { resetEvent = value; }
}

public static void ProcessPendingData()
{
do
{
//All stuff here will not be interrupted until they finish
}
while (!ResetEvent.WaitOne(15000));
}
}


Le code suivant
private static ManualResetEvent resetEvent = new ManualResetEvent(false);
public static ManualResetEvent ResetEvent
{
get { return resetEvent; }
set { resetEvent = value; }
}

définit le ResetEvent qui sert à avertir le thread qu'un événement est survenu.
public static void ProcessPendingData()
{
do
{
//All stuff here will not be interrupted until they finish
}
while (!ResetEvent.WaitOne(15000));
}
La méthode statique ProcessPendingData sert à traiter les enregistrements. Tout ce qui se trouve à l'intérieur du do-while ne sera pas interrompu par un arrêt de service.
Donc la boucle va s'exécuter toutes les 15 secondes tant et aussi longtemps qu'une demande d'arrêt n'a pas été formulée. Votre lot d'enregistrements pourra donc être traité sans être interrompu!