Man braucht es ab und zu. Ein Python-Programm das als Windows-Dienst (=Service) gestartet werden kann. Es ist auch gar nicht so schwer, wie ich mir am Anfang gedacht habe.
Installieren als Dienst war kein Problem -- aber das Starten des Dienstes hat mich ein paar gesunde Haare und Nerven gekostet. Es hat mich so um die fünf Stunden aufgehalten, bis ich raus gefunden hatte, dass ein Python-Modul, das als Dienst gestartet werden soll, NICHT in einem Netzlaufwerk liegen darf.
Da ich meine komplette Entwicklungsumgebung in einem Netzlaufwerk habe, trifft mich dieser Umstand doppelt.
Ein Python-Modul, das als Dienst gestartet wird, arbeitet in einer unkonfigurierten Umgebung. Das heißt, dass keine Umgebungsvariablen gesetzt sind und auch keine Netzlaufwerke gemappt sind. Wenn man Umgebungsvariablen braucht, dann muss man sich diese im Python-Modul selbst erstellen.
Aber jetzt zum Wesentlichen. Zuerst braucht man pywin32 von Marc Hammond. Ohne pywin32 wäre die Sache ein Pfusch.
Man könnte z.B. mit Cygwin und dessen Programm ``cygrunsrv`` einen Dienst erstellen und starten, aber man könnte im Programm nicht einfach auf eine STOP-Anweisung reagieren. Das ist also keine gute Option wenn man Aufräumarbeiten durchführen muss. Außerdem wird dazu ein installiertes Cygwin benötigt. Die Installation von Cygwin kann man auch nicht jedem Kunden zumuten...
Man könnte einen Dienst auch mit den Programmen ``instsrv.exe`` und ``srvany.exe`` zum Laufen bringen. Diese Programme sind im "Win2K Resource Kit" mit dabei. Das ist sicher eine Option wenn es sich nur um einen einfachen Dienst handeln soll, der keine Aufräumarbeiten braucht und bei dem es auch egal ist, wenn er mal länger als erwartet läuft. Außerdem erspart man sich dadurch die Installation von Cygwin. (Obwohl kein Windows ohne Cygwin sein sollte.

Siehe auch: http://www.mischiefbox.com/blog/?p=242
Aber wie schon geschrieben -- mit pywin32 hat man mehr Kontrolle und so schwer ist es auch nicht, einen Dienst zu schreiben.
So sieht Mark Hammond den kleinsten Windows-Dienst:
Code: Alles auswählen
# SmallestService.py
#
# A sample demonstrating the smallest possible service written in Python.
import win32serviceutil
import win32service
import win32event
class SmallestPythonService(win32serviceutil.ServiceFramework):
_svc_name_ = "SmallestPythonService"
_svc_display_name_ = "The smallest possible Python Service"
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
# Create an event which we will use to wait on.
# The "service stop" request will set this event.
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
def SvcStop(self):
# Before we do anything, tell the SCM we are starting the stop process.
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
# And set my event.
win32event.SetEvent(self.hWaitStop)
def SvcDoRun(self):
# We do nothing other than wait to be stopped!
win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
if __name__=='__main__':
win32serviceutil.HandleCommandLine(SmallestPythonService)
Code: Alles auswählen
Usage: 'SimpleXmlrpcService.py [options] install|update|remove|start [...]|stop|restart [...]|debug [...]'
Options for 'install' and 'update' commands only:
--username domain\username : The Username the service is to run under
--password password : The password for the username
--startup [manual|auto|disabled] : How the service starts, default = manual
--interactive : Allow the service to interact with the desktop.
--perfmonini file: .ini file to use for registering performance monitor data
--perfmondll file: .dll file to use when querying the service for
performance data, default = perfmondata.dll
Options for 'start' and 'stop' commands only:
--wait seconds: Wait for the service to actually start or stop.
If you specify --wait with the 'stop' option, the service
and all dependent services will be stopped, each waiting
the specified period.
Code: Alles auswählen
SmallestService.py install
Code: Alles auswählen
SmallestService.py remove
Und jetzt zu einem Beispiel aus der Praxis --> Ein XMLRPC-Server, der als Dienst gestartet wird und auf Anfragen eines XMLRPC-Clients wartet. Der XMLRPC-Server wird von der Dienst-Klasse aus als eigenständiger Thread gestartet. So wird der XMLRPC-Server nicht blockiert und verrichtet seinen Dienst ziemlich unabhängig.
Wird der Windows-Dienst beendet, dann wird von der Dienst-Klasse aus der XMLRPC-Server beendet.
Und hier der Code (``SimpleXmlrpcService.py``):
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
"""
Windows-Dienst, der einen XMLRPC-Server auf Port 53479 startet und
auf Anfragen wartet.
Filename: SimpleXmlrpcService.py
Created: 2006-12-23 by Gerold Penz - gerold.penz(at)tirol.utanet.at
Requirements: Python: http://www.python.org/
pywin32: http://sourceforge.net/projects/pywin32/
"""
import win32serviceutil
import win32service
import servicemanager
import threading
from SimpleXMLRPCServer import SimpleXMLRPCServer
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler
import xmlrpclib
import time
PORT = 53479
class MyXmlrpcHandler(object):
"""
In dieser Klasse stehen alle Funktionen, die per XMLRPC-Server zur Verfügung
gestellt werden.
"""
def get_quit_dummy(self):
"""
Gibt den String "Quit" zurück.
"""
# Ins Eventlog schreiben
servicemanager.LogInfoMsg("Der String 'Quit' wird gesendet...")
return "Quit"
def get_helloworld(self):
"""
Gibt den String "Hallo Welt" zurück.
"""
# Ins Eventlog schreiben
servicemanager.LogInfoMsg("Der String 'Hallo Welt' wird gesendet...")
return "Hallo Welt"
class MyXmlrpcServer(SimpleXMLRPCServer, threading.Thread):
"""
Diese Klasse stellt den XMLRPC-Server dar. Der Server läuft als
eigenständiger Thread.
"""
def __init__(
self, addr, requestHandler = SimpleXMLRPCRequestHandler, logRequests = True
):
"""
Initialisiert den XMLRPCServer und den Thread.
"""
# Instanz initialisieren
SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests)
threading.Thread.__init__(self)
# Event zum Stoppen des Threads
self.stopevent = threading.Event()
# Handler-Klassen an den XMLRPC-Server binden
self.register_instance(MyXmlrpcHandler())
def run(self):
"""
Hier läuft der Thread und wartet entweder auf das Abbruchsignal oder
auf einen neuen XMLRPC-Request.
"""
while True:
# Prüfen ob der Server beendet werden soll
if self.stopevent.isSet():
# Ins Eventlog schreiben
servicemanager.LogInfoMsg("Der XMLRPC-Server wird gestoppt...")
# Schleife abbrechen
break
# Auf eine Anfrage warten
self.handle_request()
def stop(self):
"""
Stoppt den Thread und somit den XMLRPCServer.
"""
# Stopevent auslösen
self.stopevent.set()
# Eine Sekunde warten, damit dem Server genug Zeit gelassen wird, einen
# evt. noch offenen Request zu beenden.
time.sleep(1)
# Zum Server verbinden und einen Request senden, damit der Server
# nicht mehr blockiert.
server = xmlrpclib.ServerProxy("http://localhost:" + str(PORT))
server.get_quit_dummy()
# Noch eine Sekunde warten, damit auch wirklich genug Zeit zum Beenden
# gelassen wird.
time.sleep(1)
class SimpleXmlrpcService(win32serviceutil.ServiceFramework):
"""
Ich bin der Dienst...
"""
_svc_name_ = "simplexmlrpcservice"
_svc_display_name_ = "Simple XMLRPC Service"
_svc_description_ = "Einfacher XMLRPC-Server-Dienst"
def __init__(self, args):
"""
Dienst initialisieren und Stopevent erstellen.
"""
win32serviceutil.ServiceFramework.__init__(self, args)
self.stopevent = threading.Event()
def SvcStop(self):
"""
Wird von Windows ausgeführt wenn der Dienst beendet werden soll.
"""
# Ins Eventlog schreiben
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
# Das Stopevent auslösen. Dadurch wird der Dienst nicht mehr
# blockiert und kann sich beenden. Info: Der Code blockiert in dieser
# Klasse bei ``self.stopevent.wait()`` in der Methode ``SvcDoRun``.
self.stopevent.set()
def SvcDoRun(self):
"""
Wird von Windows ausgeführt wenn der Dienst gestartet werden soll.
Startet den XMLRPC-Server und wartet.
"""
# Ins Eventlog schreiben
self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
# XMLRPCServer starten
server = MyXmlrpcServer(("localhost", PORT), logRequests = False)
server.start()
# Ins Eventlog schreiben
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
# Hier wird gewartet, bis der Dienst beendet wird
self.stopevent.wait()
# XMLRPCServer beenden
server.stop()
# Ins Eventlog schreiben
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
def main():
win32serviceutil.HandleCommandLine(SimpleXmlrpcService)
if __name__=='__main__':
main()
Und wie im Allgemeinen ein XMLRPC-Server und der dazugehörige Client erstellt wird, sieht man hier: http://www.python-forum.de/topic-5478.html
lg
Gerold

Stichworte: Pythonprogramm, Windows-Service, Service, Dienst, Windows-Dienst