编辑:如我在下面的评论中所提到的,这在pyWin32 305及更高版本中不再需要,它会自动将pythonservice.exe复制到sys.exec_prefix(即:如果您有一个虚拟环境,则在其中)并从那里运行。
如
dslosky和
this answer to a linked question所暗示的,pythonservice.exe将作为一个“系统”服务运行,因此它将拥有与您作为用户不同的环境。运行
python service.py debug
将正常运行,因为它仍然在您的用户环境下运行,但是如果您运行
python service.py start
,由于环境变量的差异,它很可能会失败。您的服务立即超时很可能是由于pythonservice.exe无法执行,并且如果缺少
PythonXX.dll
或
pywintypesXX.dll
,它将无法执行。
PythonXX.dll
很可能已经在您的系统路径中(取决于Python的安装方式),但是如果您在同一系统上使用多个Python版本并且尝试避免更改环境(就像我不幸的那样),那将是一个问题。我希望通过运行类似于.\.pyenv37\Scripts\python.exe service.py start
的命令来使用venv,这对于定期运行的常规Python脚本是有效的,但是您必须记住service.py start
只是命令Windows启动服务。实际的服务可执行文件pythonservice.exe
将从PATH变量中解析Python37.dll,而不是我在运行service.py start
时使用的venv。这意味着当pythonservice.exe作为系统服务开始运行时,Python37.dll将不再被识别,即使我在用户PATH中有Python37.dll,因为它现在正在使用系统PATH运行。由于pythonservice.exe无法在没有Python37.dll的情况下运行,这会导致立即的链接错误,并且Windows将其报告为即时超时。
同样适用于
pywintypesXX.dll
(如果找不到它,pythonservice.exe将立即超时),但与其在搜索路径中的某个位置安装它,更便携的解决方案是将其放在与
pythonservice.exe
相同的目录中,因为
默认的DLL搜索路径包括它。
编辑:这是我用来验证脚本安装/更新的内容:
if __name__=='__main__':
win32serviceutil.HandleCommandLine(AppServerSvc, customOptionHandler=post_service_update)
.
def post_service_update(*args):
import win32api, win32con, win32profile, pywintypes
from contextlib import closing
env_reg_key = "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"
hkey = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE, env_reg_key, 0, win32con.KEY_ALL_ACCESS)
with closing(hkey):
system_path = win32api.RegQueryValueEx(hkey, 'PATH')[0]
system_path = win32profile.ExpandEnvironmentStringsForUser(None, system_path)
system_path_list = system_path.split(os.pathsep)
core_dll_file = win32api.GetModuleFileName(sys.dllhandle)
core_dll_name = os.path.basename(core_dll_file)
for search_path_dir in system_path_list:
try:
dll_path = win32api.SearchPath(search_path_dir, core_dll_name)[0]
print(f"System python DLL: {dll_path}")
break
except pywintypes.error as ex:
if ex.args[1] != 'SearchPath': raise
continue
else:
print("*** WARNING ***")
print(f"Your current Python DLL ({core_dll_name}) is not in your SYSTEM PATH")
print("The service is likely to not launch correctly.")
from win32serviceutil import LocatePythonServiceExe
pythonservice_exe = LocatePythonServiceExe()
pywintypes_dll_file = pywintypes.__spec__.origin
pythonservice_path = os.path.dirname(pythonservice_exe)
pywintypes_dll_name = os.path.basename(pywintypes_dll_file)
try:
return win32api.SearchPath(pythonservice_path, pywintypes_dll_name)[0]
except pywintypes.error as ex:
if ex.args[1] != 'SearchPath': raise
print("*** WARNING ***")
print(f"{pywintypes_dll_name} is not is the same directory as pythonservice.exe")
print(f'Copy "{pywintypes_dll_file}" to "{pythonservice_path}"')
print("The service is likely to not launch correctly.")
虽然看起来很多,但至少可以避免在部署服务到新的机器/虚拟环境或更新Python时忘记执行这些步骤。