1

Question

I am attempting to start a Python script as a Windows service using pywin32. I can properly install and remove the service, but while installed, its state never changes from "stopped." How can I fix my code so that the service actually runs?

Code

#!/bin/python

import winerror
import win32event
import win32service
import win32serviceutil

SVC_RUN = 1
SVC_REMOVE = 2

class TMP_Service(win32serviceutil.ServiceFramework):

    _svc_name_ = "tmp_svc_name"
    _svc_display_name_ =  "tmp svc disp name"
    _svc_reg_class = "tmp.reg_class"
    _svc_description_ = "tmp description"

    def __init__(self, dut_name):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)

    def SvcStop(self):
        self.reportServiceStatus(win32service.SERVICE_STOP_PENDING)
        # I will call reactor.callFromThread(reactor.stop) here to stop the 
        # FTP and SCP listeners
        win32event.SetEvent(self.hWaitStop)

    def SvcDoRun(self):
        self.ReportServiceStatus(win32service.SERVICE_RUNNING)

        # This infinite loop simulates future behavior of my service. It will
        # run a Twisted reactor to handle FTP and TCP network connections.
        while True:
            pass 

        win32event.WaitforSingleObject(self.hWaitStop, win32event.INFINITE)

def install_svc():

    try:
        win32serviceutil.InstallService(
                TMP_Service._svc_reg_class,
                TMP_Service._svc_name_,
                TMP_Service._svc_display_name_,
                startType=win32service.SERVICE_AUTO_START)

    except win32service.error as e:

        if e[0] == winerror.ERROR_SERVICE_EXISTS:
            pass # ignore install error if service is already installed

        else:
            raise

def handle_service(svc):

    if svc == SVC_RUN:

        try:
            win32serviceutil.StartService(TMP_Service._svc_name_)

        except win32service.error as e:

            if ((e[0] == winerror.ERROR_SERVICE_ALREADY_RUNNING) or
                    (e[0] == winerror.ERROR_ALREADY_RUNNING_LKG)):
                pass # ignore failed start if the service is already running

            elif e[0] == winerror.ERROR_SERVICE_DOES_NOT_EXIST:

                # if the service is not installed, install it and try again
                install_svc()
                win32serviceutil.StartService(TMP_Service._svc_name_)

            else:
                # reraise any other start expection
                raise

        status = win32serviceutil.QueryServiceStatus(TMP_Service._svc_name_)
        print("Service status: {}".format(status[1]))

    else:

        try:
            win32serviceutil.RemoveService(TMP_Service._svc_name_)

        except win32service.error as e:

            if e[0] == winerror.ERROR_SERVICE_DOES_NOT_EXIST:
                pass # ignore failed removal if service is not installed
            else:
                # reraise any other remove exception
                raise

if __name__ == "__main__":

    handle_service(SVC_RUN)

Other Details

  1. When I look at the system event logs, I see this error:

    Python could not import the service's module
    ImportError: No module named tmp
    %2: %3

    The timestamp for these messages match the times that I tried to run this script.

  2. I have seen this question: Can't start Windows service written in Python (win32serviceutil). Based on the advice there, I have made my code match the suggestion in the top answer there, and made sure that C:\Python27 is in my system path. Neither suggestion made a difference.

  3. The status that is printed is always "2". According to MSDN, this is SERVICE_START_PENDING, which means the service should be starting.

Updated Details

  1. If I change the value of the _svc_reg_class_ attribute to "tmp2.reg_class", then the name of the missing module, as reported in the Windows event log, changes to "tmp2", so this error is related to the _svc_reg_class_ attribute.

  2. In reply to a comment below: I don't think I can capture the full traceback, because my service class is instantiated and used in the pywin32 library code. The offending import doesn't happen anywhere in my code, so there's nothing for me to wrap in a try/except block.

Community
  • 1
  • 1
skrrgwasme
  • 9,358
  • 11
  • 54
  • 84
  • Can you show the traceback of that ImportError? You can print it to a file with try, except and `import traceback; traceback.print_exc(...)` – User Jul 16 '15 at 09:35
  • @User See updated details. – skrrgwasme Jul 16 '15 at 15:37
  • What happens if you name the file `tmp.py` or `tmp2.py`? Maybe the reg class has something to do with the module name. Also, try to put it into the site-packages. – User Jul 16 '15 at 17:28

1 Answers1

0

Change the _svc_reg_class = "tmp.reg_class" as shown below.

try:
    module_path = modules[TMP_Service.__module__].__file__
except AttributeError:
    # maybe py2exe went by
    from sys import executable
    module_path = executable

module_file = splitext(abspath(module_path))[0]
TMP_Service._svc_reg_class = '%s.%s' % (module_file, TMP_Service.__name__)

Below is the complete modified version of your original code.

#!/bin/python

import winerror
import win32event
import win32service
import win32serviceutil
from sys import modules
from os.path import splitext, abspath

SVC_RUN = 1
SVC_REMOVE = 2

class TMP_Service(win32serviceutil.ServiceFramework):

    _svc_name_ = "tmp_svc_name"
    _svc_display_name_ =  "tmp svc disp name"
    _svc_reg_class = "tmp.reg_class"
    _svc_description_ = "tmp description"

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        self.Stop = True
        # I will call reactor.callFromThread(reactor.stop) here to stop the 
        # FTP and SCP listeners
        win32event.SetEvent(self.hWaitStop)
        self.ReportServiceStatus(win32service.SERVICE_STOPPED)

    def SvcDoRun(self):
        self.Stop = False
        self.ReportServiceStatus(win32service.SERVICE_RUNNING)

        # This infinite loop simulates future behavior of my service. It will
        # run a Twisted reactor to handle FTP and TCP network connections.
        while self.Stop == False:
            pass 

        win32event.WaitforSingleObject(self.hWaitStop, win32event.INFINITE)

def install_svc():

    try:
        module_path = modules[TMP_Service.__module__].__file__
    except AttributeError:
        # maybe py2exe went by
        from sys import executable
        module_path = executable

    module_file = splitext(abspath(module_path))[0]
    TMP_Service._svc_reg_class = '%s.%s' % (module_file, TMP_Service.__name__)

    try:
        win32serviceutil.InstallService(
                TMP_Service._svc_reg_class,
                TMP_Service._svc_name_,
                TMP_Service._svc_display_name_,
                startType=win32service.SERVICE_AUTO_START)

    except win32service.error as e:

        if e[0] == winerror.ERROR_SERVICE_EXISTS:
            pass # ignore install error if service is already installed

        else:
            raise

def handle_service(svc):

    if svc == SVC_RUN:

        try:
            win32serviceutil.StartService(TMP_Service._svc_name_)

        except win32service.error as e:

            if ((e[0] == winerror.ERROR_SERVICE_ALREADY_RUNNING) or
                    (e[0] == winerror.ERROR_ALREADY_RUNNING_LKG)):
                pass # ignore failed start if the service is already running

            elif e[0] == winerror.ERROR_SERVICE_DOES_NOT_EXIST:

                # if the service is not installed, install it and try again
                install_svc()
                win32serviceutil.StartService(TMP_Service._svc_name_)

            else:
                # reraise any other start expection
                raise

        status = win32serviceutil.QueryServiceStatus(TMP_Service._svc_name_)
        print("Service status: {}".format(status[1]))

    else:

        try:
            win32serviceutil.RemoveService(TMP_Service._svc_name_)

        except win32service.error as e:

            if e[0] == winerror.ERROR_SERVICE_DOES_NOT_EXIST:
                pass # ignore failed removal if service is not installed
            else:
                # reraise any other remove exception
                raise

if __name__ == "__main__":
    handle_service(SVC_RUN)

Reference: here

Vinkal
  • 2,964
  • 1
  • 19
  • 19