ActiveState Code

Recipe 551780: Win Services helper


A simple way to implement Windows Service.

Python
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
# winservice.py

from os.path import splitext, abspath
from sys import modules

import win32serviceutil
import win32service
import win32event
import win32api

class Service(win32serviceutil.ServiceFramework):
	_svc_name_ = '_unNamed'
	_svc_display_name_ = '_Service Template'
	def __init__(self, *args):
		win32serviceutil.ServiceFramework.__init__(self, *args)
		self.log('init')
		self.stop_event = win32event.CreateEvent(None, 0, 0, None)
	def log(self, msg):
		import servicemanager
		servicemanager.LogInfoMsg(str(msg))
        def sleep(self, sec):
                win32api.Sleep(sec*1000, True)
	def SvcDoRun(self):
		self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
		try:
			self.ReportServiceStatus(win32service.SERVICE_RUNNING)
			self.log('start')
			self.start()
			self.log('wait')
			win32event.WaitForSingleObject(self.stop_event, win32event.INFINITE)
			self.log('done')
		except Exception, x:
			self.log('Exception : %s' % x)
			self.SvcStop()
	def SvcStop(self):
		self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
		self.log('stopping')
		self.stop()
		self.log('stopped')
		win32event.SetEvent(self.stop_event)
		self.ReportServiceStatus(win32service.SERVICE_STOPPED)
	# to be overridden
	def start(self): pass
	# to be overridden
	def stop(self): pass

def instart(cls, name, display_name=None, stay_alive=True):
    '''
        Install and  Start (auto) a Service
            
            cls : the class (derived from Service) that implement the Service
            name : Service name
            display_name : the name displayed in the service manager
            stay_alive : Service will stop on logout if False
    '''
    cls._svc_name_ = name
    cls._svc_display_name_ = display_name or name
    try:
        module_path=modules[cls.__module__].__file__
    except AttributeError:
        # maybe py2exe went by
        from sys import executable
        module_path=executable
    module_file=splitext(abspath(module_path))[0]
    cls._svc_reg_class_ = '%s.%s' % (module_file, cls.__name__)
    if stay_alive: win32api.SetConsoleCtrlHandler(lambda x: True, True)
    try:
        win32serviceutil.InstallService(
                cls._svc_reg_class_,
                cls._svc_name_,
                cls._svc_display_name_,
                startType=win32service.SERVICE_AUTO_START
                )
        print 'Install ok'
        win32serviceutil.StartService(
                cls._svc_name_
                )
        print 'Start ok'
    except Exception, x:
        print str(x)

#
#
#
#
##### TEST MODULE
#
#
#
#

# winservice_test.py
from winservice import Service, instart

class Test(Service):
    def start(self):
        self.runflag=True
        while self.runflag:
            self.sleep(10)
            self.log("I'm alive ...")
    def stop(self):
        self.runflag=False
        self.log("I'm done")

instart(Test, 'aTest', 'Python Service Test')

Discussion

Tested on Win XP only.

Patch for py2exe from Franck VINCENT

Comments

  1. 1. At 9:59 a.m. on 20 mar 2008, Matt Keranen said:

    Method for service removal? What steps would remove a service registered by this code from the registry?

  2. 2. At 2:59 a.m. on 21 mar 2008, Louis Riviere (the author) said:

    Service management. A Service is just a regular Windows Service, you can handle it like any other service running on your system.

    You can start/stop/... it from the Services Manager, in which you will see the _svc_display_name of your Service.

    You can handle it from a console with the NET or the SC commands.

    Note that these commands, unlike the Windows Service Manager, use the _svc_name.

    So, if you've install a Service like that:

    >>> instart(MyServiceClass, 'myservice')
    

    You can stop it like that :

    >>> net stop myservice
    

    You can remove like that :

    >>> sc delete myservice
    
  3. 3. At 6:25 a.m. on 28 mar 2008, franck vincent said:

    don't work with a binary compiled with py2exe. take the same code, compile it with py2exe and you will have this error:

    Traceback (most recent call last):
      File "test.py", line 14, in
        instart(Test, 'aTest', 'Python Service Test')
      File "winservice.pyc", line 59, in instart
    
    AttributeError: 'module' object has no attribute '__file__'
    

    Any idea ?

  4. 4. At 11:35 p.m. on 8 apr 2008, franck vincent said:

    current compiled executable path/name. The error below because __file__ attributes is no longer available when you are executing the binary.

    You have to use

    "sys.executable"

    rather than

    "modules[cls.__module__].__file__"

    Below the code i used:

    if hasattr(modules[cls.__module__],'__file__'):
            cmod=modules[cls.__module__].__file__
        else:
            cmod=sys.executable
        module_file=splitext(abspath(cmod))[0]
        cls._svc_reg_class_ = '%s.%s' % (module_file, cls.__name__)
    
  5. 5. At 11:36 p.m. on 8 apr 2008, franck vincent said:

    whereAmI when i'm compiled ? Interesting stuff here:

    http://www.py2exe.org/index.cgi/WhereAmI

  6. 6. At 1:16 p.m. on 12 nov 2008, Gerard Vignes said:

    Hi Louis,

    I like your winservice module. I want to use it to daemonize a simple python project i have started, craigslistTools. I have your winservice module posted (crediting you) to my google site at http://sites.google.com/site/ninetynine99s/craigslist-tools-1 so ppl can run my code and see what it does. I would like to create a sourceforge project so maybe i can get some help developing this into a real project (i am a python newbie).

    I would like to get your permission to include winservice.py as part of this craigslistTools project, giving you full credit for that module Please let me know if this is ok.

    Thanks, Gerard

    PS. I couldn't find your email address. This is the only way I knew to contact you.

  7. 7. At 3:44 a.m. on 16 nov 2008, Louis Riviere (the author) said:

    Hi Gerard !

    It is indeed totally ok for you to use this recipe in any way you like, as it is the all purpose of this Cookbook.

    PS : I can be reached at riviere@ati33.org

  8. 8. At 6:50 p.m. on 18 jan 2009, Matias Gonzalez said:

    Hello Louis,

    I could make it to install the service from the command line, but when I try to run compiled in a py2exe executable it installs ok and then shows an error "(1053, 'StartService', ...)" saying that the service didnt response. Any ideas? I try the same script (without the client import) all in one file, to avoid imports (for path troubles). I think it has to be something with py2exe but i couldn't find anything else.

    Thanks in advance! Matias.

  9. 9. At 6:16 a.m. on 23 jan 2009, Louis Riviere (the author) said:

    Nope,
    I don't use py2exe and I don't know much about it.
    Maybe you can find something useful in the system event log.
    Good luck.

  10. 10. At 5:59 a.m. on 18 mar 2009, Jacob Oscarson said:

    Just a little gotcha for you who tries this recipe: your script can't live on a mapped drive... (duh.. maybe obvious to more win-centric persons than me)

  11. 11. At 11:16 p.m. on 13 sep 2009, apochekutov said:

    Can use: win32serviceutil.HandleCommandLine(Service) for command line register, unregister, start, stop service

    class Service(win32serviceutil.ServiceFramework): _svc_name_ = "sample_python_service" _svc_display_name_ = "Python Service" _svc_description_ = "Tests Python service"

    if __name__ == '__main__': win32serviceutil.HandleCommandLine(Service)

Sign in to comment