我已经在python中写了一个Windows服务。 如果我从命令提示符运行我的脚本
python runService.py
当我这样做的服务安装并正确启动。 我一直在尝试使用pyinstaller创build一个可执行文件,因为我已经看到了与py2exe相同的问题。 当我运行.exe服务安装,但不启动,我得到以下错误
error 1053 the service did not respond to the start or control request in a timely fashion
我看到很多人都有这个问题,但似乎无法find一个明确的答案,如何解决这个问题。
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
我通过使用py2exe解决了这个问题,但同样的改变可能也适用于pyinstaller。 我没有时间自己检查一下。
我不得不删除instartfunction。 下面是我的winservice.py现在读取。
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
下面是我使用py2exe的setup.py文件。 这是从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] )
一旦你创build了exe,你可以使用下面的命令从命令安装服务
winservice_py2exe.exe -install
然后启动您可以使用的服务:
net start aTest
或从Windows服务pipe理器。 所有其他Windows命令行function现在可以在服务以及Windows服务pipe理器上运行。
尝试改变最后几行
if __name__ == '__main__': if len(sys.argv) == 1: servicemanager.Initialize() servicemanager.PrepareToHostSingle(Service) servicemanager.StartServiceCtrlDispatcher() else: win32serviceutil.HandleCommandLine(Service)
MrTorture先生有这个答案的关键,但我想在此建立。 需要注意的是,即使使用win32serviceutil函数以编程方式管理服务(与通过命令提示符安装,启动等),您必须包含入口点命令行调度才能在独立上下文中工作(即当用pyinstaller或py2exe构建一个exe文件)。 如果你不这样做,Windows将无法启动服务。 你会得到可怕的1053错误!
除此之外,请注意,如果您将服务作为较大项目的一部分来创建,则需要构建一个专用于此服务的exe文件。 你不能把它作为一个更大的exe文件中的一个子组件运行(至少我没有运气!)。 我已经包括我的安装功能来证明这一点。
同样,当使用.py脚本,通过pythonservice.exe进行管理时,这些问题都不存在,这些仅仅是关于独立exes的问题。
以下是我的功能代码的一些INCOMPLETE代码片断,但它们可能为您节省很多麻烦:
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
在您定义服务的模块(从win32serviceutil.ServiceFramework派生)中,将其包含在其末尾:
if __name__ == "__main__": WinServiceManager( MyServiceClass, "MyServiceBinary.exe" ).dispatch()