I am attempting to start a Python script as a Windows service using pywin32. I can properly install and remove the service, but while installed, its state never changes from "stopped." How can I fix my code so that the service actually runs?
#!/bin/python
import winerror
import win32event
import win32service
import win32serviceutil
SVC_RUN = 1
SVC_REMOVE = 2
class TMP_Service(win32serviceutil.ServiceFramework):
_svc_name_ = "tmp_svc_name"
_svc_display_name_ = "tmp svc disp name"
_svc_reg_class = "tmp.reg_class"
_svc_description_ = "tmp description"
def __init__(self, dut_name):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
def SvcStop(self):
self.reportServiceStatus(win32service.SERVICE_STOP_PENDING)
# I will call reactor.callFromThread(reactor.stop) here to stop the
# FTP and SCP listeners
win32event.SetEvent(self.hWaitStop)
def SvcDoRun(self):
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
# This infinite loop simulates future behavior of my service. It will
# run a Twisted reactor to handle FTP and TCP network connections.
while True:
pass
win32event.WaitforSingleObject(self.hWaitStop, win32event.INFINITE)
def install_svc():
try:
win32serviceutil.InstallService(
TMP_Service._svc_reg_class,
TMP_Service._svc_name_,
TMP_Service._svc_display_name_,
startType=win32service.SERVICE_AUTO_START)
except win32service.error as e:
if e[0] == winerror.ERROR_SERVICE_EXISTS:
pass # ignore install error if service is already installed
else:
raise
def handle_service(svc):
if svc == SVC_RUN:
try:
win32serviceutil.StartService(TMP_Service._svc_name_)
except win32service.error as e:
if ((e[0] == winerror.ERROR_SERVICE_ALREADY_RUNNING) or
(e[0] == winerror.ERROR_ALREADY_RUNNING_LKG)):
pass # ignore failed start if the service is already running
elif e[0] == winerror.ERROR_SERVICE_DOES_NOT_EXIST:
# if the service is not installed, install it and try again
install_svc()
win32serviceutil.StartService(TMP_Service._svc_name_)
else:
# reraise any other start expection
raise
status = win32serviceutil.QueryServiceStatus(TMP_Service._svc_name_)
print("Service status: {}".format(status[1]))
else:
try:
win32serviceutil.RemoveService(TMP_Service._svc_name_)
except win32service.error as e:
if e[0] == winerror.ERROR_SERVICE_DOES_NOT_EXIST:
pass # ignore failed removal if service is not installed
else:
# reraise any other remove exception
raise
if __name__ == "__main__":
handle_service(SVC_RUN)
When I look at the system event logs, I see this error:
Python could not import the service's module
ImportError: No module named tmp
%2: %3
The timestamp for these messages match the times that I tried to run this script.
I have seen this question: Can't start Windows service written in Python (win32serviceutil). Based on the advice there, I have made my code match the suggestion in the top answer there, and made sure that C:\Python27 is in my system path. Neither suggestion made a difference.
The status that is printed is always "2". According to MSDN, this is SERVICE_START_PENDING, which means the service should be starting.
Updated Details
If I change the value of the _svc_reg_class_
attribute to "tmp2.reg_class"
, then the name of the missing module, as reported in the Windows event log, changes to "tmp2", so this error is related to the _svc_reg_class_
attribute.
In reply to a comment below: I don't think I can capture the full traceback, because my service class is instantiated and used in the pywin32 library code. The offending import doesn't happen anywhere in my code, so there's nothing for me to wrap in a try/except block.
Change the _svc_reg_class = "tmp.reg_class" as shown below.
try:
module_path = modules[TMP_Service.__module__].__file__
except AttributeError:
# maybe py2exe went by
from sys import executable
module_path = executable
module_file = splitext(abspath(module_path))[0]
TMP_Service._svc_reg_class = '%s.%s' % (module_file, TMP_Service.__name__)
Below is the complete modified version of your original code.
#!/bin/python
import winerror
import win32event
import win32service
import win32serviceutil
from sys import modules
from os.path import splitext, abspath
SVC_RUN = 1
SVC_REMOVE = 2
class TMP_Service(win32serviceutil.ServiceFramework):
_svc_name_ = "tmp_svc_name"
_svc_display_name_ = "tmp svc disp name"
_svc_reg_class = "tmp.reg_class"
_svc_description_ = "tmp description"
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.Stop = True
# I will call reactor.callFromThread(reactor.stop) here to stop the
# FTP and SCP listeners
win32event.SetEvent(self.hWaitStop)
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
def SvcDoRun(self):
self.Stop = False
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
# This infinite loop simulates future behavior of my service. It will
# run a Twisted reactor to handle FTP and TCP network connections.
while self.Stop == False:
pass
win32event.WaitforSingleObject(self.hWaitStop, win32event.INFINITE)
def install_svc():
try:
module_path = modules[TMP_Service.__module__].__file__
except AttributeError:
# maybe py2exe went by
from sys import executable
module_path = executable
module_file = splitext(abspath(module_path))[0]
TMP_Service._svc_reg_class = '%s.%s' % (module_file, TMP_Service.__name__)
try:
win32serviceutil.InstallService(
TMP_Service._svc_reg_class,
TMP_Service._svc_name_,
TMP_Service._svc_display_name_,
startType=win32service.SERVICE_AUTO_START)
except win32service.error as e:
if e[0] == winerror.ERROR_SERVICE_EXISTS:
pass # ignore install error if service is already installed
else:
raise
def handle_service(svc):
if svc == SVC_RUN:
try:
win32serviceutil.StartService(TMP_Service._svc_name_)
except win32service.error as e:
if ((e[0] == winerror.ERROR_SERVICE_ALREADY_RUNNING) or
(e[0] == winerror.ERROR_ALREADY_RUNNING_LKG)):
pass # ignore failed start if the service is already running
elif e[0] == winerror.ERROR_SERVICE_DOES_NOT_EXIST:
# if the service is not installed, install it and try again
install_svc()
win32serviceutil.StartService(TMP_Service._svc_name_)
else:
# reraise any other start expection
raise
status = win32serviceutil.QueryServiceStatus(TMP_Service._svc_name_)
print("Service status: {}".format(status[1]))
else:
try:
win32serviceutil.RemoveService(TMP_Service._svc_name_)
except win32service.error as e:
if e[0] == winerror.ERROR_SERVICE_DOES_NOT_EXIST:
pass # ignore failed removal if service is not installed
else:
# reraise any other remove exception
raise
if __name__ == "__main__":
handle_service(SVC_RUN)
Reference: here