I am trying to run a python program, written using Anaconda, as a Windows Service. The complexity is that I'd like to run the Windows Service from a specific conda virtual environment. The idea being that in the future, we may develop more python based windows services that may have different module dependencies, thus keeping each within its own virtual environment would be ideal.
I've found several excellent articles about how to write a python program as a windows service and they work fine. I created a very simple test program that simply writes some messages to a text file after the service starts. I can successfully install this test python program as a windows service and I see the various text messages in my file. However, when I try to import modules like Numpy or TensorFlow into my simple test python program, the service won't start and I get failure messages that their respective DLL's can't be found.
I am sure the problem is because the required conda virtual environment hasn't been activated. At the same time, I've tried replicating the various conda environment variables at the system level; tried adding all of the required python library paths from the virtual environment to the system path and system-wide python path, but to no avail.
I suspect that if I could activate the conda virtual environment as part of my python code, that would solve the problem. (I also suspect that installing all of the required modules into my base configuration would solve the problem, but i'd like to avoid that).
Here is the little test program I've written. This program works just fine with the basic Python modules like sys, os and so forth. It fails with the following error message when I try to run it and include Numpy or TensorFlow: (This is from the Windows Event Viewer after I try and start my service - which does install correctly):
Python could not import the service's module Traceback (most recent call last): File "D:\TFS\Projects\DEV\AEPEnrollmentForms\src\aepenrl\Windows_Service_Example.py", line 35, in import numpy as np File "C:\Users\pboerner\AppData\Local\conda\conda\envs\aepenr\lib\site-packages\numpy__init__.py", line 140, in from . import _distributor_init File "C:\Users\pboerner\AppData\Local\conda\conda\envs\aepenr\lib\site-packages\numpy_distributor_init.py", line 34, in from . import _mklinit ImportError: DLL load failed: The specified module could not be found. %2: %3
Here is the code for the simple test program. (Most of the Windows Service integration work I took from an excellent article provided by Davide Mastromatteo)
import numpy as np
import socket
import sys
import time
import win32serviceutil
import servicemanager
import win32event
import win32service
class SimpleService(win32serviceutil.ServiceFramework):
'''Base class to create winservice in Python'''
_svc_name_ = 'TestPythonSrvc'
_svc_display_name_ = 'Test Python Service'
_svc_description_ = 'Test to see how to create a windows service with python'
@classmethod
def parse_command_line(cls):
'''
ClassMethod to parse the command line
'''
win32serviceutil.HandleCommandLine(cls)
def __init__(self, args):
'''
Constructor of the winservice
'''
self.isrunning=True
self.fid = open("D:\\temp\\simple_service.txt", "w")
self.fid.write("Initialize\n")
self.fid.flush()
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
'''
self.isrunning = True
self.fid.write("Start method called\n")
self.fid.flush()
def stop(self):
'''
Override to add logic before the stop
eg. invalidating running condition
'''
self.isrunning = False
self.fid.write("STOP method called. Setting stop flag\n")
self.fid.flush()
def main(self):
'''
Main class to be ovverridden to add logic
'''
a = np.zeros((100,1))
while True:
if self.isrunning:
self.fid.write(f"Tick. Numpy array shape {a.shape}\n")
self.fid.flush()
time.sleep(1)
else:
self.fid.write("Breaking out of main loop\n")
self.fid.flush()
break;
self.fid.write("Closing the log file\n")
self.fid.flush()
self.fid.close()
if __name__ == '__main__':
# This code block was required to get this simple service example to run
# on a Windows 10 laptop with Admin privs. Only calling the
# HandleCommandLine method alone didn'd seem to work. Not sure why but this
# code was provided as a solution on the Web.
if len(sys.argv) == 1:
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(SimpleService)
servicemanager.StartServiceCtrlDispatcher()
else:
win32serviceutil.HandleCommandLine(SimpleService)