Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Windows service pyinstaller executables error 1053

I have written a Windows service in python. If I run my script from the command prompt

python runService.py 

When I do this the service installs and starts correctly. I have been trying to create an executable using pyinstaller because i've seen the same issue with py2exe. When I run the .exe the service installs but does not start and I get the following error

error 1053 the service did not respond to the start or control request in a timely fashion 

I have seen that many people have had this issue but I can't seem to find a definitive answer as to how to fix this.

winservice.py

from os.path import splitext, abspath
from sys import modules, executable
from time import *
import win32serviceutil
import win32service
import win32event
import win32api

class Service(win32serviceutil.ServiceFramework):
    _svc_name_ = '_unNamed'
    _svc_display_name_ = '_Service Template'
    _svc_description_ = '_Description template'
    def __init__(self, *args):
        win32serviceutil.ServiceFramework.__init__(self, *args)
        self.log('init')
        self.stop_event = win32event.CreateEvent(None, 0, 0, None)

    #logs into the system event log
def log(self, msg):
    import servicemanager
    servicemanager.LogInfoMsg(str(msg))

def sleep(self, minute):
        win32api.Sleep((minute*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, description, 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
        decription: the description 
        stay_alive : Service will stop on logout if False
    '''
    cls._svc_name_ = name
    cls._svc_display_name_ = display_name or name
    cls._svc_desciption_ = description
    try:
        module_path=modules[cls.__module__].__file__
    except AttributeError:

        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,
            description = cls._svc_desciption_
        )
        print 'Install ok'
        win32serviceutil.StartService(
        cls._svc_name_
    )
    print 'Start ok'
except Exception, x:
    print str(x)

UPDATE

I resolved this issue by using py2exe but the same changes may work for pyinstaller too. I haven't had time to check this out myself.

I had to remove the instart function. Below is how my winservice.py reads now.

winservice_py2exe.py

from os.path import splitext, abspath
from sys import modules, executable
from time import *
import win32serviceutil
import win32service
import win32event
import win32api

class Service(win32serviceutil.ServiceFramework):
    _svc_name_ = 'actualServiceName' #here is now the name you would input as an arg for instart
    _svc_display_name_ = 'actualDisplayName' #arg for instart
    _svc_description_ = 'actualDescription'# arg from instart
    def __init__(self, *args):
        win32serviceutil.ServiceFramework.__init__(self, *args)
        self.log('init')
        self.stop_event = win32event.CreateEvent(None, 0, 0, None)

    #logs into the system event log
def log(self, msg):
    import servicemanager
    servicemanager.LogInfoMsg(str(msg))

def sleep(self, minute):
        win32api.Sleep((minute*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

if __name__ == '__main__':
     # Note that this code will not be run in the 'frozen' exe-file!!!
     win32serviceutil.HandleCommandLine(VidiagService) #added from example included with py2exe

Below is the setup.py file i used with py2exe. This was taken from the example included with the py2exe installation:

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. All other windows command line functionality now works on the service as well as from windows service manager.

like image 392
user1385894 Avatar asked Sep 10 '14 16:09

user1385894


2 Answers

Try changing the last few lines to

if __name__ == '__main__':
    if len(sys.argv) == 1:
        servicemanager.Initialize()
        servicemanager.PrepareToHostSingle(Service)
        servicemanager.StartServiceCtrlDispatcher()
    else:
        win32serviceutil.HandleCommandLine(Service)
like image 139
MrTorture Avatar answered Oct 14 '22 10:10

MrTorture


MrTorture had the key to this answer, but I'd like to build upon that here. Something critical to note is that even when using the win32serviceutil functions to manage the service programmatically (vs installing, starting, etc. via the command prompt), you must include the entry point command line dispatch for this to work in a standalone context (i.e. when building an exe with pyinstaller or py2exe). If you don't, Windows will not be able to start the service. You'll get the dreaded 1053 error!

In addition to that, note that if you are creating a service as part of a larger project, you will need to build an exe dedicated to the service. You cannot run it as a sub component within a larger exe (at least I didn't have luck trying to!). I've included my install function to demonstrate that.

Again, when using .py scripts, managed via pythonservice.exe, neither of these issues are present, these are only concerns for standalone exes.

Here are some INCOMPLETE snippets of my functional code, but they might save you a lot of trouble:

SUCCESS = winerror.ERROR_SUCCESS
FAILURE = -1

class WinServiceManager():  

    # pass the class, not an instance of it!
    def __init__( self, serviceClass, serviceExeName=None ):
        self.serviceClass_ = serviceClass
        # Added for pyInstaller v3
        self.serviceExeName_ = serviceExeName

    def isStandAloneContext( self ) : 
        # Changed for pyInstaller v3
        #return sys.argv[0].endswith( ".exe" ) 
        return not( sys.argv[0].endswith( ".py" ) )

    def dispatch( self ):
        if self.isStandAloneContext() :
            servicemanager.Initialize()
            servicemanager.PrepareToHostSingle( self.serviceClass_ )
            servicemanager.Initialize( self.serviceClass_._svc_name_, 
                os.path.abspath( servicemanager.__file__ ) )
            servicemanager.StartServiceCtrlDispatcher()        
        else :
            win32api.SetConsoleCtrlHandler(lambda x: True, True)  
            win32serviceutil.HandleCommandLine( self.serviceClass_ )        

    # Service management functions
    #            
    # Note: all of these functions return:
    # SUCCESS when explicitly successful
    # FAILURE when explicitly not successful at their specific purpose
    # winerror.XXXXXX when win32service (or related class) 
    # throws an error of that nature
    #------------------------------------------------------------------------

    # Note: an "auto start" service is not auto started upon installation!
    # To install and start simultaneously, use start( autoInstall=True ).
    # That performs both actions for manual start services as well.
    def install( self ):
        win32api.SetConsoleCtrlHandler(lambda x: True, True)        
        result = self.verifyInstall()
        if result == SUCCESS or result != FAILURE: return result
        thisExePath = os.path.realpath( sys.argv[0] )
        thisExeDir  = os.path.dirname( thisExePath )        
        # Changed for pyInstaller v3 - which now incorrectly reports the calling exe
        # as the serviceModPath (v2 worked correctly!)
        if self.isStandAloneContext() :
            serviceModPath = self.serviceExeName_
        else :
            serviceModPath = sys.modules[ self.serviceClass_.__module__ ].__file__        
        serviceModPath = os.path.splitext(os.path.abspath( serviceModPath ))[0] 
        serviceClassPath = "%s.%s" % ( serviceModPath, self.serviceClass_.__name__ )
        self.serviceClass_._svc_reg_class_ = serviceClassPath
        # Note: in a "stand alone context", a dedicated service exe is expected 
        # within this directory (important for cases where a separate master exe 
        # is managing services).  
        serviceExePath = (serviceModPath + ".exe") if self.isStandAloneContext() else None        
        isAutoStart = self.serviceClass_._svc_is_auto_start_
        startOpt = (win32service.SERVICE_AUTO_START if isAutoStart else 
                    win32service.SERVICE_DEMAND_START)        
        try :      
            win32serviceutil.InstallService(
                pythonClassString = self.serviceClass_._svc_reg_class_,
                serviceName       = self.serviceClass_._svc_name_,
                displayName       = self.serviceClass_._svc_display_name_,
                description       = self.serviceClass_._svc_description_,
                exeName           = serviceExePath,
                startType         = startOpt
            ) 
        except win32service.error as e: return e[0]
        except Exception as e: raise e        
        win32serviceutil.SetServiceCustomOption( 
            self.serviceClass_._svc_name_, WORKING_DIR_OPT_NAME, thisExeDir )
        for i in range( 0, MAX_STATUS_CHANGE_CHECKS ) :
            result = self.verifyInstall()
            if result == SUCCESS: return SUCCESS
            time.sleep( STATUS_CHANGE_CHECK_DELAY )            
        return result         

In the module where you define your service (derived from win32serviceutil.ServiceFramework), include this at the end of it:

if __name__ == "__main__":   
    WinServiceManager( MyServiceClass, "MyServiceBinary.exe" ).dispatch()
like image 30
BuvinJ Avatar answered Oct 14 '22 11:10

BuvinJ