Search code examples
pythonpython-2.7windows-servicespywin32

pywin32 service start error: no module named 'tmp'


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.


Solution

  • 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