Ecrire un service Windows, un vrai, celui qui se manipule avec les commandes usuelles de Windows et qui se retrouve dans la mmc services.msc de Windows n'est pas toujours aisé si l'on n'utilise pas du C# ou du C++. Pour des raisons d'efficacité et de rapidité de mise en place, utiliser Python apporte une véritable valeur ajoutée et lui adjoindre la bibliothèque pywin32 en fait un candidat de choix pour l'écriture d'outils pour Windows.
Si vous ne disposez pas déjà des packages requis :
Un service avec Python est composé d'une classe décrivant le programme et les actions qu'il effectue. Par défaut, un service est capable de passer par plusieurs états qui sont généralement Started, Stopped, Running, Pending, Enabled, Disabled... Un exemple de classe de base peut être celle-ci :
class MyService:
"""Ne fait rien de particulier à part disposer de méthodes basiques"""
def stop(self):
"""Arrêt du service"""
self.running = False
def run(self):
"""Démarrage du service et boucle principale. Tout se passe ici."""
self.running = True
while self.running:
time.sleep(10) # Un peu de temps pour les autres et accessoirement l'emplacement où vont se dérouler les opérations
servicemanager.LogInfoMsg("Service running...")
Ce morceau de code servira de base de classe pour déclarer les services :
class MyServiceFramework(win32serviceutil.ServiceFramework):
_svc_name_ = 'MyService'
_svc_display_name_ = 'My Service display name'
def SvcStop(self):
"""Commande d'arrêt du service"""
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.service_impl.stop()
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
def SvcDoRun(self):
"""Lance le démarrage du service. Ne rend pas la main tant que le service est en route."""
self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
self.service_impl = MyService()
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
# Run the service
self.service_impl.run()
Pour que le service puisse être géré il faut inclure un certain nombre de bibliothèques :
import time # pour la gestion du temps d'attente notamment
import win32serviceutil # ServiceFramework et helper de commandline
import win32service # Gestion des événements relatifs aux services
import servicemanager # Setup et logging du service
...
...
...
# Fonction appelée en mode stand-alone
# Si argv == 1 cela veut dire que le programme souhaite exécuter le Service Dispatcher pour
# permettre à Windows de gérer le service. S'il y a d'autres arguments, alors on considère que ce sont des
# arguments pour générer une action sur le service. Dans tous les cas le paramètre MyServiceFramework est passé
# ServiceManager va interagir avec le SCM de Windows pour Service Control Manager
# Wind32ServiceUtil va gérer les arguments de la ligne de commande.
# pour que la classe soit disponible.
def init():
if len(sys.argv) == 1:
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(MyServiceFramework)
servicemanager.StartServiceCtrlDispatcher()
else:
win32serviceutil.HandleCommandLine(MyServiceFramework)
if __name__ == '__main__':
init()
La transformation du script de service python en fichier exécutable Windows va se faire assez facilement par l'intermédiaire de l'outil pyinstaller. La commande globale sera la suivante :
pyinstaller --runtime-tmpdir=. --onefile --hidden-import win32timezone myservice.py
Les options sont les suivantes :
Le plus simple est d'ouvrir une commande MS-DOS en mode Administrateur. Il faut absolument disposer de privilèges élevés pour installer des services...
Sources documentaires :