Python Windows服务pyinstaller可执行文件错误1053
我写了一个Windows服务,使用的是Python。如果我在命令提示符下运行我的脚本,
python runService.py
这样做的话,服务就能正确安装并启动。我尝试用pyinstaller创建一个可执行文件,因为我在使用py2exe时也遇到了同样的问题。当我运行这个.exe文件时,服务虽然安装了,但没有启动,并且出现了以下错误:
error 1053 the service did not respond to the start or control request in a timely fashion
我看到很多人也遇到过这个问题,但我找不到一个明确的解决办法。
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)
更新
我通过使用py2exe解决了这个问题,但同样的修改可能也适用于pyinstaller。我还没时间自己去验证这一点。
我需要删除instart函数。下面是我现在的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]
)
一旦你创建了exe文件,就可以使用以下命令从命令行安装服务:
winservice_py2exe.exe -install
然后要启动服务,你可以使用:
net start aTest
或者通过Windows服务管理器来启动。现在,所有其他Windows命令行功能也可以在服务上使用,并且可以通过Windows服务管理器进行操作。
3 个回答
你可以参考这个例子:
import sys
import time
import win32serviceutil # ServiceFramework and commandline helper
import win32service # Events
import servicemanager # Simple setup and logging
class MyService:
"""Silly little application stub"""
def stop(self):
"""Stop the service"""
self.running = False
def run(self):
"""Main service loop. This is where work is done!"""
self.running = True
while self.running:
time.sleep(10) # Important work
servicemanager.LogInfoMsg("Service running...")
class MyServiceFramework(win32serviceutil.ServiceFramework):
_svc_name_ = 'MyService'
_svc_display_name_ = 'My Service display name'
def SvcStop(self):
"""Stop the service"""
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.service_impl.stop()
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
def SvcDoRun(self):
"""Start the service; does not return until stopped"""
self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
self.service_impl = MyService()
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
# Run the service
self.service_impl.run()
def init():
if len(sys.argv) == 1:
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(MyServiceFramework)
servicemanager.StartServiceCtrlDispatcher()
else:
win32serviceutil.HandleCommandLine(MyServiceFramework)
if __name__ == '__main__':
init()
- pyinstaller命令:
pyinstaller --onefile --runtime-tmpdir=. --hidden-import win32timezone <your_file_name>.py
- 将.exe注册为Windows服务
<path_to_your_exe_file> install
之后你就可以像使用Windows服务一样使用它。代码在我这儿运行得很好。
MrTorture 提供了这个问题的关键答案,但我想在这里进一步补充一些内容。首先要注意的是,即使你使用 win32serviceutil 函数来编程管理服务(也就是不是通过命令提示符来安装、启动等),你仍然需要包含一个入口点的命令行调度,这样才能在独立的环境中正常工作(比如用 pyinstaller 或 py2exe 打包成 exe 文件时)。如果不这样做,Windows 就无法启动这个服务,你会遇到让人头疼的 1053 错误!
另外,如果你是在一个更大的项目中创建服务,你需要为这个服务单独构建一个 exe 文件。你不能把它作为一个子组件在更大的 exe 文件中运行(至少我尝试过是没成功的!)。我在下面的安装函数中展示了这一点。
再说一次,当你使用 .py 脚本并通过 pythonservice.exe 管理时,这些问题就不存在了,这些只是独立 exe 文件需要注意的事项。
以下是我功能代码的一些不完整片段,但它们可能会帮你省去很多麻烦:
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()
试着把最后几行改成这样:
if __name__ == '__main__':
if len(sys.argv) == 1:
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(Service)
servicemanager.StartServiceCtrlDispatcher()
else:
win32serviceutil.HandleCommandLine(Service)