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.
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)
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With