Thanks very much for posting this question and a solution. I took a slightly different approach which might also be useful. It is pretty difficult to find working tips for Python services, let alone doing it with a virtualenv. Anyway...
Steps
This is using Windows 7 x64, Python 3.5.1 x64, pywin32-220 (or pypiwin32-219).
- Open an Administrator command prompt.
- Create a virtualenv.
C:Python35python -m venv myvenv
- Activate the virtualenv.
call myvenvscriptsactivate.bat
- Install pywin32, either:
- Run the post-install script
python myvenvScriptspywin32_postinstall.py -install
.
- This script registers the DLL's in the system, and copies them to
C:WindowsSystem32
. The DLL's are named pythoncom35.dll
and pywintypes35.dll
. So virtual environments on the same machine on the same major Python point release will share these... it's a minor tradeoff :)
- Copy
myvenvLibsite-packageswin32pythonservice.exe
to myvenvScriptspythonservice.exe
- On the service class (whatever subclasses win32serviceutil.ServiceFramework), set the class property
_exe_path_
to point to this relocated exe. This will become the service binPath. For example: _exe_path_ = os.path.join(*[os.environ['VIRTUAL_ENV'], 'Scripts', 'pythonservice.exe'])
.
Discussion
I think why this works is that Python looks upwards to figure out where the Libs folders are and based on that sets package import paths, similar to the accepted answer. When pythonservice.exe is in the original location, that doesn't seem to work smoothly.
It also resolves DLL linking problems (discoverable with depends.exe from http://www.dependencywalker.com/). Without the DLL business sorted out, it won't be possible to import from the *.pyd files from venvLibsite-packageswin32
as modules in your scripts. For example it's needed allow import servicemanager
; as servicemanager.pyd
is not in the package as a .py file, and has some cool Windows Event Log capabilities.
One of the problems I had with the accepted answer is that I couldn't figure out how to get it to accurately pick up on package.egg-link paths that are created when using setup.py develop
. These .egg-link files include the path to the package when it's not located in the virtualenv under myvenvLibsite-packages
.
If it all went smoothly, it should be possible to install, start and test the example win32 service (from an Admin prompt in the activated virtualenv):
python venvLibsite-packageswin32DemosservicepipeTestService.py install
python venvLibsite-packageswin32DemosservicepipeTestService.py start
python venvLibsite-packageswin32DemosservicepipeTestServiceClient.py
The Service Environment
Another important note in all this is that the service will execute the python code in a completely separate environment to the one you might run python myservice.py debug
. So for example os.environ['VIRTUAL_ENV']
will be empty when running the service. This can be handled by either:
- Setting environment variables from inside the script, e.g.
- Find current path starting from the sys.executable, as described in the accepted answer.
- Use that path to locate a config file.
- Read the config file and put them in the environment with
os.environ
.
- Add registry keys to the service with the environment variables.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…