简体   繁体   English

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

[英]How do you run a Python script as a service in Windows?

I am sketching the architecture for a set of programs that share various interrelated objects stored in a database.我正在勾画一组程序的架构,这些程序共享存储在数据库中的各种相互关联的对象。 I want one of the programs to act as a service which provides a higher level interface for operations on these objects, and the other programs to access the objects through that service.我希望其中一个程序充当服务,为对这些对象的操作提供更高级别的接口,而其他程序则通过该服务访问对象。

I am currently aiming for Python and the Django framework as the technologies to implement that service with.我目前的目标是将 Python 和 Django 框架作为实现该服务的技术。 I'm pretty sure I figure how to daemonize the Python program in Linux.我很确定我知道如何在 Linux 中守护 Python 程序。 However, it is an optional spec item that the system should support Windows.但是,系统应该支持 Windows 是一个可选的规格项目。 I have little experience with Windows programming and no experience at all with Windows services.我几乎没有 Windows 编程经验,也完全没有 Windows 服务经验。

Is it possible to run a Python programs as a Windows service (ie run it automatically without user login)?是否可以将 Python 程序作为 Windows 服务运行(即无需用户登录即可自动运行)? I won't necessarily have to implement this part, but I need a rough idea how it would be done in order to decide whether to design along these lines.我不一定要实现这部分,但我需要一个粗略的想法,以便决定是否按照这些思路进行设计。

Edit: Thanks for all the answers so far, they are quite comprehensive.编辑:感谢到目前为止的所有答案,它们非常全面。 I would like to know one more thing: How is Windows aware of my service?我还想知道一件事: Windows 如何知道我的服务? Can I manage it with the native Windows utilities?我可以使用本机 Windows 实用程序对其进行管理吗? What is the equivalent of putting a start/stop script in /etc/init.d?什么相当于在 /etc/init.d 中放置启动/停止脚本?

Yes you can.是的你可以。 I do it using the pythoncom libraries that come included with ActivePython or can be installed with pywin32 (Python for Windows extensions).我使用ActivePython附带的 pythoncom 库来完成它,或者可以使用pywin32 (Windows 扩展的 Python)安装。

This is a basic skeleton for a simple service:这是一个简单服务的基本框架:

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)

Your code would go in the main() method—usually with some kind of infinite loop that might be interrupted by checking a flag, which you set in the SvcStop method您的代码将进入main()方法 - 通常带有某种无限循环,可能会通过检查您在SvcStop方法中设置的标志来中断

The simplest way is to use the: NSSM - the Non-Sucking Service Manager.最简单的方法是使用: NSSM - Non-Sucking Service Manager。 Just download and unzip to a location of your choosing.只需下载并解压缩到您选择的位置。 It's a self-contained utility, around 300KB (much less than installing the entire pywin32 suite just for this purpose) and no "installation" is needed.它是一个独立的实用程序,大约 300KB(远小于为此目的而安装整个 pywin32 套件)并且不需要“安装”。 The zip contains a 64-bit and a 32-bit version of the utility. zip 包含该实用程序的 64 位和 32 位版本。 Either should work well on current systems (you can use the 32-bit version to manage services on 64-bit systems).两者都应该在当前系统上运行良好(您可以使用 32 位版本来管理 64 位系统上的服务)。

GUI approach图形用户界面方法

1 - install the python program as a service. 1 - 将 python 程序安装为服务。 Open a Win prompt as admin以管理员身份打开 Win 提示符

c:\>nssm.exe install WinService

2 - On NSSM´s GUI console: 2 - 在 NSSM 的 GUI 控制台上:

path: C:\Python27\Python27.exe路径:C:\Python27\Python27.exe

Startup directory: C:\Python27启动目录:C:\Python27

Arguments: c:\WinService.py参数:c:\WinService.py

3 - check the created services on services.msc 3 - 检查 services.msc 上创建的服务

Scripting approach (no GUI)脚本方法(无 GUI)

This is handy if your service should be part of an automated, non-interactive procedure, that may be beyond your control, such as a batch or installer script.如果您的服务应该是自动化、非交互式过程的一部分,这可能会超出您的控制范围,例如批处理或安装程序脚本,这将非常方便。 It is assumed that the commands are executed with administrative privileges.假设命令以管理权限执行。

For convenience the commands are described here by simply referring to the utility as nssm.exe .为方便起见,此处通过简单地将实用程序称为nssm.exe来描述命令。 It is advisable, however, to refer to it more explicitly in scripting with its full path c:\path\to\nssm.exe , since it's a self-contained executable that may be located in a private path that the system is not aware of.但是,建议在脚本中使用其完整路径c:\path\to\nssm.exe更明确地引用它,因为它是一个独立的可执行文件,可能位于系统不知道的私有路径中的。

1. Install the service 1.安装服务

You must specify a name for the service, the path to the proper Python executable, and the path to the script:您必须指定服务的名称、正确 Python 可执行文件的路径以及脚本的路径:

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

More explicitly:更明确地说:

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"

Alternatively you may want your Python app to be started as a Python module.或者,您可能希望您的 Python 应用程序作为 Python 模块启动。 One easy approach is to tell nssm that it needs to change to the proper starting directory, as you would do yourself when launching from a command shell:一种简单的方法是告诉 nssm 它需要更改为正确的起始目录,就像您在从命令 shell 启动时所做的那样:

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

This approach works well with virtual environments and self-contained (embedded) Python installs.这种方法适用于虚拟环境和自包含(嵌入式)Python 安装。 Just make sure to have properly resolved any path issues in those environments with the usual methods.只需确保使用常用方法正确解决了这些环境中的任何路径问题。 nssm has a way to set environment variables (eg PYTHONPATH) if needed, and can also launch batch scripts.如果需要,nssm 可以设置环境变量(例如 PYTHONPATH),还可以启动批处理脚本。

2. To start the service 2.启动服务

nssm.exe start ProjectService 

3. To stop the service 3.停止服务

nssm.exe stop ProjectService

4. To remove the service , specify the confirm parameter to skip the interactive confirmation. 4. 移除服务,指定confirm参数跳过交互确认。

nssm.exe remove ProjectService confirm

Although I upvoted the chosen answer a couple of weeks back, in the meantime I struggled a lot more with this topic.尽管几周前我对所选择的答案投了赞成票,但与此同时,我在这个话题上挣扎了很多。 It feels like having a special Python installation and using special modules to run a script as a service is simply the wrong way.感觉就像有一个特殊的 Python 安装和使用特殊的模块来运行一个脚本作为服务是完全错误的方式。 What about portability and such?便携性之类的呢?

I stumbled across the wonderful Non-sucking Service Manager , which made it really simple and sane to deal with Windows Services.我偶然发现了出色的Non-sucking Service Manager ,它使处理 Windows 服务变得非常简单和理智。 I figured since I could pass options to an installed service, I could just as well select my Python executable and pass my script as an option.我想既然我可以将选项传递给已安装的服务,我也可以选择我的 Python 可执行文件并将我的脚本作为选项传递。

I have not yet tried this solution, but I will do so right now and update this post along the process.我还没有尝试过这个解决方案,但我现在会这样做,并在这个过程中更新这篇文章。 I am also interested in using virtualenvs on Windows, so I might come up with a tutorial sooner or later and link to it here.我也对在 Windows 上使用 virtualenvs 感兴趣,所以我迟早会想出一个教程并在此处链接到它。

The simplest way to achieve this is to use native command sc.exe:实现这一点的最简单方法是使用本机命令 sc.exe:

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

References:参考:

  1. https://technet.microsoft.com/en-us/library/cc990289(v=ws.11).aspx https://technet.microsoft.com/en-us/library/cc990289(v=ws.11).aspx
  2. When creating a service with sc.exe how to pass in context parameters? 使用 sc.exe 创建服务时如何传入上下文参数?

There are a couple alternatives for installing as a service virtually any Windows executable.几乎所有 Windows 可执行文件都可以通过多种方式安装为服务。

Method 1: Use instsrv and srvany from rktools.exe方法一:使用 rktools.exe 中的 instsrv 和 srvany

For Windows Home Server or Windows Server 2003 (works with WinXP too), the Windows Server 2003 Resource Kit Tools comes with utilities that can be used in tandem for this, called instsrv.exe and srvany.exe .对于 Windows Home Server 或 Windows Server 2003(也可与 WinXP 一起使用), Windows Server 2003 资源工具包工具附带了可以为此串联使用的实用程序,称为instsrv.exesrvany.exe See this Microsoft KB article KB137890 for details on how to use these utils.有关如何使用这些实用程序的详细信息,请参阅此 Microsoft 知识库文章KB137890

For Windows Home Server, there is a great user friendly wrapper for these utilities named aptly " Any Service Installer ".对于 Windows Home Server,这些实用程序有一个很好的用户友好包装器,其名称恰如其分地命名为“ Any Service Installer ”。

Method 2: Use ServiceInstaller for Windows NT方法 2: 使用 Windows NT 的 ServiceInstaller

There is another alternative using ServiceInstaller for Windows NT ( download-able here ) with python instructions available .还有另一种方法是使用Windows NT 的 ServiceInstaller可在此处下载),并提供python 指令 Contrary to the name, it works with both Windows 2000 and Windows XP as well.与名称相反,它也适用于 Windows 2000 和 Windows XP。 Here are some instructions for how to install a python script as a service.以下是有关如何将 python 脚本安装为服务的一些说明。

Installing a Python script安装 Python 脚本

Run ServiceInstaller to create a new service.运行 ServiceInstaller 以创建新服务。 (In this example, it is assumed that python is installed at c:\python25) (本例假设python安装在c:\python25)

 Service Name : PythonTest Display Name : PythonTest Startup : Manual (or whatever you like) Dependencies : (Leave blank or fill to fit your needs) Executable : c:\python25\python.exe Arguments : c:\path_to_your_python_script\test.py Working Directory : c:\path_to_your_python_script

After installing, open the Control Panel's Services applet, select and start the PythonTest service.安装后,打开控制面板的服务小程序,选择并启动PythonTest服务。

After my initial answer, I noticed there were closely related Q&A already posted on SO.在我最初的回答之后,我注意到 SO 上已经发布了密切相关的问答。 See also:也可以看看:

Can I run a Python script as a service (in Windows)? 我可以将 Python 脚本作为服务运行(在 Windows 中)吗? How? 如何?

How do I make Windows aware of a service I have written in Python? 如何让 Windows 知道我用 Python 编写的服务?

Step by step explanation how to make it work :逐步解释如何使其工作:

1- First create a python file according to the basic skeleton mentioned above. 1-首先根据上面提到的基本骨架创建一个python文件。 And save it to a path for example : "c:\PythonFiles\AppServerSvc.py"并将其保存到例如路径:“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 - On this step we should register our service. 2 - 在这一步我们应该注册我们的服务。

Run command prompt as administrator and type as:管理员身份运行命令提示符并键入:

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

the first argument of binpath is the path of python.exe binpath的第一个参数是python.exe的路径

second argument of binpath is the path of your python file that we created already binpath的第二个参数是我们已经创建的 python 文件的路径

Don't miss that you should put one space after every " = " sign.不要错过你应该在每个“ = ”符号后放置一个空格。

Then if everything is ok, you should see然后如果一切正常,你应该看到

[SC] CreateService SUCCESS [SC] 创建服务成功

Now your python service is installed as windows service now.现在您的 python 服务已安装为 Windows 服务。 You can see it in Service Manager and registry under :您可以在以下服务管理器和注册表中看到它:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TestService HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TestService

3- Ok now. 3- 现在好了。 You can start your service on service manager.您可以在服务管理器上启动您的服务。

You can execute every python file that provides this service skeleton.您可以执行每个提供此服务框架的 python 文件。

pysc: Service Control Manager on Python pysc: Python 上的服务控制管理器

Example script to run as a service taken from pythonhosted.org :作为从 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()

Create and start service创建并启动服务

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'))

Stop and delete service停止和删除服务

import pysc service_name = 'test_xmlrpc_server' pysc.stop(service_name) pysc.delete(service_name)
pip install pysc

nssm in python 3+ python中的nssm 3+

(I converted my .py file to .exe with pyinstaller ) (我使用pyinstaller将我的 .py 文件转换为 .exe )

nssm: as said before nssm:如前所述

  • run nssm install {ServiceName}运行 nssm install {ServiceName}
  • On NSSM´s console:在 NSSM 的控制台上:

    path: path\to\your\program.exe路径: path\to\your\program.exe

    Startup directory: path\to\your\ #same as the path but without your program.exe启动目录: path\to\your\ #与路径相同但没有你的program.exe

    Arguments: empty参数:空

If you don't want to convert your project to .exe如果您不想将项目转换为 .exe

  • create a .bat file with python {{your python.py file name}}使用python {{your python.py file name}}创建一个 .bat 文件
  • and set the path to the .bat file并设置 .bat 文件的路径

I started hosting as a service with pywin32 .我开始使用pywin32作为服务托管。

Everything was well but I met the problem that service was not able to start within 30 seconds (default timeout for Windows) on system startup.一切都很好,但我遇到了系统启动时服务无法在 30 秒内启动(Windows 默认超时)的问题。 It was critical for me because Windows startup took place simultaneous on several virtual machines hosted on one physical machine, and IO load was huge.这对我来说至关重要,因为 Windows 启动是在一台物理机上托管的多个虚拟机上同时进行的,并且 IO 负载非常大。 Error messages were:错误消息是:

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.

I fought a lot with pywin, but ended up with using NSSM as it was proposed in this answer .我与 pywin 进行了很多斗争,但最终使用了 NSSM,因为它是在这个答案中提出的。 It was very easy to migrate to it.迁移到它非常容易。

A complete pywin32 example using loop or subthread使用循环或子线程的完整 pywin32 示例

After working on this on and off for a few days, here is the answer I would have wished to find, using pywin32 to keep it nice and self contained.在断断续续地工作了几天之后,这是我希望找到的答案,使用 pywin32 来保持它的美观和独立。

This is complete working code for one loop-based and one thread-based solution.这是一个基于循环和一个基于线程的解决方案的完整工作代码。 It may work on both python 2 and 3, although I've only tested the latest version on 2.7 and Win7.它可能适用于 python 2 和 3,尽管我只在 2.7 和 Win7 上测试了最新版本。 The loop should be good for polling code, and the tread should work with more server-like code.循环应该适用于轮询代码,而线程应该适用于更多类似服务器的代码。 It seems to work nicely with the waitress wsgi server that does not have a standard way to shut down gracefully.它似乎与没有标准方式正常关闭的女服务员wsgi 服务器很好地配合使用。

I would also like to note that there seems to be loads of examples out there, like this that are almost useful, but in reality misleading, because they have cut and pasted other examples blindly.我还想指出,那里似乎有很多示例,像这样几乎有用,但实际上具有误导性,因为他们盲目地剪切和粘贴了其他示例。 I could be wrong.我可能是错的。 but why create an event if you never wait for it?但是,如果您从不等待它,为什么还要创建一个事件呢?

That said I still feel I'm on somewhat shaky ground here, especially with regards to how clean the exit from the thread version is, but at least I believe there are nothing misleading here.也就是说,我仍然觉得我在这里有点不稳定,特别是关于线程版本的退出有多干净,但至少我相信这里没有任何误导

To run simply copy the code to a file and follow the instructions.要运行,只需将代码复制到文件并按照说明进行操作。

update:更新:

Use a simple flag to terminate thread.使用一个简单的标志来终止线程。 The important bit is that "thread done" prints.重要的是“线程完成”打印。
For a more elaborate example exiting from an uncooperative server thread see my post about the waitress wsgi server .有关从不合作的服务器线程退出的更详细的示例,请参阅我关于女服务员 wsgi 服务器的帖子

# 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()

The accepted answer using win32serviceutil works but is complicated and makes debugging and changes harder.使用win32serviceutil的公认答案有效,但很复杂,并且使调试和更改更加困难。 It is far easier to use NSSM ( the Non-Sucking Service Manager) .使用 NSSM(非吸吮服务管理器)要容易得多 You write and comfortably debug a normal python program and when it finally works you use NSSM to install it as a service in less than a minute:您编写并舒适地调试一个普通的 python 程序,当它最终运行时,您使用 NSSM 在不到一分钟的时间内将其安装为服务:

From an elevated (admin) command prompt you run nssm.exe install NameOfYourService and you fill-in these options:从提升的(管理员)命令提示符运行nssm.exe install NameOfYourService并填写以下选项:

  • path : (the path to python.exe eg C:\Python27\Python.exe ) path : (python.exe 的路径,例如C:\Python27\Python.exe )
  • Arguments : (the path to your python script, eg c:\path\to\program.py )参数:(python 脚本的路径,例如c:\path\to\program.py

By the way, if your program prints useful messages that you want to keep in a log file NSSM can also handle this and a lot more for you.顺便说一句,如果您的程序打印出有用的消息,并且您希望将这些消息保存在日志文件中,NSSM 也可以为您处理这个以及更多的事情。

This answer is plagiarizer from several sources on StackOverflow - most of them above, but I've forgotten the others - sorry.这个答案是来自 StackOverflow 上的几个来源的剽窃者——大部分都在上面,但我忘记了其他的——对不起。 It's simple and scripts run "as is".这很简单,脚本“按原样”运行。 For releases you test you script, then copy it to the server and Stop/Start the associated service.对于测试脚本的版本,然后将其复制到服务器并停止/启动相关服务。 And it should work for all scripting languages (Python, Perl, node.js), plus batch scripts such as GitBash, PowerShell, even old DOS bat scripts.它应该适用于所有脚本语言(Python、Perl、node.js),以及 GitBash、PowerShell 等批处理脚本,甚至是旧的 DOS bat 脚本。 pyGlue is the glue that sits between Windows Services and your script. 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)

Now there's a bit of editing and you don't want all your services called 'pyGlue'.现在有一些编辑,你不希望你的所有服务都称为“pyGlue”。 So there's a script (build.sh) to plug in the bits and create a customized 'pyGlue' and create an '.exe'.所以有一个脚本 (build.sh) 可以插入这些位并创建一个自定义的“pyGlue”并创建一个“.exe”。 It is this '.exe' which gets installed as a Windows Service.正是这个“.exe”作为 Windows 服务安装。 Once installed you can set it to run automatically.安装后,您可以将其设置为自动运行。

#!/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

Installation - copy the '.exe' the server and the script to the specified folder.安装 - 将“.exe”服务器和脚本复制到指定文件夹。 Run the '.exe', as Administrator, with the 'install' option.使用“安装”选项以管理员身份运行“.exe”。 Open Windows Services, as Adminstrator, and start you service.以管理员身份打开 Windows 服务,然后启动您的服务。 For upgrade, just copy the new version of the script and Stop/Start the service.对于升级,只需复制新版本的脚本并停止/启动服务。

Now every server is different - different installations of Python, different folder structures.现在每台服务器都是不同的——不同的 Python 安装,不同的文件夹结构。 I maintain a folder for every server, with a copy of pyGlue.py and build.sh.我为每个服务器维护一个文件夹,其中包含 pyGlue.py 和 build.sh 的副本。 And I create a 'serverBuild.sh' script for rebuilding all the service on that server.我创建了一个“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

For anyone who want to create service in VENV or Pycharm !!!!!!! 对于想要在VENV或Pycharm中创建服务的人!!!!!!!

After reading all the anwsers and create some scripts, if you can run python service.py install and python service.py debug , but python service.py start has no response. 读完所有的anwsers并创建一些脚本后,如果你可以运行python service.py installpython service.py debug ,但是python service.py start没有响应。

Maybe it's caused by venv problem, because windows service start your service by exec PROJECT\\venv\\Lib\\site-packages\\win32\\pythonservice.exe . 也许它是由venv问题引起的,因为windows服务通过exec PROJECT\\venv\\Lib\\site-packages\\win32\\pythonservice.exe启动你的服务。

You can use powershell or cmd to test your service to find more error's details. 您可以使用powershellcmd来测试您的服务,以查找更多错误的详细信息。

PS C:\Users\oraant> E:

PS E:\> cd \Software\PythonService\venv\Lib\site-packages\win32

PS E:\Software\PythonService\venv\Lib\site-packages\win32> .\pythonservice.exe -debug ttttt
Debugging service ttttt - press Ctrl+C to stop.
Error 0xC0000004 - Python could not import the service's module

Traceback (most recent call last):
  File "E:\Software\PythonService\my_service.py", line 2, in <module>
    import win32serviceutil
ModuleNotFoundError: No module named 'win32serviceutil'

(null): (null)

If you get some error like me, then you can check my answer in another question, I fixed it and post my code here . 如果你得到像我这样的错误,那么你可以在另一个问题中检查我的答案,我修复它并在这里发布我的代码。

This doesn't answer the original question, but might help other people that want to automatically start a Python script at Windows startup: Have a look at the Windows Task Scheduler instead, it is way easier if you just want to start a script after boot without all the service functionality of a Windows Service.这不能回答最初的问题,但可能会帮助其他想要在 Windows 启动时自动启动 Python 脚本的人:改为查看Windows 任务计划程序,如果您只想在启动后启动脚本,这会更容易没有 Windows 服务的所有服务功能。

Create a new task, select "At startup" as trigger, "Start program" as action with "C:\Python39\python.exe" as the program (or wherever your python.exe is) and the full path to your script ("C:...\my_dir\xyz.py") as argument (you can use " if the path contains spaces). You can also select the path of your script (without the .py file, eg "C:...\my_dir") for "start in" if you use relative paths in your script, eg for logging.创建一个新任务,选择“启动时”作为触发器,选择“启动程序”作为操作,将“C:\Python39\python.exe”作为程序(或任何你的 python.exe 所在的位置)和脚本的完整路径( “C:...\my_dir\xyz.py”)作为参数(如果路径包含空格,您可以使用“)。您还可以选择脚本的路径(没有 .py 文件,例如“C:.. .\my_dir") 如果您在脚本中使用相对路径,例如用于记录,则表示“开始”。

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

  1. Follow up the PySvc.py跟进 PySvc.py

  2. changing the dll folder更改dll文件夹

I know this is old but I was stuck on this forever.我知道这很旧,但我一直坚持这一点。 For me, this specific problem was solved by copying this file - pywintypes36.dll对我来说,这个特定的问题是通过复制这个文件解决的 - pywintypes36.dll

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

To -> Python36\Lib\site-packages\win32到 -> 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
  1. changing the path to python folder by更改python文件夹的路径

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

  1. NET START PySvc
  2. NET STOP PySvc

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM