diff --git a/README.md b/README.md index 09c7638412..765575ce84 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,17 @@ The possible solutions are: * Otherwise, find and remove all other copies of `pywintypesXX.dll` and `pythoncomXX.dll` (where `XX` is the Python version - eg, "39") +### Running as a Windows Service + +Modern Python installers do not, by default, install Python in a way that is suitable for +running as a service, particularly for other users. + +* Ensure Python is installed in a location where the user running the service has + access to the installation and is able to load `pywintypesXX.dll` and `pythonXX.dll`. + +* Manually copy `pythonservice.exe` from the `site-packages/win32` directory to + the same place as these DLLs. + ## Building from source Building from source has been simplified recently - you just need Visual Studio diff --git a/win32/Lib/win32serviceutil.py b/win32/Lib/win32serviceutil.py index 5e3760c8ac..7a861b464b 100644 --- a/win32/Lib/win32serviceutil.py +++ b/win32/Lib/win32serviceutil.py @@ -8,11 +8,58 @@ import win32service, win32api, win32con, winerror import sys, pywintypes, os, warnings +import importlib error = RuntimeError +# We need to find a `pythonservice.exe` to register as a service. Now that the +# 90's have passed, we really can't assume that pythonXX.dll/pywintypesXX.dll +# etc are all in SYSTEM32. pythonservice.exe isn't, by default, in a place where +# these DLLs are likely to be found in a service context. + +# We could try and have the postinstall script copy the .exe? But I guess the +# number of users is tiny and everyone else doesn't need the extra .exe hanging +# around on the PATH. +# So - just make noise. +noise = """ +**** WARNING **** +The executable at "{exe}" is being used as a service. + +This executable doesn't have pythonXX.dll and/or pywintypesXX.dll in the same +directory. This is likely to fail when used in the context of a service. + +The exact environment needed will depend on which user runs the service and +where Python is installed. If the service fails to run, this will be why. + +NOTE: You should consider copying this executable to the directory where these +DLLs live - "{good}" might be a good place. +**** +""" + def LocatePythonServiceExe(exeName=None): + found = _LocatePythonServiceExe(exeName) + where = os.path.dirname(found) + under_d = "_d" if "_d.pyd" in importlib.machinery.EXTENSION_SUFFIXES else "" + suffix = "%s%s%s.dll" % ( + sys.version_info[0], + sys.version_info[1], + under_d, + ) + # If someone isn't using pythonservice.exe we assume they know what they are doing. + if found != sys.executable and ( + not os.path.exists(os.path.join(where, "python{}".format(suffix))) + or not os.path.exists(os.path.join(where, "pywintypes{}".format(suffix))) + ): + print( + noise.format(exe=found, good=os.path.dirname(pywintypes.__file__)), + file=sys.stderr, + ) + + return found + + +def _LocatePythonServiceExe(exeName=None): if not exeName and hasattr(sys, "frozen"): # If py2exe etc calls this with no exeName, default is current exe. return sys.executable