Пройдите тест, узнайте какой профессии подходите
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы
Быстрый ответ
Для того чтобы Python-скрипт мог работать в роли службы Windows, рекомендуется использовать библиотеку pywin32
. Создайте класс службы, который будет унаследован от win32serviceutil.ServiceFramework
, а затем переопределите методы SvcDoRun
и SvcStop
для управления запуском и остановкой службы. Установка и управление службой происходит через командную строку. Пример кода представлен ниже:
Для установки и запуска службы воспользуйтесь следующими командами:
Данный метод позволит вам работать со скриптом Python как с службой Windows.

Расширение функционала вашей службы
Использование NSSM для упрощённого управления службами
NSSM (Non-Sucking Service Manager) предлагает комфортный графический интерфейс для управления службами, облегчая настройку использования скриптов и переменных среды.
Регистрация вашей службы с помощью sc.exe
Можно зарегистрировать ваш скрипт как службу, используя команду sc.exe
:
Применение Python и Django для управления службами
Python и Django предоставляют возможность создавать веб-интерфейсы для управления службами, улучшая удобство и гибкость их использования.
Использование опыта работы с демонами в Linux
Если у вас имеется опыт работы с демонами в Linux, то вы усвоите создание служб для Windows быстрее, несмотря на специфику реализации в этих системах.
Визуализация
Представьте себе Python-скрипт, превращающийся в службу Windows, как робот (🤖), которы работает в автономном режиме:
Изначально Python-скрипт — это просто набор команд (📜), но после аккуратной настройки (🔧👨🔧), он превращается в полноценно функционирующую службу (🤖).
Решение возникающих проблем
Журналирование работы службы и обработка ошибок
Servicemanager
поможет вести журнал работы службы и адекватно обрабатывать возникающие ошибки, давая полное представление о функционировании службы и возможных проблемах.
Управление переменными окружения
Использование динамической конфигурации на основе переменных окружения позволит службе адаптироваться к различным условиям работы: отладка, предпродакшн, продакшн.
Управление зависимостями
Работа службы может требовать конкретные версии библиотек или внешние модули Python. Для этого используются виртуальные среды и PyInstaller, который создаёт исполняемые файлы со всеми необходимыми зависимостями.
Продвинутое управление службой
Масштабирование службы
Для организации управления нагрузкой на службы можно применить виртуальное окружение, что упростит изоляцию и решение потенциальных проблем, связанных с масштабированием.
Тонкая настройка службы через реестр
Можно детально настроить поведение службы на уровне реестра Windows по адресу HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
.
Бесперебойные обновления и поддержка служб
Обеспечивайте возможности «горячего» обновления и «мягкого» завершения работы службы для сокращения времени простоя. Нужно продумать надежный механизм обновлений, который позволит внедрять новые версии службы без ручного вмешательства.
Полезные материалы
- NSSM – the Non-Sucking Service Manager — инструмент для простого управления Python-службами.
- Как запустить скрипт Python как службу в Windows на Stack Overflow — обсуждения и решения от сообщества разработчиков.
- Полный пример службы от Microsoft — официальные рекомендации по созданию служб Windows.
- Руководство по PyInstaller — создание автономных исполняемых файлов из Python-скриптов.
- Планировщик задач для разработчиков от Microsoft — использование Планировщика задач Windows для автоматизации Python-скриптов.
- Supervisor: Система контроля процессов — мониторинг и контроль над продолжительными службами на Python.
Манипуляция службами в Windows c помощью Питона
Start / Stop windows services with Python
При работе в Windows приходится использовать некоторые службы, которые не нужны постоянно. Особенно, когда эта машина локальная, не серверная.
Например, sphinxsearh, MySQL, PostgreSQL, очереди сообщений и проч. нужны либо в момент работы над проектом, их использующим, либо при изучении их разработчиком.
Бывают и другие примеры не нужных постоянно служб.
Обычно пользователи Виндоус могут остановить службу через Панель Управления (Администрирование — Службы) или, кто попродвинутей, через командную строку. Оба этих способа подразумевают трату времени или на поиск службы в списке, или на узнавание имени службы.
Хорошим способом будет запуск/останов/рестарт требуемых служб из Питон.
Для этого нам понадобится пакет pywin32 и простой скрипт на питоне.
Вначале автор нашел нечто готовое для задачи в PyPi репозитории. Возможно, вам подойдет:
Отсюда скачивайте последнюю версию pywin32 (там она вполне по x64 есть, не смущайтесь названием) под требуемую версию Питона, а отсюда ( ну или с Гитхаба) устанавливаем PyWinServiceManager.
Для проверки работоспособности можно сваять простенький скрипт:
from pywinservicemanager.WindowsServiceConfigurationManager import ServiceExists serviceName = 'TestService' serviceExists = ServiceExists(serviceName) print serviceExists
Сходу у меня он дал ошибку, решение которой описано тут:
http://stackoverflow.com/questions/22490233/win32com-import-error-python-3-4
(нужно выполнить из Питона скрипт [PythonDir]\Scripts\pywin32_postinstall.py)
После исправления ошибки все запускается, однако, похоже, PyWinServiceManager писан для второго Питона. Или не для второго, но ошибок многовато и править их лень. Хотя сама библиотека задумана с размахом и может подойти для задач типа «запуск Python скрипта как windows сервис».
Мы же поищем что-то попроще.
Что-то попроще описано по ссылкам:
http://code.activestate.com/recipes/59872-manipulating-windows-services/
https://www.safaribooksonline.com/library/view/python-cookbook/0596001673/ch07s13.html
Хинт: поскольку имеем дело с локальной машиной, в выозвах достаточно указать
machine = None или вообще переписать без использования этого параметра (по сети не тестировал, но идея выглядит интересной)
т.е. указываем
machine = None service = 'postgresql-x64-9.5' action = 'restart'
и проверяем работоспособность.
Тут все хорошо, кроме того, что служба стартует не мгновенно, точнее, не так быстро, как ее статус опрашивается. Как следствие — при запуске и перезапуске (start / restart) вы будете получать сообщение, что запуск невозможен.
Исправим это простой функции проверки статуса.
Итоговый код:
import win32serviceutil def service_running(service, machine): status = (win32serviceutil.QueryServiceStatus(service)[1] == 4) if not status: import time time.sleep(3) status = (win32serviceutil.QueryServiceStatus(service)[1] == 4) return status def service_info(action, machine, service): running = service_running(service, machine) servnam = 'service (%s) on machine(%s)'%(service, machine) action = action.lower( ) if action == 'stop': if not running: print ("Can't stop, %s not running"%servnam) return 0 win32serviceutil.StopService(service, machine) running = service_running(service, machine) if running: print ("Can't stop %s (???)"%servnam) return 0 print ('%s stopped successfully' % servnam) elif action == 'start': if running: print ("Can't start, %s already running"%servnam) return 0 win32serviceutil.StartService(service, machine) running = service_running(service, machine) if not running: print ("Can't start %s (???)"%servnam) return 0 print ('%s started successfully' % servnam) elif action == 'restart': if not running: print ("Can't restart, %s not running"%servnam) return 0 win32serviceutil.RestartService(service, machine) running = service_running(service, machine) if not running: print ("Can't restart %s (???)"%servnam) return 0 print ('%s restarted successfully' % servnam) elif action == 'status': if running: print ("%s is running" % servnam) else: print ("%s is not running" % servnam) else: print ("Unknown action (%s) requested on %s"%(action, servnam)) if __name__ == '__main__': machine = None service = 'postgresql-x64-9.5' action = 'restart' service_info(action, machine, service)
теперь, зная список служб, можно держать их в отдельном файле и стартовать/останавливать группой применительно к конкретной задаче.
P.S. конечно же, права на запуск должны быть Администратора. Т.е. или запускайте PyCharm c админ правами или классическое CMD c Run As Administrator опцией.
еще сссылки по теме (не проверялись):
http://code.activestate.com/recipes/135700-win32-service-administration/
https://github.com/SublimeText/Pywin32/blob/master/lib/x32/win32/lib/win32serviceutil.py
windowsservice
is a Python package for building Windows services.
The key features are:
- Easy to use
- Support for PyInstaller
- Support for
multiprocessing
Getting ready
Create and activate a virtual environment:
python -m venv venv
.\venv\Scripts\activate
Installation
Install the windowsservice package from PyPI:
pip install windowsservice
The windowsservice package depends on pywin32 created by Mark Hammond. Installing the windowsservice package also installs the pywin32 dependency.
Coding
-
Import the
BaseService
class:from windowsservice import BaseService
-
Create a new subclass that inherits from the
BaseService
class. -
Define the following three class variables in the subclass:
_svc_name_
: A unique identifier for your service._svc_display_name_
: The name shown in the service control manager._svc_description_
: The description shown in the service control manager.
For example:
_svc_name_ = "MyWindowsService" _svc_display_name_ = "My Windows Service" _svc_description_ = "This is my custom Windows service."
-
Override the following methods in the subclass:
-
start(self)
: This method is invoked when the service starts. Override
this to add setup code, such as initializing a running condition. -
main(self)
: This method is invoked afterstart
. Override this to
create a run loop, typically based on a running condition. -
stop(self)
: This method is invoked when the service stops. Override
this to add cleanup code, such as releasing resources or to invalidate a
running condition.
For example:
def start(self): self.is_running = True def main(self): while self.is_running: time.sleep(5) def stop(self): self.is_running = False
-
-
Call the
parse_command_line
class method from the module’s entry point. This handles command-line arguments for installing, starting, stopping,
and debugging the service.For example:
if __name__ == "__main__": ExampleService.parse_command_line()
Examples
Basic example (example_service.py):
import time from windowsservice import BaseService, utils class ExampleService(BaseService): """Example Windows service in Python.""" _svc_name_ = "PythonExampleWindowsService" _svc_display_name_ = "Python Example Windows Service" _svc_description_ = "Example Windows service in Python" def start(self): self.is_running = True def main(self): while self.is_running: utils.log(f"{self._svc_display_name_} is running...") time.sleep(5) def stop(self): self.is_running = False if __name__ == "__main__": ExampleService.parse_command_line()
Example that demonstrates support for multiprocessing
(example_service_multiprocessing.py):
import time from multiprocessing import Process, freeze_support from windowsservice import BaseService, utils def stub_server(): """ A function that simulates a server process hosted by the Windows service. This function logs a message every 5 seconds for a total of 100 times. """ for _ in range(100): utils.log("Hello from a process hosted by a Windows service...") time.sleep(5) class ExampleService(BaseService): """Example Windows service in Python.""" _svc_name_ = "PythonExampleWindowsService" _svc_display_name_ = "Python Example Windows Service" _svc_description_ = "Example Windows service in Python" def start(self): self.server_process = Process(target=stub_server) def main(self): self.server_process.start() self.server_process.join() def stop(self): if self.server_process: self.server_process.terminate() if __name__ == "__main__": freeze_support() ExampleService.parse_command_line()
Usage
Some interactions with a Windows service may require administrative privileges, so you must use an elevated command line interface.
All arguments and options can be listed by invoking the module:
python .\examples\example_service.py
Usage: 'example_service.py [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.
Install the service
If you want to install the service from the example_service.py
example, run:
python .\examples\example_service.py install
Installing service PythonExampleWindowsService Service installed
You can also choose to install the service so that it will start automatically when Windows starts:
python .\examples\example_service.py --startup=auto install
Start the service
To start/stop the service you can now use the service control manager.
Or, from the command line interface, you can run:
python .\examples\example_service.py start
To start the service in debug mode, run:
python .\examples\example_service.py debug
To verify that the service is working, make sure it is running, and then look into the Windows Event Viewer or, if it is running in debug mode, look at the standard output stream. You should see a message every 5 seconds.
Remove the service
To remove the service, run:
python .\examples\example_service.py remove
PyInstaller
To bundle the service into a convenient standalone executable, you can use PyInstaller.
Install PyInstaller
Install PyInstaller inside your activated virtual environment:
pip install pyinstaller
Create executable
To create a standalone (one-file) executable, use the pyinstaller
command:
pyinstaller .\examples\example_service.py --clean --noupx --onefile --noconfirm --hidden-import=win32timezone
The resulting example_service.exe
executable can be found in the .\dist
directory.
You can use this executable with the same arguments and options as discussed above. For example, to install the service, run:
.\dist\example_service.exe install
You can also create a one-folder bundle. Because the executable does not have to be unpacked first in a temporary location, this has the advantage of making the service start faster.
pyinstaller .\examples\example_service.py --clean --noupx --onedir --noconfirm --hidden-import=win32timezone
In this case, the resulting executable can be found in the dist\example_service
directory.
Acknowledgement
This package utilizes the pywin32 library, a collection of Python extensions for Windows. The maintenance and development of this library is credited to Mark Hammond and others in the Python community. Their contributions enable the development of Windows services in Python. Please note that the windowsservice
package does not aim to replace pywin32
or win32serviceutil.ServiceFramework
, it’s just here to make it a little bit easier to use.
Creating a Windows executable using PyInstaller is reasonably straightforward:
pyinstaller.exe myservice.py
however, this will A) create a folder with the executable and supporting files, and B) not actually work!
The reason it won’t work is that PyInstaller, while very clever, can’t find all imports to include, and in this case will miss including win32timezone
in the package. To fix this issue we can tell PyInstaller to include it:
pyinstaller.exe --hidden-import win32timezone myservice.py
Testing is important here – the build will succeed and then the program will fail in run time, so for complex application it becomes a case of trial and error – building, running to find any missing imports, then re-building after adding new --hidden-imports
.
With this, the service works and we could install it with myservice.exe install
, but I find I’d rather have a single file, which can be accomplished using the --onefile
flag:
pyinstaller.exe --onefile --hidden-import win32timezone myservice.py
This creates a single executable, easy to distribute, and it will look like it works. However, it will unpack files in the Windows temp folder, which normally gets periodically wiped – this is likely to break any non-trivial service running on Windows for a long time. The fix for this issue is to unpack it somewhere where Windows will leave it alone, for example the current folder whatever that may be:
pyinstaller.exe --runtime-tmpdir=. --onefile --hidden-import win32timezone myservice.py
This should work – but due to issue 4579 on PyInstaller (fix incoming here), can break unexpectedly depending on what you set the runtime-tmpdir
to – my solution has been to install PyInstaller from the PR until it is merged and released.
Problem Statement
Creating a Windows service in Python is a common task, often facilitated by numerous code examples available online and tools like Chart GPT. However, a significant challenge arises when attempting to access or start Windows processes within the context of a Windows service. The primary complication stems from the fact that services run under the Local System authority, introducing permission constraints that can hinder the seamless execution of certain tasks.
Introduction
Windows services provide a powerful mechanism for running background tasks that do not require user interaction. In Python, the win32serviceutil
module, part of the pywin32
library, allows developers to create and manage Windows services seamlessly. In this article, we’ll explore a Python script that utilizes win32serviceutil
to create a simple Windows service.
import win32serviceutil
import win32service
import win32event
import servicemanager
import socket
import os
import time
class MyService(win32serviceutil.ServiceFramework):
_svc_name_ = 'MyService'
_svc_display_name_ = 'My Service'
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
socket.setdefaulttimeout(120)
self.is_alive = True
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
self.is_alive = False
def SvcDoRun(self):
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED,
(self._svc_name_, ''))
self.main()
def main(self):
# Main service logic goes here
while self.is_alive:
# Perform your service tasks here
time.sleep(5) # Example: Sleep for 5 seconds
if __name__ == '__main__':
if len(os.sys.argv) == 1:
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(MyService)
servicemanager.StartServiceCtrlDispatcher()
else:
win32serviceutil.HandleCommandLine(MyService)
Enter fullscreen mode
Exit fullscreen mode
Understanding the Python Script:
Let’s break down the provided Python script, which serves as the foundation for our Windows service.
import win32serviceutil
import win32service
import win32event
import servicemanager
import socket
import os
import time
Enter fullscreen mode
Exit fullscreen mode
These import statements include essential modules such as win32serviceutil
, win32service
, win32event
, servicemanager
, socket
, os
, and time
. These modules collectively provide the necessary tools for creating and managing Windows services.
class MyService(win32serviceutil.ServiceFramework):
_svc_name_ = 'MyService'
_svc_display_name_ = 'My Service'
Enter fullscreen mode
Exit fullscreen mode
Here, a class named MyService
is defined, inheriting from win32serviceutil.ServiceFramework
. This class represents our custom Windows service. The _svc_name_
attribute defines the service name, and _svc_display_name_
sets the display name visible in the Windows Services manager.
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
socket.setdefaulttimeout(120)
self.is_alive = True
Enter fullscreen mode
Exit fullscreen mode
The __init__
method initializes the service. It creates an event (hWaitStop
) that signals the service to stop when set. The socket.setdefaulttimeout(120)
line sets the default timeout for socket operations, and self.is_alive
tracks whether the service is running.
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
self.is_alive = False
Enter fullscreen mode
Exit fullscreen mode
The SvcStop
method is called when the service is stopped. It reports the stop status and signals the hWaitStop
event.
def SvcDoRun(self):
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED,
(self._svc_name_, ''))
self.main()
Enter fullscreen mode
Exit fullscreen mode
The SvcDoRun
method is the main entry point for the service. It logs a service start message and calls the main
method.
def main(self):
# Main service logic goes here
while self.is_alive:
# Perform your service tasks here
time.sleep(5) # Example: Sleep for 5 seconds
Enter fullscreen mode
Exit fullscreen mode
The main
method contains the core logic of your service. In this example, it includes a loop simulating a service that performs tasks every 5 seconds.
if __name__ == '__main__':
if len(os.sys.argv) == 1:
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(MyService)
servicemanager.StartServiceCtrlDispatcher()
else:
win32serviceutil.HandleCommandLine(MyService)
Enter fullscreen mode
Exit fullscreen mode
Finally, the script checks whether it’s being run as a standalone application or imported as a module. If run standalone, it initializes and starts the service using servicemanager
.
Conclusion:
This script provides a template for building Windows services in Python using win32serviceutil. Customize the main method to implement your service’s specific functionality. In subsequent articles, we’ll explore service installation, execution, and management.
Stay tuned for more on mastering Windows services with Python Part II