ChatGPT解决这个技术问题 Extra ChatGPT

如何在 Windows 中将 Python 脚本作为服务运行?

我正在勾画一组程序的架构,这些程序共享存储在数据库中的各种相互关联的对象。我希望其中一个程序充当服务,为对这些对象的操作提供更高级别的接口,而其他程序则通过该服务访问对象。

我目前的目标是将 Python 和 Django 框架作为实现该服务的技术。我很确定我知道如何在 Linux 中守护 Python 程序。但是,系统应该支持 Windows 是一个可选的规格项目。我几乎没有 Windows 编程经验,也完全没有 Windows 服务经验。

是否可以将 Python 程序作为 Windows 服务运行(即无需用户登录即可自动运行)?我不一定要实现这部分,但我需要一个粗略的想法,以便决定是否按照这些思路进行设计。

编辑:感谢到目前为止的所有答案,它们非常全面。我还想知道一件事:Windows 如何知道我的服务?我可以使用本机 Windows 实用程序对其进行管理吗?什么相当于在 /etc/init.d 中放置启动/停止脚本?


a
artburkart

是的你可以。我使用 ActivePython 附带的或可以与 pywin32 一起安装的 pythoncom 库(Windows 扩展的 Python)来执行此操作。

这是一个简单服务的基本框架:

import win32serviceutil
import win32service
import win32event
import servicemanager
import socket


class AppServerSvc (win32serviceutil.ServiceFramework):
    _svc_name_ = "TestService"
    _svc_display_name_ = "Test Service"

    def __init__(self,args):
        win32serviceutil.ServiceFramework.__init__(self,args)
        self.hWaitStop = win32event.CreateEvent(None,0,0,None)
        socket.setdefaulttimeout(60)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)

    def SvcDoRun(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                              servicemanager.PYS_SERVICE_STARTED,
                              (self._svc_name_,''))
        self.main()

    def main(self):
        pass

if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(AppServerSvc)

您的代码将进入 main() 方法 - 通常带有某种无限循环,可能会通过检查您在 SvcStop 方法中设置的标志来中断


编码后,我如何告诉 Windows 将其作为服务运行?
@Kit:使用带有参数“install”的命令行运行您的脚本。然后您将能够在 Windows 的服务列表中看到您的应用程序,您可以在其中启动、停止它或将其设置为自动启动
python my_script_as_service.py install ?
您特别提到了 pythoncom,并在示例代码中导入了它。问题是您从未在示例代码中的任何地方实际使用过 pythoncom,您只需要导入它。为什么要特别提到它而不显示它的用法?
为什么为 socket.setdefaulttimeout(60) 是?它是服务所需要的,还是只是从某些现有服务中意外复制的? :)
M
Michael Ekoka

最简单的方法是使用: NSSM - Non-Sucking Service Manager。只需下载并解压缩到您选择的位置。它是一个独立的实用程序,大约 300KB(远小于为此目的而安装整个 pywin32 套件)并且不需要“安装”。 zip 包含该实用程序的 64 位和 32 位版本。两者都应该在当前系统上运行良好(您可以使用 32 位版本来管理 64 位系统上的服务)。

图形用户界面方法

- 将 python 程序安装为服务。以管理员身份打开 Win 提示符

c:\>nssm.exe install WinService

- 在 NSSM 的 GUI 控制台上:

路径:C:\Python27\Python27.exe

启动目录:C:\Python27

参数:c:\WinService.py

3 - 检查 services.msc 上创建的服务

脚本方法(无 GUI)

如果您的服务应该是自动化、非交互式过程的一部分,这可能会超出您的控制范围,例如批处理或安装程序脚本,这将非常方便。假设命令以管理权限执行。

为方便起见,此处通过简单地将实用程序称为 nssm.exe 来描述这些命令。但是,建议使用完整路径 c:\path\to\nssm.exe 在脚本中更明确地引用它,因为它是一个独立的可执行文件,可能位于系统不知道的私有路径中。

1.安装服务

您必须指定服务的名称、正确 Python 可执行文件的路径以及脚本的路径:

nssm.exe install ProjectService "c:\path\to\python.exe" "c:\path\to\project\app\main.py"

更明确地说:

nssm.exe install ProjectService 
nssm.exe set ProjectService Application "c:\path\to\python.exe"
nssm.exe set ProjectService AppParameters "c:\path\to\project\app\main.py"

或者,您可能希望您的 Python 应用程序作为 Python 模块启动。一种简单的方法是告诉 nssm 它需要更改为正确的起始目录,就像您在从命令 shell 启动时所做的那样:

nssm.exe install ProjectService "c:\path\to\python.exe" "-m app.main"
nssm.exe set ProjectService AppDirectory "c:\path\to\project"

这种方法适用于虚拟环境和自包含(嵌入式)Python 安装。只需确保使用常用方法正确解决了这些环境中的任何路径问题。如果需要,nssm 可以设置环境变量(例如 PYTHONPATH),还可以启动批处理脚本。

2.启动服务

nssm.exe start ProjectService 

3.停止服务

nssm.exe stop ProjectService

<强> 4。要删除服务,请指定 confirm 参数以跳过交互式确认。

nssm.exe remove ProjectService confirm

我以前使用 nssm.exe 安装我的 Visual Studio C++ .exe 作为服务,现在我可以使用 nssm.exe 以及我的 Python .pyc 作为服务。谢谢。
注意:如果您的 *.py 脚本位于带空格的文件夹中(例如:C:\Program Files\myapp.py),则需要在引号中指定参数: 参数:“C:\Program Files\myapp.py”
如何提供虚拟环境?
不要浪费更多时间并采用 NSSM 方法。对于虚拟环境,您只需指向 virtualenv 文件夹中的 python 可执行文件。
@Adriano P 但是当我们想在客户系统上将我们的应用程序作为服务运行时?在那种情况下如何使用 nssm?
m
mknaf

尽管几周前我对所选择的答案投了赞成票,但与此同时,我在这个话题上挣扎了很多。感觉就像有一个特殊的 Python 安装和使用特殊的模块来运行一个脚本作为服务是完全错误的方式。便携性之类的呢?

我偶然发现了精彩的 Non-sucking Service Manager,它使处理 Windows 服务变得非常简单和理智。我想既然我可以将选项传递给已安装的服务,我也可以选择我的 Python 可执行文件并将我的脚本作为选项传递。

我还没有尝试过这个解决方案,但我现在会这样做,并在这个过程中更新这篇文章。我也对在 Windows 上使用 virtualenvs 感兴趣,所以我迟早会想出一个教程并在此处链接到它。


运气好的话?我正在为客户端构建一个非常简单的站点,不需要使用整个 Apache 堆栈。正如我从其他评论中看到的那样,我自己构建服务听起来也像是自找麻烦。
是的,这很有效,而且很容易做到。您只需提供脚本的路径和参数。我能够在没有控制台的情况下让我的运行,以防万一有人以某种方式结束控制台窗口。
虽然这显然可行,但还有其他困难,尤其是当您“不需要使用整个 Apache 堆栈”时:例如 gunicorn 还没有在 Windows 上运行,这实际上是我的最佳选择。
这里的诀窍是将 python.exe 作为服务运行,将你的 python 脚本作为参数运行:比如 "nssm install MyServiceName c:\python27\python.exe c:\temp\myscript.py"
效果很好!在具有多个虚拟环境的系统上,该路径可以引用所需虚拟环境的 Scripts 目录中的 Python 解释器 exe。似乎 PowerShell 中的 new-service 应该能够做到这一点,但是将脚本作为服务启动(和监视)显然涉及更多的细节,nssm 很好地处理了这些细节。
m
mohsensajjadi

实现这一点的最简单方法是使用本机命令 sc.exe:

sc create PythonApp binPath= "C:\Python34\Python.exe --C:\tmp\pythonscript.py"

参考:

https://technet.microsoft.com/en-us/library/cc990289(v=ws.11).aspx 使用 sc.exe 创建服务时如何传入上下文参数?


我认为,这是您的命令或应用程序本身的问题。无论如何,请检查此support.microsoft.com/en-us/help/886695/…
我的应用程序在服务之外运行良好,我使用了上面相同的代码但没有结果。
如何提供虚拟环境?
你试过virtualenv吗?
这行不通。 Windows 服务必须公开 pywin32 包所做的某个接口。然而,一个普通的 Python 脚本是不够的。
C
Community

几乎所有 Windows 可执行文件都可以通过多种方式安装为服务。

方法一:使用 rktools.exe 中的 instsrv 和 srvany

对于 Windows Home Server 或 Windows Server 2003(也可与 WinXP 一起使用),Windows Server 2003 Resource Kit Tools 附带了可以为此串联使用的实用程序,称为 instsrv.exesrvany.exe< /强>。有关如何使用这些实用程序的详细信息,请参阅此 Microsoft 知识库文章 KB137890

对于 Windows Home Server,有一个对这些实用程序非常友好的包装器,名称恰当地命名为“Any Service Installer”。

方法 2: 使用 Windows NT 的 ServiceInstaller

还有另一种将 ServiceInstaller for Windows NT (download-able here) 与 python instructions available 结合使用的替代方法。与名称相反,它也适用于 Windows 2000 和 Windows XP。以下是有关如何将 python 脚本安装为服务的一些说明。

安装 Python 脚本 运行 ServiceInstaller 以创建新服务。 (在此示例中,假设 python 安装在 c:\python25 )服务名称:PythonTest 显示名称:PythonTest 启动:手动(或任何您喜欢的)依赖项:(留空或填写以适应您的需要)可执行文件:c :\python25\python.exe 参数:c:\path_to_your_python_script\test.py 工作目录:c:\path_to_your_python_script 安装后,打开控制面板的服务小程序,选择并启动PythonTest服务。

在我最初的回答之后,我注意到 SO 上已经发布了密切相关的问答。也可以看看:

Can I run a Python script as a service (in Windows)? How?

How do I make Windows aware of a service I have written in Python?


我刚刚注意到已经有其他类似的问答:stackoverflow.com/questions/32404/… stackoverflow.com/questions/34328/…
Service Installer 不适用于 64 位架构,因此选项 1 成为 goto 选项。
上面指向 ServiceInstaller 的链接不再有效。我在这里找到它:sites.google.com/site/conort/…
注意,我认为 NT 不一定与名称“相反”,至少在程序员的民间演讲中不会。它只是指“NT architecture”,而不是“NT brand”。这就是说,根据 talk on wikipedia,这是有争议的,因为“这不是 Microsoft 的官方术语”,但这种思路仍然存在传统。
S
Seckin Sanli

逐步解释如何使其工作:

1-首先根据上面提到的基本骨架创建一个python文件。并将其保存到例如路径:“c:\PythonFiles\AppServerSvc.py”

import win32serviceutil
import win32service
import win32event
import servicemanager
import socket


class AppServerSvc (win32serviceutil.ServiceFramework):
    _svc_name_ = "TestService"
    _svc_display_name_ = "Test Service"


    def __init__(self,args):
        win32serviceutil.ServiceFramework.__init__(self,args)
        self.hWaitStop = win32event.CreateEvent(None,0,0,None)
        socket.setdefaulttimeout(60)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)

    def SvcDoRun(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                          servicemanager.PYS_SERVICE_STARTED,
                          (self._svc_name_,''))
        self.main()

    def main(self):
        # Your business logic or call to any class should be here
        # this time it creates a text.txt and writes Test Service in a daily manner 
        f = open('C:\\test.txt', 'a')
        rc = None
        while rc != win32event.WAIT_OBJECT_0:
            f.write('Test Service  \n')
            f.flush()
            # block for 24*60*60 seconds and wait for a stop event
            # it is used for a one-day loop
            rc = win32event.WaitForSingleObject(self.hWaitStop, 24 * 60 * 60 * 1000)
        f.write('shut down \n')
        f.close()

if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(AppServerSvc)

2 - 在这一步我们应该注册我们的服务。

以管理员身份运行命令提示符并键入:

sc create TestService binpath= "C:\Python36\Python.exe c:\PythonFiles\AppServerSvc.py" DisplayName= "TestService" start= auto

binpath的第一个参数是python.exe的路径

binpath 的第二个参数是我们已经创建的 python 文件的路径

不要错过你应该在每个“=”符号后放置一个空格。

然后如果一切正常,你应该看到

[SC] 创建服务成功

现在您的 python 服务已安装为 Windows 服务。您可以在以下服务管理器和注册表中看到它:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TestService

3- 现在好了。您可以在服务管理器上启动您的服务。

您可以执行每个提供此服务框架的 python 文件。


关于如何使用 SetEvent(self.hWaitStop)WaitForSingleObject 有很多不好的例子。可能是基于此处所选答案的轻率复制。这是一个很好的方法,它对“调试”结束和“停止”参数都有效。 (当 HandleCommandLine 完成这项工作时,关于使用 SC 的部分似乎是多余的,并且可以运行调试。)
我花了一段时间才意识到,不仅你必须在“=”符号之后放置一个空格,而且你不能在符号之前放置一个空格。
谢谢你。这很有效,而我没有设法从接受的答案中获得 install 参数来工作。
M
Matt

pysc:Service Control Manager on Python

作为服务运行的示例脚本 taken from pythonhosted.org

from xmlrpc.server import SimpleXMLRPCServer from pysc import event_stop class TestServer: def echo(self, msg): return msg if __name__ == '__main__': server = SimpleXMLRPCServer(('127.0.0.1', 9001)) @event_stop def stop( ): server.server_close() server.register_instance(TestServer()) server.serve_forever() 创建并启动服务 import os import sys from xmlrpc.client import ServerProxy import pysc if __name__ == '__main__': service_name = 'test_xmlrpc_server' script_path = os.path.join(os.path.dirname(__file__), 'xmlrpc_server.py') pysc.create( service_name=service_name, cmd=[sys.executable, script_path]) pysc.start(service_name) client = ServerProxy( 'http://127.0.0.1:9001') print(client.echo('test scm')) 停止并删除服务 import pysc service_name = 'test_xmlrpc_server' pysc.stop(service_name) pysc.delete(service_name)

pip install pysc

有谁知道为什么这会被否决?这看起来是一个不错的解决方案。
C
Community

nssm 在 python 3+ 中

(我使用 pyinstaller 将我的 .py 文件转换为 .exe)

nssm:如前所述

运行 nssm install {ServiceName}

在 NSSM 的控制台上:路径:path\to\your\program.exe 启动目录:path\to\your\ #same as the path but without your program.exe 参数:空

如果您不想将项目转换为 .exe

使用 python {{您的 python.py 文件名}} 创建一个 .bat 文件

并设置 .bat 文件的路径


如何提供虚拟环境?
f
flam3

我开始使用 pywin32 作为服务托管。

一切都很好,但我遇到了系统启动时服务无法在 30 秒内启动(Windows 默认超时)的问题。这对我来说至关重要,因为 Windows 启动是在一台物理机上托管的多个虚拟机上同时进行的,并且 IO 负载非常大。错误消息是:

Error 1053: The service did not respond to the start or control request in a timely fashion.

Error 7009: Timeout (30000 milliseconds) waiting for the <ServiceName> service to connect.

我与 pywin 进行了很多斗争,但最终还是使用了建议的 NSSM in this answer。迁移到它非常容易。


C
Community

使用循环或子线程的完整 pywin32 示例

在断断续续地工作了几天之后,这是我希望找到的答案,使用 pywin32 来保持它的美观和独立。

这是一个基于循环和一个基于线程的解决方案的完整工作代码。它可能适用于 python 2 和 3,尽管我只在 2.7 和 Win7 上测试了最新版本。循环应该适用于轮询代码,而线程应该适用于更多类似服务器的代码。它似乎与没有标准方式正常关闭的 waitress wsgi 服务器很好地配合使用。

我还想指出,那里似乎有很多示例,例如 this,它们几乎有用,但实际上具有误导性,因为它们盲目地剪切和粘贴了其他示例。我可能是错的。但是,如果您从不等待它,为什么还要创建一个事件呢?

也就是说,我仍然觉得我在这里有点不稳定,特别是关于线程版本的退出有多干净,但至少我相信这里没有任何误导。

要运行,只需将代码复制到文件并按照说明进行操作。

更新:

使用一个简单的标志来终止线程。重要的是打印“线程完成”。
有关从不合作的服务器线程退出的更详细的示例,请参阅我的 post about the waitress wsgi server

# uncomment mainthread() or mainloop() call below
# run without parameters to see HandleCommandLine options
# install service with "install" and remove with "remove"
# run with "debug" to see print statements
# with "start" and "stop" watch for files to appear
# check Windows EventViever for log messages

import socket
import sys
import threading
import time
from random import randint
from os import path

import servicemanager
import win32event
import win32service
import win32serviceutil
# see http://timgolden.me.uk/pywin32-docs/contents.html for details


def dummytask_once(msg='once'):
    fn = path.join(path.dirname(__file__),
                '%s_%s.txt' % (msg, randint(1, 10000)))
    with open(fn, 'w') as fh:
        print(fn)
        fh.write('')


def dummytask_loop():
    global do_run
    while do_run:
        dummytask_once(msg='loop')
        time.sleep(3)


class MyThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        global do_run
        do_run = True
        print('thread start\n')
        dummytask_loop()
        print('thread done\n')

    def exit(self):
        global do_run
        do_run = False


class SMWinservice(win32serviceutil.ServiceFramework):
    _svc_name_ = 'PyWinSvc'
    _svc_display_name_ = 'Python Windows Service'
    _svc_description_ = 'An example of a windows service in Python'

    @classmethod
    def parse_command_line(cls):
        win32serviceutil.HandleCommandLine(cls)

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.stopEvt = win32event.CreateEvent(None, 0, 0, None)  # create generic event
        socket.setdefaulttimeout(60)

    def SvcStop(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                            servicemanager.PYS_SERVICE_STOPPED,
                            (self._svc_name_, ''))
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.stopEvt)  # raise event

    def SvcDoRun(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                            servicemanager.PYS_SERVICE_STARTED,
                            (self._svc_name_, ''))
        # UNCOMMENT ONE OF THESE
        # self.mainthread()
        # self.mainloop()

    # Wait for stopEvt indefinitely after starting thread.
    def mainthread(self):
        print('main start')
        self.server = MyThread()
        self.server.start()
        print('wait for win32event')
        win32event.WaitForSingleObject(self.stopEvt, win32event.INFINITE)
        self.server.exit()
        print('wait for thread')
        self.server.join()
        print('main done')

    # Wait for stopEvt event in loop.
    def mainloop(self):
        print('loop start')
        rc = None
        while rc != win32event.WAIT_OBJECT_0:
            dummytask_once()
            rc = win32event.WaitForSingleObject(self.stopEvt, 3000)
        print('loop done')


if __name__ == '__main__':
    SMWinservice.parse_command_line()

这是一个在后台运行的程序,没有控制台,打印命令在哪里输出消息?
打印出来是为了证明它可以在 CLI 上运行。 IIRC 它们也将在作为带有调试标志的服务运行时显示。
n
ndemou

使用 win32serviceutil 的公认答案有效,但很复杂,使调试和更改更加困难。 更容易使用 NSSM(the Non-Sucking Service Manager)。您编写并轻松调试一个普通的 Python 程序,当它最终工作时,您使用 NSSM 将其作为服务安装在少于一分钟:

从提升的(管理员)命令提示符运行 nssm.exe install NameOfYourService 并填写以下选项:

路径:(python.exe 的路径,例如 C:\Python27\Python.exe)

参数:(python 脚本的路径,例如 c:\path\to\program.py)

顺便说一句,如果您的程序打印出有用的消息,并且您希望将这些消息保存在日志文件中,NSSM 也可以为您处理这个以及更多的事情。


是的,这是阿德里亚诺答案的副本。我赞成该答案并尝试对其进行编辑,但在编辑后我正在寻找一个新答案。
如何提供虚拟环境?
R
Russell McDonell

这个答案是来自 StackOverflow 上的几个来源的剽窃者——大部分都在上面,但我忘记了其他的——对不起。这很简单,脚本“按原样”运行。对于测试脚本的版本,然后将其复制到服务器并停止/启动相关服务。它应该适用于所有脚本语言(Python、Perl、node.js),以及 GitBash、PowerShell 等批处理脚本,甚至是旧的 DOS bat 脚本。 pyGlue 是位于 Windows 服务和脚本之间的粘合剂。

'''
A script to create a Windows Service, which, when started, will run an executable with the specified parameters.
Optionally, you can also specify a startup directory

To use this script you MUST define (in class Service)
1. A name for your service (short - preferably no spaces)
2. A display name for your service (the name visibile in Windows Services)
3. A description for your service (long details visible when you inspect the service in Windows Services)
4. The full path of the executable (usually C:/Python38/python.exe or C:WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe
5. The script which Python or PowerShell will run(or specify None if your executable is standalone - in which case you don't need pyGlue)
6. The startup directory (or specify None)
7. Any parameters for your script (or for your executable if you have no script)

NOTE: This does not make a portable script.
The associated '_svc_name.exe' in the dist folder will only work if the executable,
(and any optional startup directory) actually exist in those locations on the target system

Usage: 'pyGlue.exe [options] install|update|remove|start [...]|stop|restart [...]|debug [...]'
Options for 'install' and 'update' commands only:
        --username domain\\username : The Username the service is to run under
        --password password : The password for the username
        --startup [manual|auto|disabled|delayed] : How the service starts, default = manual
        --interactive : Allow the service to interact with the desktop.
        --perfmonini file: .ini file to use for registering performance monitor data
        --perfmondll file: .dll file to use when querying the service for performance data, default = perfmondata.dll
Options for 'start' and 'stop' commands only:
        --wait seconds: Wait for the service to actually start or stop.
                If you specify --wait with the 'stop' option, the service and all dependent services will be stopped,
                each waiting the specified period.
'''

# Import all the modules that make life easy
import servicemanager
import socket
import sys
import win32event
import win32service
import win32serviceutil
import win32evtlogutil
import os
from logging import Formatter, Handler
import logging
import subprocess


# Define the win32api class
class Service (win32serviceutil.ServiceFramework):
        # The following variable are edited by the build.sh script
        _svc_name_ = "TestService"
        _svc_display_name_ = "Test Service"
        _svc_description_ = "Test Running Python Scripts as a Service"
        service_exe = 'c:/Python27/python.exe'
        service_script = None
        service_params = []
        service_startDir = None

        # Initialize the service
        def __init__(self, args):
                win32serviceutil.ServiceFramework.__init__(self, args)
                self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
                self.configure_logging()
                socket.setdefaulttimeout(60)

        # Configure logging to the WINDOWS Event logs
        def configure_logging(self):
                self.formatter = Formatter('%(message)s')
                self.handler = logHandler()
                self.handler.setFormatter(self.formatter)
                self.logger = logging.getLogger()
                self.logger.addHandler(self.handler)
                self.logger.setLevel(logging.INFO)

        # Stop the service
        def SvcStop(self):
                self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
                win32event.SetEvent(self.hWaitStop)

        # Run the service
        def SvcDoRun(self):
                self.main()

        # This is the service
        def main(self):

                # Log that we are starting
                servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED,
                                                          (self._svc_name_, ''))

                # Fire off the real process that does the real work
                logging.info('%s - about to call Popen() to run %s %s %s', self._svc_name_, self.service_exe, self.service_script, self.service_params)
                self.process = subprocess.Popen([self.service_exe, self.service_script] + self.service_params, shell=False, cwd=self.service_startDir)
                logging.info('%s - started process %d', self._svc_name_, self.process.pid)

                # Wait until WINDOWS kills us - retrigger the wait for stop every 60 seconds
                rc = None
                while rc != win32event.WAIT_OBJECT_0:
                        rc = win32event.WaitForSingleObject(self.hWaitStop, (1 * 60 * 1000))

                # Shut down the real process and exit
                logging.info('%s - is terminating process %d', self._svc_name_, self.process.pid)
                self.process.terminate()
                logging.info('%s - is exiting', self._svc_name_)


class logHandler(Handler):
        '''
Emit a log record to the WINDOWS Event log
        '''

        def emit(self, record):
                servicemanager.LogInfoMsg(record.getMessage())


# The main code
if __name__ == '__main__':
        '''
Create a Windows Service, which, when started, will run an executable with the specified parameters.
        '''

        # Check that configuration contains valid values just in case this service has accidentally
        # been moved to a server where things are in different places
        if not os.path.isfile(Service.service_exe):
                print('Executable file({!s}) does not exist'.format(Service.service_exe), file=sys.stderr)
                sys.exit(0)
        if not os.access(Service.service_exe, os.X_OK):
                print('Executable file({!s}) is not executable'.format(Service.service_exe), file=sys.stderr)
                sys.exit(0)
        # Check that any optional startup directory exists
        if (Service.service_startDir is not None) and (not os.path.isdir(Service.service_startDir)):
                print('Start up directory({!s}) does not exist'.format(Service.service_startDir), file=sys.stderr)
                sys.exit(0)

        if len(sys.argv) == 1:
                servicemanager.Initialize()
                servicemanager.PrepareToHostSingle(Service)
                servicemanager.StartServiceCtrlDispatcher()
        else:
                # install/update/remove/start/stop/restart or debug the service
                # One of those command line options must be specified
                win32serviceutil.HandleCommandLine(Service)

现在有一些编辑,你不希望你的所有服务都称为“pyGlue”。所以有一个脚本 (build.sh) 可以插入这些位并创建一个自定义的“pyGlue”并创建一个“.exe”。正是这个“.exe”作为 Windows 服务安装。安装后,您可以将其设置为自动运行。

#!/bin/sh
# This script build a Windows Service that will install/start/stop/remove a service that runs a script
# That is, executes Python to run a Python script, or PowerShell to run a PowerShell script, etc

if [ $# -lt 6 ]; then
        echo "Usage: build.sh Name Display Description Executable Script StartupDir [Params]..."
        exit 0
fi

name=$1
display=$2
desc=$3
exe=$4
script=$5
startDir=$6
shift; shift; shift; shift; shift; shift
params=
while [ $# -gt 0 ]; do
        if [ "${params}" != "" ]; then
                params="${params}, "
        fi
        params="${params}'$1'"
        shift
done

cat pyGlue.py | sed -e "s/pyGlue/${name}/g" | \
        sed -e "/_svc_name_ =/s?=.*?= '${name}'?" | \
        sed -e "/_svc_display_name_ =/s?=.*?= '${display}'?" | \
        sed -e "/_svc_description_ =/s?=.*?= '${desc}'?" | \
        sed -e "/service_exe =/s?=.*?= '$exe'?" | \
        sed -e "/service_script =/s?=.*?= '$script'?" | \
        sed -e "/service_params =/s?=.*?= [${params}]?" | \
        sed -e "/service_startDir =/s?=.*?= '${startDir}'?" > ${name}.py

cxfreeze ${name}.py --include-modules=win32timezone

安装 - 将“.exe”服务器和脚本复制到指定文件夹。使用“安装”选项以管理员身份运行“.exe”。以管理员身份打开 Windows 服务,然后启动您的服务。对于升级,只需复制新版本的脚本并停止/启动服务。

现在每台服务器都是不同的——不同的 Python 安装,不同的文件夹结构。我为每个服务器维护一个文件夹,其中包含 pyGlue.py 和 build.sh 的副本。我创建了一个“serverBuild.sh”脚本来重建该服务器上的所有服务。

# A script to build all the script based Services on this PC
sh build.sh AutoCode 'AutoCode Medical Documents' 'Autocode Medical Documents to SNOMED_CT and AIHW codes' C:/Python38/python.exe autocode.py C:/Users/russell/Documents/autocoding -S -T

M
Matthias Luh

这不能回答最初的问题,但可能会帮助其他想要在 Windows 启动时自动启动 Python 脚本的人:看看 Windows Task Scheduler,如果您只想在启动后启动脚本,这会更容易没有 Windows 服务的所有服务功能。

创建一个新任务,选择“启动时”作为触发器,选择“启动程序”作为操作,将“C:\Python39\python.exe”作为程序(或任何你的 python.exe 所在的位置)和脚本的完整路径( “C:...\my_dir\xyz.py”)作为参数(如果路径包含空格,您可以使用“)。您还可以选择脚本的路径(没有 .py 文件,例如“C:.. .\my_dir") 如果您在脚本中使用相对路径,例如用于记录,则表示“开始”。


W
Werner Henze

https://www.chrisumbel.com/article/windows_services_in_python

跟进 PySvc.py 更改 dll 文件夹

我知道这很旧,但我一直坚持这一点。对我来说,这个特定的问题是通过复制这个文件解决的 - pywintypes36.dll

从 -> Python36\Lib\site-packages\pywin32_system32

到 -> Python36\Lib\site-packages\win32

setx /M PATH "%PATH%;C:\Users\user\AppData\Local\Programs\Python\Python38-32;C:\Users\user\AppData\Local\Programs\Python\Python38-32\Scripts;C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\pywin32_system32;C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\win32

更改python文件夹的路径

cd C:\Users\user\AppData\Local\Programs\Python\Python38-32

网络启动 PySvc 网络停止 PySvc