Python create windows service

teaser

Hi guys, today’s post is just for the ones of you that work with the “OS of the misoriented slashes”: Microsoft Windows. :)

Have you ever had the need of writing a Python script that could run in background as a Windows Service? In this post, you will learn how to do it in less than 10 minutes, no jokes.

I will skip all the introduction about Windows Services, how convenient they could be, how much could be appreciated the fact that they can be run in background even when the user is logged off etc… I mean, if you can code in Python and you use Windows I bet you already know what a Windows Service is, don’t you?

So, first of all, let’s start by installing the Python for Windows extensions:

c:test> pip install pywin32

Once you have done it, let’s write this base class, your Windows service will be a subclass of this base class.

'''
SMWinservice
by Davide Mastromatteo

Base class to create winservice in Python
-----------------------------------------

Instructions:

1. Just create a new class that inherits from this base class
2. Define into the new class the variables
   _svc_name_ = "nameOfWinservice"
   _svc_display_name_ = "name of the Winservice that will be displayed in scm"
   _svc_description_ = "description of the Winservice that will be displayed in scm"
3. Override the three main methods:
    def start(self) : if you need to do something at the service initialization.
                      A good idea is to put here the inizialization of the running condition
    def stop(self)  : if you need to do something just before the service is stopped.
                      A good idea is to put here the invalidation of the running condition
    def main(self)  : your actual run loop. Just create a loop based on your running condition
4. Define the entry point of your module calling the method "parse_command_line" of the new class
5. Enjoy
'''

import socket

import win32serviceutil

import servicemanager
import win32event
import win32service


class SMWinservice(win32serviceutil.ServiceFramework):
    '''Base class to create winservice in Python'''

    _svc_name_ = 'pythonService'
    _svc_display_name_ = 'Python Service'
    _svc_description_ = 'Python Service Description'

    @classmethod
    def parse_command_line(cls):
        '''
        ClassMethod to parse the command line
        '''
        win32serviceutil.HandleCommandLine(cls)

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

    def SvcStop(self):
        '''
        Called when the service is asked to stop
        '''
        self.stop()
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)

    def SvcDoRun(self):
        '''
        Called when the service is asked to start
        '''
        self.start()
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                              servicemanager.PYS_SERVICE_STARTED,
                              (self._svc_name_, ''))
        self.main()

    def start(self):
        '''
        Override to add logic before the start
        eg. running condition
        '''
        pass

    def stop(self):
        '''
        Override to add logic before the stop
        eg. invalidating running condition
        '''
        pass

    def main(self):
        '''
        Main class to be ovverridden to add logic
        '''
        pass

# entry point of the module: copy and paste into the new module
# ensuring you are calling the "parse_command_line" of the new created class
if __name__ == '__main__':
    SMWinservice.parse_command_line()

Let’s examine the class we have just introduced a little.

def SvcDoRun(self): it’s the method that will be called when the service is requested to start.

def SvcStop(self): it’s the method that will be called when the service is requested to stop.

def start(self): it’s a method that you will be asked to override if you need to do something when the service is starting (before started)

def stop(self): it’s the method that you will be asked to override if you need to do something when the service is stopping (before stopped)

def main(self): it’s the method that will contain the logic of your script, usually in a loop that keeps it alive until the service is stopped.

def parse_command_line(cls): it’s the method that handles the command line interface that you can use to install and update your windows service

Can you see how easy it is with pywin32 to interface with the system to create a Windows Service? The last mention is for the following variables:

svc_name = "PythonCornerExample"
svc_display_name = "Python Corner's Winservice Example"
svc_description = "That's a great winservice! :)"

These are just three variables that contain the name of the service, the “friendly name” that will be used by Windows to display the name on the mmc console and a short description of your service.

As always, enough talk, let’s code something useful!


Let’s pretend that we want to create a Winservice that, when started, creates a random file on our C: drive every 5 seconds.

What? Do you think it is stupid?
Well, install it on your boss PC, set the destination folder as its user’s desktop and you will change your mind. :)

However, how can you achieve this result? Super easy.

  • Subclass the SMWinservice class we have just met.
  • On the new class, override the three variables _svc_name_, _svc_display_name_ and _svc_description_.
  • Override the “start” method to set the running condition. Setting a boolean variable will be enough for that.
  • Override the “stop” method to invalidate the running condition when the service is requested to be stopped.
  • Override the “main” method to add the logic of creating a random file every 5 seconds
  • Add the call at the “parse_command_line” function to handle the command line interface.

The result should be something like this:

import time
import random
from pathlib import Path
from SMWinservice import SMWinservice

class PythonCornerExample(SMWinservice):
    _svc_name_ = "PythonCornerExample"
    _svc_display_name_ = "Python Corner's Winservice Example"
    _svc_description_ = "That's a great winservice! :)"

    def start(self):
        self.isrunning = True

    def stop(self):
        self.isrunning = False

    def main(self):
        i = 0
        while self.isrunning:
            random.seed()
            x = random.randint(1, 1000000)
            Path(f'c:{x}.txt').touch()
            time.sleep(5)

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

That’s it! Now it’s time to install our newly created winservice. Just open a command prompt, navigate to your script directory and install the service with the command:

C:test> python PythonCornerExample.py install
Installing service PythonCornerExample
Service installed

In the future, if you want to change the code of your service, just modify it and reinstall the service with

C:test> python PythonCornerExample.py update
Changing service configuration
Service updated

Now, open the “Services” msc snap in

locate your new PythonCornerExample winservice, and right click and choose properties. Here you can start your service and configure it at your will.

Now try to start your service and go to see your C: folder contents.

Can you see all these files being created to your C: folder? Yeah, that’s working!

But now it’s time to stop it!  :) You can do it from the previous windows or just by using the command line

C:test> net stop PythonCornerExample

Il servizio Python Corner's Winservice Example sta per essere arrestato.. 
Servizio Python Corner's Winservice Example arrestato.

If something goes wrong…

There are a couple of known problems that can happen writing Windows Services in Python. If you have successfully installed the service but starting it you get an error, follow this iter to troubleshoot your service:

  • Check if Python is in your PATH variable. It MUST be there. To check this, just open a command prompt and try starting the python interpreter by typing “python”. If it starts, you are ok.
  • Be sure to have the file C:\Program Files\Python36\Lib\site-packages\win32\pywintypes36.dll (please note that “36” is the version of your Python installation). If you don’t have this file, take it from C:\Program Files\Python36\Lib\site-packages\pywin32_system32\pywintypes36.dll and copy it into C:\Program Files\Python36\Lib\site-packages\win32

If you still have problems, try executing your Python script in debug mode. To try this with our previous example, open a terminal, navigate to the directory where the script resides and type

c:test> python PythonCornerExample.py debug

Ok, that’s all for today, this was just a brief introduction to developing Windows Services with Python. Try it by yourself and … happy coding!

D.

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

  1. Import the BaseService class:

    from windowsservice import BaseService
    
  2. Create a new subclass that inherits from the BaseService class.

  3. 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."
    
  4. 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 after start. 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
    
  5. 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.

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

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.

Пройдите тест, узнайте какой профессии подходите

Работать самостоятельно и не зависеть от других

Работать в команде и рассчитывать на помощь коллег

Организовывать и контролировать процесс работы

Быстрый ответ

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

Для установки и запуска службы воспользуйтесь следующими командами:

Данный метод позволит вам работать со скриптом Python как с службой Windows.

Кинга Идем в IT: пошаговый план для смены профессии

Расширение функционала вашей службы

Использование 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.

Бесперебойные обновления и поддержка служб

Обеспечивайте возможности «горячего» обновления и «мягкого» завершения работы службы для сокращения времени простоя. Нужно продумать надежный механизм обновлений, который позволит внедрять новые версии службы без ручного вмешательства.

Полезные материалы

  1. NSSM – the Non-Sucking Service Manager — инструмент для простого управления Python-службами.
  2. Как запустить скрипт Python как службу в Windows на Stack Overflow — обсуждения и решения от сообщества разработчиков.
  3. Полный пример службы от Microsoft — официальные рекомендации по созданию служб Windows.
  4. Руководство по PyInstaller — создание автономных исполняемых файлов из Python-скриптов.
  5. Планировщик задач для разработчиков от Microsoft — использование Планировщика задач Windows для автоматизации Python-скриптов.
  6. Supervisor: Система контроля процессов — мониторинг и контроль над продолжительными службами на Python.

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Как установить whatsapp на компьютер windows 10
  • Как получить права администратора в windows server 2008
  • Skachat opera dlya windows 7
  • Windows map disk to folder
  • Windows 10 pro compact lite флибустьер