Python servicio de Windows pyinstaller ejecutables error 1053

He escrito un servicio de Windows en Python. Si ejecuto mi script desde el símbolo del sistema

python runService.py 

Cuando hago esto el servicio se instala y se inicia correctamente. He estado intentando crear un ejecutable utilizando pyinstaller porque he visto el mismo problema con py2exe. Cuando ejecuto el archivo .exe, el servicio se instala pero no se inicia y aparece el siguiente error

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

He visto que muchas personas han tenido este problema, pero parece que no puedo encontrar una respuesta definitiva sobre cómo solucionar este problema.

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) 

ACTUALIZAR

Resolví este problema usando py2exe pero los mismos cambios también pueden funcionar para pyinstaller. No he tenido tiempo de comprobarlo yo mismo.

Tuve que quitar la función instart . A continuación es cómo se lee mi winservice.py ahora.

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 

A continuación se muestra el archivo setup.py que usé con py2exe. Esto se tomó del ejemplo incluido con la instalación de py2exe:

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] ) 

Una vez que haya creado el archivo ejecutable, puede instalar el servicio desde el comando utilizando el siguiente comando

 winservice_py2exe.exe -install 

Luego para iniciar el servicio puedes usar:

  net start aTest 

o desde el administrador de servicios de Windows. Todas las demás funciones de la línea de comandos de Windows ahora funcionan tanto en el servicio como en el administrador de servicios de Windows.

Intenta cambiar las últimas líneas para

 if __name__ == '__main__': if len(sys.argv) == 1: servicemanager.Initialize() servicemanager.PrepareToHostSingle(Service) servicemanager.StartServiceCtrlDispatcher() else: win32serviceutil.HandleCommandLine(Service) 

MrTorture tenía la clave para esta respuesta, pero me gustaría continuar con eso aquí. Algo crítico que se debe tener en cuenta es que incluso cuando se usan las funciones win32serviceutil para administrar el servicio mediante progtwigción (vs instalación, inicio, etc. a través del símbolo del sistema), debe incluir el envío de la línea de comando del punto de entrada para que esto funcione en un contexto independiente (es decir, al construir un exe con pyinstaller o py2exe). Si no lo hace, Windows no podrá iniciar el servicio. Obtendrás el temido error 1053!

Además de eso, tenga en cuenta que si está creando un servicio como parte de un proyecto más grande, tendrá que crear un exe dedicado al servicio. No puedes ejecutarlo como un subcomponente dentro de un exe más grande (¡al menos no tuve suerte al intentarlo!). He incluido mi función de instalación para demostrar eso.

Nuevamente, cuando se usan scripts .py, administrados a través de pythonservice.exe, ninguno de estos problemas está presente, solo son preocupaciones para los exes independientes.

Aquí hay algunos fragmentos INCOMPLETOS de mi código funcional, pero pueden ahorrarle muchos problemas:

 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 

En el módulo donde define su servicio (derivado de win32serviceutil.ServiceFramework), incluya esto al final de este:

 if __name__ == "__main__": WinServiceManager( MyServiceClass, "MyServiceBinary.exe" ).dispatch()