Running a Python Application as a Windows Service
By: Nai Biao Zhou | Updated: 2022-07-11 | Comments (6) | Related: More > Python
We may need to run a long-running application without human intervention. For example, we may want to monitor performance data on a schedule or react to specific events. The application should run in its session and not interfere with other applications. In this case, we can develop an application that runs as a Window Service (Altvater, 2017). Python applications have been proliferating for performing routine tasks in recent years. Therefore, IT professionals might want to run a Python application as a Windows service.
We can use the "Python for Windows Extensions" package (known as pywin32) to run a Python application as a Windows service. The pywin32 package allows us to use Win32 application programming interface (API) features on Python (PYPI, 2022). We often use the win32serviceutil module in this package to utilize APIs. For example, the module contains a base class "win32serviceutil.ServiceFramework" that helps create Windows services in Python (Robinson & Hammond, 2000). When creating a service in Python, we usually derive service classes from the base class. The service classes inherit methods and properties from the class. This approach simplifies working with Windows services.
We design a fictitious project to exhibit the process of creating a windows service in Python. MSSQLTips.com, owned by Edgewood Solutions, has served the SQL Server community since 2006. The website's landing page presents titles and short summaries of the most recent tips. In addition, every article comes with a featured image. Images have many purposes when used on web pages, besides helping the page look great (Simolo, 2016). However, for some reason, occasionally, some images may not display. Therefore, the CTO at Edgewood Solutions wants to monitor all featured images on the landing page to make sure all images display correctly.
We may have various Python applications on the same machine that depend on different versions of the same packages. In this case, each application should run in an isolated environment. Therefore, this project needs to run in a Python virtual environment.
We also need to use the "win32serviceutil" module to interact with Windows services. After briefly covering these prerequisites, we write an abstract class derived from the "win32serviceutil.ServiceFramework" class. We then create a service class that implements the abstract methods in order to perform the service functionality. Finally, the article explores a step-by-step process of creating a Windows service.
The author tested the project on Windows 10 Pro 10.0 <X64> and ran the Python scripts with Python 3.9.7 (64-bit). The IDE is Microsoft Visual Studio Community 2022 (64-bit).
1 – Prerequisite
There is a growing collection of several thousand third-party packages maintained by the Python community worldwide. Each package may have several versions. To avoid the version conflict, it is a good idea to run a Python application on a Python virtual environment. In addition, Python has complete support for Windows services, which are long-running applications in a Windows system.
1.1 Running a Python Script in a Virtual Environment
The tip "Creating a SQL Server Agent Job to Run Python Scripts in a Virtual Environment" demonstrates the steps to install Python on Windows. Without loss of generality, we can follow the instructions in the tip to create a virtual environment in the "C:\pyvirtuals\monitor_landing_page" folder. Click here to download the complete source code that contains the configuration file, "requirements.txt," for installing the virtual environment. We run the following commands to create the Python virtual environment for this project and install all necessary packages from the "requirements.txt" file:
REM 1, Create a Virtual Environment python -m venv C:\pyvirtuals\monitor_landing_page REM 2, Activate the Virtual Environment C:\pyvirtuals\monitor_landing_page\Scripts\activate.bat REM 3, Install the Necessary Packages pip install -r C:\pyapps\monitor_landing_page\requirements.txt
We copy all the Python scripts to the "C:\pyapps\monitor_landing_page" folder. The Python script "app.py" implements our business requirements. Before running the script as a Windows service, we manually run the script. We use the following command to run the Python script. Figure 1 illustrates the output of the program execution. We can also find the "log_file.txt" file in the "C:\pyapps\monitor_landing_page" folder. These outputs indicate that we run the Python script successfully.
REM Run the Python script in the virtual environment. python C:\pyapps\monitor_landing_page\app.py
Figure 1 Running the Python Script in the Virtual Environment
1.2 Using the win32serviceutil Module to Connect to a Windows Service
The service control manager (SCM), started at system boot, maintains a database of installed and driver services. The SCM can control services by using control requests. For example, when a service needs to start, the SCM issues a control request to the service. The service acts on the request and reports its status to the SCM. In addition, the SCM provides a user interface to allow the user to start and stop services manually. The SCM also provides an API to enable programmatic control of services. This feature allows us to use a program to control services (Robinson & Hammond, 2000).
The Python win32serviceutil module provides some handy utilities to control existing Windows services. For example, we can connect a Windows service with some Python functions. We can conduct some tests to gain hands-on experience using this module. We start a Command Prompt as an administrator so that these Python functions have permission to access the services. The author's machine name is "myPy," and we test these functions against the "SQL Server VSS Writer" service. Here are the function syntaxes and Python statements used in this test:
REM the function signature StartService( serviceName , args=None , machine=None ) StopService( serviceName , machine=None ) QueryServiceStatus( serviceName , machine=None ) import win32serviceutil win32serviceutil.StopService("SQL Server VSS Writer", "myPy") win32serviceutil.StartService("SQL Server VSS Writer", None, "myPy") win32serviceutil.QueryServiceStatus("SQL Server VSS Writer", "myPy")
Figure 2 demonstrates the steps to stop a Windows service. We use the "win32serviceutil.StopService" function to stop the service and the "win32serviceutil.QueryServiceStatus" function to check the service status. Both the functions return a SERVICE_STATUS structure. According to Microsoft Docs (MS Docs, 2021), the dwCurrentState "3" means that the service is stopping, and the dwCurrentState "1" suggests that the service is not running. The output indicates that we successfully stop the Windows service. Meanwhile, we can check the service status through the SCM interface. Figure 3 shows that the service stopped.
Figure 2 Stopping the SQL Server VSS Writer Service on the Computer Named myPy
Figure 3 Check the Service Status through the SCM Interface
2 – Creating Subclasses of the win32serviceutil.ServiceFramework Class
Because of some special requirements for Windows service, we use the executable, PythonService.exe, to host Python services. When a Python service starts, the executable creates an instance of the Python service class and delegates all service functionality to this instance (Robinson & Hammond, 2000). The Python service class should provide methods to interact with the SCM. The module win32serviceutil offers a base class, "win32serviceutil.ServiceFramework," which includes some predefined methods, for example, ServiceCtrlHandler() and SvcRun(). Therefore, when writing a Python service, we often create a class inherited from the base class.
In practice, we often write an abstract service class that provides a blueprint for other classes. The abstract class provides a standard interface for different implementations of a component. This project creates an abstract class "PythonService" that inherits from the "win32serviceutil.ServiceFramework" class. This class is responsible for installing, debugging, starting, and stopping the service. We may share this class on different projects. Next, we write another class, "MointorImageService," that inherits from the "PythonService" class. We concentrate on the service functionality in this class.
2.1 Creating the Abstract Class to Interact with the SCM
The "win32serviceutil.ServiceFramework" class has already defined some methods. Most Python services are a subclass of this base class. Inspired by the smallest possible Python service written in Robinson & Hammond’s book, we create an abstract class to interact with the SCM. The abstract class declares the three abstract methods, i.e., start(), stop(), and main(), in which the derived class should have implementations. The following Python code exhibits the class, and the comments help explain the code.
# Mastromatteo, D. (2018): https://thepythoncorner.com/posts/2018-08-01-how-to-create-a-windows-service-in-python/
# Robinson, S. & Hammond, M. (2000). Python Programming on Win32. Sebastopol, CA: O'Reilly Media.
from abcimport ABC,abstractmethod
class PythonService(win32serviceutil.ServiceFramework, ABC):
''' Parse the command line '''
# Override the method in the subclass to do something just before the service is stopped.
# Override the method in the subclass to do something at the service initialization.
# Override the method in the subclass to perform actual service task.
def __init__(self, args):
''' Class constructor'''
# Create an event which we will use to wait on.
# The "service stop" request will set this event.
self.hWaitStop = win32event.CreateEvent(None, 0, 0,None)
'''Called when the service is asked to stop'''
# We may need to do something just before the service is stopped.
# Before we do anything, tell the SCM we are starting the stop process.
# And set my event.
'''Called when the service is asked to start. The method handles the service functionality.'''
# We may do something at the service initialization.
# Starts a worker loop waiting either for work to do or a notification to stop, pause, etc.
2.2 Writing the Subclass to Perform Service Functionality
We create another new class that inherits methods and properties from the abstract class. The new class concentrates on the service functionality and some service configurations. Therefore, the new class should implement the three abstract methods declared in the abstract class. First, we set the running condition in the start() method. We then negate the running condition in the stop() method. Finally, the main() method executes a loop when the running condition is valid. The class also defines the service name and sets the location of the PythonService.exe. The following block demonstrates the code. It is worth noting that the code contains startup code that handles the command line when run as a script. We can install, debug, and start the service using the startup code.
# Mastromatteo, D. (2018): https://thepythoncorner.com/posts/2018-08-01-how-to-create-a-windows-service-in-python/
from PythonService import PythonService
# Define the class variables
_svc_name_ = "MointorImageOnLandingPageService"
_svc_display_name_ = "MSSQLTips Mointor Images Service"
_svc_description_ = "Mointor images on the landing page of the MSSQLTips.com."
_exe_name_ = "C:\pyvirtuals\monitor_landing_page\Scripts\pythonservice.exe"
# Override the method to set the running condition
# Override the method to invalidate the running condition
# When the service is requested to be stopped.
# Override the method to perform the service function
# Use this condition to determine the execution context.
if __name__ == '__main__':
# Handle the command line when run as a script
3 –Creating a Windows Service Using Python
We have already tested the app.py script in the virtual environment. We also created the abstract class "PythonService" and its subclass "MointorImageService." The "C:\pyapps\monitor_landing_page" folder should have these three files. All files from the virtual environment creation are in the "C:\pyvirtuals\monitor_landing_page" folder. We use this folder structure for demonstration purposes. In practice, we can use a different folder structure. The service account should have permission to write text into the log file. We then walk through a step-by-step process to run the Python scripts as a Windows service.
Step 1: Copy the executable, pythonservice.exe, to the virtual environment scripts folder.
Copy pythonservice.exe from the "C:\pyvirtuals\monitor_landing_page\Lib\site-packages\win32" folder to the "C:\pyvirtuals\monitor_landing_page\Scripts folder" (Lindsay, 2016). The service class variable "_exe_name" should contain the fully qualified path of the executable. The "MointorImageService" class definition demonstrates the class variable initialization. In practice, we often put the path into a configuration file.
Step 2: Open the command prompt as administrator.
Enter "command" in the Windows 10 search box. The "Command Prompt" appears in the pop-up list. Right-click on the "Command Prompt" item and select "Run as administrator" in the context menu (Glenn, 2021). We then change the directory to the "C:\pyapps\monitor_landing_page" folder.
Step 3: Run the pywin32_postinstall.py to register two dynamic link libraries.
The Python script pywin32_postinstall.py is in the "C:\pyvirtuals\monitor_landing_page\Scripts" folder. We first run the "C:\pyvirtuals\monitor_landing_page\Scripts\activate.bat" to activate the virtual environment. Then, we execute the Python script "C:\pyvirtuals\monitor_landing_page\Scripts\pywin32_postinstall.py" in the command window. Figure 4 presents the output of the two commands. Here are the two commands:
C:\pyvirtuals\monitor_landing_page\Scripts\activate.bat C:\pyvirtuals\monitor_landing_page\Scripts\pywin32_postinstall.py -install
Figure 4 Execute the pywin32_postinstall.py in the command window
If we want to unregister these two dynamic link libraries, we can execute the following command to roll back the change:
Step 4: Install the service
Execute the following command to install the service. Figure 5 demonstrates this step and presents the output. We can ignore the warning message because we have already run the pywin32_postinstall.py script. The "pythoncom39.dll" and "pywintypes39.dll" are in the "c:\Windows\system32" folder. The installation command also generates a Windows service log (Event ID 7045), as shown in Figure 6. The log briefly describes the newly installed service.
python MointorImageService.py install
Figure 5 Use the command prompt to install the service
Figure 6 The Window service log indicates the new installed service
Step 5: Start the service
There are several ways to start a service. A started service should report its status to the SCM and listen for control requests from the SCM. We can use the following command to start the service:
python MointorImageService.py start
To ensure that the service starts correctly, we can check the log file, which should look like the following block.
***Start***: 20/06/2022 22:48:42 ****End****: 20/06/2022 22:48:49 The execution time: 06.734439 seconds ***Start***: 20/06/2022 22:48:54 ****End****: 20/06/2022 22:48:59 The execution time: 05.593806 seconds
The SCM provides a user interface to control services, as shown in Figure 7. We can start the service process through the interface. By default, the service startup type is "manually," meaning a user should start the service manually. However, we can change the type to "automatically" so that the service automatically starts when the system boots. In addition, the service account is LocalSystem, a predefined local account used by the service control manager. The SCM allows us to change the service account.
Figure 7 Use the Service Control Manager to manually services
Step 6 (optional): Stop the service
The SCM provides a user interface to allow us to stop Windows services manually. When we stop a service, the SCM issues a control request to the service. The service then reacts to this request and reports its status to the SCM before it terminates. We also can use the following command to stop the service.
python MointorImageService.py stop
Step 7 (optional): Debug the service
If the service does not work properly, we use the following command to execute the service in debug mode. We often use this command to test the service after installation. The logging module helps print the debug message to the console. Figure 8 shows the output of this command. We should turn off the console output when running the service on the production server.
python MointorImageService.py debug
Figure 8 Debug the Windows service
Step 8 (optional): Update the service
When changing the Python script, we can use the following command to update the service.
python MointorImageService.py update
Step 9 (optional): Uninstall the service
We can also remove the service using a command line, as shown below:
python MointorImageService.py remove
Running a Python application as a Windows service enables us to execute long-running Python applications that utilize Windows features. We can use the Service Control Manager (SCM) to control these services. The article created a fictitious project that extracts image information from the MSSQLTips' landing page. We wrote Python scripts to scrape the web page. We then ran the Python application as a Windows service.
We briefly covered creating a Python virtual environment and installing third-party packages. We then explored the "win32serviceutil" module and used functions to control Windows services with Python. Next, the article introduced the "win32serviceutil.ServiceFramework" base class. After creating the abstract class derived from the base class, we wrote the service class to implement the abstract methods. Finally, we walked through the step-by-step process of creating a Windows service.
Altvater, A. (2017). What are Windows Services? How Windows Services Work, Examples, Tutorials and More. https://stackify.com/what-are-windows-services/.
Glenn, W. (2021). How to Open the Command Prompt as Administrator in Windows 8 or 10. https://www.howtogeek.com/194041/how-to-open-the-command-prompt-as-administrator-in-windows-8.1/.
Lee, X. (2005). Python: List Modules, Search Path, Loaded Modules. http://xahlee.info/python/standard_modules.html.
Lindsay.stevens.au. (2016). Using PythonService.exe to host python service while using virtualenv. https://stackoverflow.com/questions/34696815/using-pythonservice-exe-to-host-python-service-while-using-virtualenv.
MS Docs. (2021). SERVICE_STATUS structure. https://docs.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_status.
PYPI. (2022). pywin32 304 - Python for Window Extensions. https://pypi.org/project/pywin32/.
Robinson, S. & Hammond, M. (2000). Python Programming On Win32. Sebastopol, CA: O'Reilly Media.
Simolo, G. (2016). When Words Meet Pictures: How to Use Text and Images to Create Striking Articles for Readers. https://www.freelancewriting.com/freelancing/when-words-meet-pictures/.
- This tip uses the Python for Win32 (pywin32) extensions to create a Windows service that can run Python scripts. The ability to customize the service class allows us to implement various business requirements. However, the code provided in this article is for demonstration purposes. There is much room to improve the code. For example, we should put the executable location in a configuration file. Besides the pywin32 package, we may also use the Non-sucking Service Manager to run a Python application as a Windows service.
- Check out these related tips:
- Learning Python in Visual Studio 2019
- Python Programming Tutorial with Top-Down Approach
- Using Python to Download Data from an HTML Table to an SQL Server Database
- Creating a SQL Server Agent Job to Run Python Scripts in a Virtual Environment
- Using Windows Task Scheduler to Run a Python Script at Prescribed Times
- Using Advanced Python Web Scraping to Conduct a Web Content Audit
- Using Web Crawling in Python to Conduct a Website Content Audit
About the author
View all my tips
Article Last Updated: 2022-07-11