Welcome, guest | Sign In | My Account | Store | Cart

A simple way to implement Windows Service.

Python, 105 lines
  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')

Tested on Win XP only.

Patch for py2exe from Franck VINCENT

27 comments

Matt Keranen 16 years ago  # | flag

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

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
franck vincent 15 years, 11 months ago  # | flag

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 ?

franck vincent 15 years, 11 months ago  # | flag

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__)
franck vincent 15 years, 11 months ago  # | flag

whereAmI when i'm compiled ? Interesting stuff here:

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

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.

Louis RIVIERE (author) 15 years, 4 months ago  # | flag

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

Matias Gonzalez 15 years, 2 months ago  # | flag

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.

Louis RIVIERE (author) 15 years, 1 month ago  # | flag

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.

Jacob Oscarson 15 years ago  # | flag

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)

andrey pochekutov 14 years, 6 months ago  # | flag

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)

K 11 years, 12 months ago  # | flag

Your code uses tabs rather than spaces for indentation, which does not conform with PEP8. Could you please fix your indentation?

PEP8 "Style Guide for Python Code": http://www.python.org/dev/peps/pep-0008/

Louis RIVIERE (author) 11 years, 11 months ago  # | flag

@k : done

alexander 10 years, 10 months ago  # | flag

Hi Louis, I'm trying you receipe... I've got one error in instart(): <type 'exceptions.Exception'>(1073, 'CreateService', 'The specified service already exists.' This error leads to win32serviceutil.InstallPythonClassString(pythonClassString, serviceName)

Maybe you have an idea how to fix it?

thank you in advance

john klann 10 years, 4 months ago  # | flag

alexander check your services and see if the display name you are trying to use is already in use, if so create a new display name or use

sc delete myservice

to delete the service that you are trying to use

Andrew Ripo 9 years, 7 months ago  # | flag

Hello Louis,

How would I go about adding a description for the service?

I added a variable for description in the service class and the instart function, but when I added it to the win32serviceutil.InstallService function call an error occured.

code:

 win32serviceutil.InstallService(
   cls._svc_reg_class_,
    cls._svc_name_,
        cls._svc_display_name_,
        startType = win32service.SERVICE_AUTO_START,
        cls._svc_desciption_
    )

Error:

cls._svc_desciption_
SyntaxError: non-keyword arg after keyword arg

Thanks in advance.

Louis RIVIERE (author) 9 years, 7 months ago  # | flag

Try that :

    win32serviceutil.InstallService(
        cls._svc_reg_class_,
        cls._svc_name_,
        cls._svc_display_name_,
        startType = win32service.SERVICE_AUTO_START,
        description = 'Your Description'
    )
Andrew Ripo 9 years, 7 months ago  # | flag

Yeah that worked. Thank you very much. This is a really good example.

Martin Ponweiser 9 years, 6 months ago  # | flag

Please check lines 21,22 - I think your indentation is wrong. Thanks for the module, I will give it a try.

Louis RIVIERE (author) 9 years, 6 months ago  # | flag

@Martin Ponweiser : fixed. Thanks for the report.

Andrew Ripo 9 years, 6 months ago  # | flag

Louis,

I'm having the same problem that Matias Gonzalez had a few years ago. The service runs fine from command line, but when I try to run compiled in a py2exe executable it installs ok and then shows an error "(1053, 'StartService', ...)

I was wondering if you knew what is causing this or how to correct it?

Thanks again

Louis RIVIERE (author) 9 years, 6 months ago  # | flag

@Andrew Ripo : No, I still don't use py2exe and I still don't have a clue. Sorry.

Andrew Ripo 9 years, 6 months ago  # | flag

Louis,

I have solved the problem with the 1053 error. I'm going to leave my solution here for anyone that encounters the same issue.

When you create the .exe using py2exe you no longer need the instart function, because this is handled using command line functions on the .exe created.

So the start and stop functions from winservice_test.py need to be included in the winservice.py file in place of the functions they overwrite.

In addition the details for the name and display_name that would be passed to the instart function need to be included at the top of the winservice.py file instead of the templates

 _svc_name_ = '_unNamed'
_svc_display_name_ = '_Service Template'

becomes:

_svc_name_ = 'aTest'
_svc_display_name_ = 'Python Service Test'

Following the example from the py2exe sample service I added the following function.

if __name__ == '__main__':
     # Note that this code will not be run in the 'frozen' exe-file!!!
     win32serviceutil.HandleCommandLine(Service)

in the next comment I will include a full copy of the working winservice_py2exe.py and a py2exe setup.py file.

Andrew Ripo 9 years, 6 months ago  # | flag
 # winservice_py2exe.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):
        self.runflag=True
        while self.runflag:
            self.sleep(10)
            self.log("I'm alive ...")
    # to be overridden
    def stop(self): 
         self.runflag=False
         self.log("I'm done")

if __name__ == '__main__':
     # Note that this code will not be run in the 'frozen' exe-file!!!
     win32serviceutil.HandleCommandLine(VidiagService)
Andrew Ripo 9 years, 6 months ago  # | flag

This setup.py is taken from thenn py2exe samples included when you install the package.

setup.py

 from distutils.core import setup
 import py2exe
 import sys
 if len(sys.argv) == 1:
     sys.argv.append("py2exe")
     sys.argv.append("-q")

 class Target:
     def __init__(self, **kw):
         self.__dict__.update(kw)
         # for the versioninfo resources
         self.version = "0.5.0"
         self.company_name = "No Company"
         self.copyright = "no copyright"
         self.name = "py2exe sample files"

 myservice = Target(
     # used for the versioninfo resource
     description = "A sample Windows NT service",
     # what to build.  For a service, the module name (not the
     # filename) must be specified!
     modules = ["winservice_py2exe"]
     )

 setup(
     options = {"py2exe": {"typelibs":
                      # typelib for WMI
                      [('{565783C6-CB41-11D1-8B02-00600806D9B6}', 0, 1, 2)],
                      # create a compressed zip archive
                      "compressed": 1,
                      "optimize": 2}},
     # The lib directory contains everything except the executables and the python dll.
     # Can include a subdirectory name.
     zipfile = "lib/shared.zip",

     service = [myservice]
     )

once you create the exe you can install the service from command using the following command

winservice_py2exe.exe -install

then to start the service you can use:

net start aTest

or from windows service manager.

Hope this was helpful.

KK 8 years, 4 months ago  # | flag

On windows 7 64 bit with Anaconda 32bit installed
It will output
Install ok
Start ok

But actually it wasn't started. If I type
net start aTest

I got error code 3547

Adrian Boeh 8 years, 2 months ago  # | flag

Thank you very much! This helped me create my first Windows service via python. While I have a service now, I am not sure how to use it... meaning I have some code I would like windows to run as a service (start on start up.. etc) but I don't know where/how my code fits into the service code above. Any examples of a very simple script running as a service ('hello world') would be greatly appreciated!