mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-15 01:32:57 -07:00
* Bump cherrypy from 18.8.0 to 18.9.0 Bumps [cherrypy](https://github.com/cherrypy/cherrypy) from 18.8.0 to 18.9.0. - [Changelog](https://github.com/cherrypy/cherrypy/blob/main/CHANGES.rst) - [Commits](https://github.com/cherrypy/cherrypy/compare/v18.8.0...v18.9.0) --- updated-dependencies: - dependency-name: cherrypy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Update cherrypy==18.9.0 --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> [skip ci]
672 lines
24 KiB
Python
672 lines
24 KiB
Python
"""Utilities for registering objects.
|
|
|
|
This module contains utility functions to register Python objects as
|
|
valid COM Servers. The RegisterServer function provides all information
|
|
necessary to allow the COM framework to respond to a request for a COM object,
|
|
construct the necessary Python object, and dispatch COM events.
|
|
|
|
"""
|
|
import os
|
|
import sys
|
|
|
|
import pythoncom
|
|
import win32api
|
|
import win32con
|
|
import winerror
|
|
|
|
CATID_PythonCOMServer = "{B3EF80D0-68E2-11D0-A689-00C04FD658FF}"
|
|
|
|
|
|
def _set_subkeys(keyName, valueDict, base=win32con.HKEY_CLASSES_ROOT):
|
|
hkey = win32api.RegCreateKey(base, keyName)
|
|
try:
|
|
for key, value in valueDict.items():
|
|
win32api.RegSetValueEx(hkey, key, None, win32con.REG_SZ, value)
|
|
finally:
|
|
win32api.RegCloseKey(hkey)
|
|
|
|
|
|
def _set_string(path, value, base=win32con.HKEY_CLASSES_ROOT):
|
|
"Set a string value in the registry."
|
|
|
|
win32api.RegSetValue(base, path, win32con.REG_SZ, value)
|
|
|
|
|
|
def _get_string(path, base=win32con.HKEY_CLASSES_ROOT):
|
|
"Get a string value from the registry."
|
|
|
|
try:
|
|
return win32api.RegQueryValue(base, path)
|
|
except win32api.error:
|
|
return None
|
|
|
|
|
|
def _remove_key(path, base=win32con.HKEY_CLASSES_ROOT):
|
|
"Remove a string from the registry."
|
|
|
|
try:
|
|
win32api.RegDeleteKey(base, path)
|
|
except win32api.error as xxx_todo_changeme1:
|
|
(code, fn, msg) = xxx_todo_changeme1.args
|
|
if code != winerror.ERROR_FILE_NOT_FOUND:
|
|
raise win32api.error(code, fn, msg)
|
|
|
|
|
|
def recurse_delete_key(path, base=win32con.HKEY_CLASSES_ROOT):
|
|
"""Recursively delete registry keys.
|
|
|
|
This is needed since you can't blast a key when subkeys exist.
|
|
"""
|
|
try:
|
|
h = win32api.RegOpenKey(base, path)
|
|
except win32api.error as xxx_todo_changeme2:
|
|
(code, fn, msg) = xxx_todo_changeme2.args
|
|
if code != winerror.ERROR_FILE_NOT_FOUND:
|
|
raise win32api.error(code, fn, msg)
|
|
else:
|
|
# parent key found and opened successfully. do some work, making sure
|
|
# to always close the thing (error or no).
|
|
try:
|
|
# remove all of the subkeys
|
|
while 1:
|
|
try:
|
|
subkeyname = win32api.RegEnumKey(h, 0)
|
|
except win32api.error as xxx_todo_changeme:
|
|
(code, fn, msg) = xxx_todo_changeme.args
|
|
if code != winerror.ERROR_NO_MORE_ITEMS:
|
|
raise win32api.error(code, fn, msg)
|
|
break
|
|
recurse_delete_key(path + "\\" + subkeyname, base)
|
|
|
|
# remove the parent key
|
|
_remove_key(path, base)
|
|
finally:
|
|
win32api.RegCloseKey(h)
|
|
|
|
|
|
def _cat_registrar():
|
|
return pythoncom.CoCreateInstance(
|
|
pythoncom.CLSID_StdComponentCategoriesMgr,
|
|
None,
|
|
pythoncom.CLSCTX_INPROC_SERVER,
|
|
pythoncom.IID_ICatRegister,
|
|
)
|
|
|
|
|
|
def _find_localserver_exe(mustfind):
|
|
if not sys.platform.startswith("win32"):
|
|
return sys.executable
|
|
if pythoncom.__file__.find("_d") < 0:
|
|
exeBaseName = "pythonw.exe"
|
|
else:
|
|
exeBaseName = "pythonw_d.exe"
|
|
# First see if in the same directory as this .EXE
|
|
exeName = os.path.join(os.path.split(sys.executable)[0], exeBaseName)
|
|
if not os.path.exists(exeName):
|
|
# See if in our sys.prefix directory
|
|
exeName = os.path.join(sys.prefix, exeBaseName)
|
|
if not os.path.exists(exeName):
|
|
# See if in our sys.prefix/pcbuild directory (for developers)
|
|
if "64 bit" in sys.version:
|
|
exeName = os.path.join(sys.prefix, "PCbuild", "amd64", exeBaseName)
|
|
else:
|
|
exeName = os.path.join(sys.prefix, "PCbuild", exeBaseName)
|
|
if not os.path.exists(exeName):
|
|
# See if the registry has some info.
|
|
try:
|
|
key = "SOFTWARE\\Python\\PythonCore\\%s\\InstallPath" % sys.winver
|
|
path = win32api.RegQueryValue(win32con.HKEY_LOCAL_MACHINE, key)
|
|
exeName = os.path.join(path, exeBaseName)
|
|
except (AttributeError, win32api.error):
|
|
pass
|
|
if not os.path.exists(exeName):
|
|
if mustfind:
|
|
raise RuntimeError("Can not locate the program '%s'" % exeBaseName)
|
|
return None
|
|
return exeName
|
|
|
|
|
|
def _find_localserver_module():
|
|
import win32com.server
|
|
|
|
path = win32com.server.__path__[0]
|
|
baseName = "localserver"
|
|
pyfile = os.path.join(path, baseName + ".py")
|
|
try:
|
|
os.stat(pyfile)
|
|
except os.error:
|
|
# See if we have a compiled extension
|
|
if __debug__:
|
|
ext = ".pyc"
|
|
else:
|
|
ext = ".pyo"
|
|
pyfile = os.path.join(path, baseName + ext)
|
|
try:
|
|
os.stat(pyfile)
|
|
except os.error:
|
|
raise RuntimeError(
|
|
"Can not locate the Python module 'win32com.server.%s'" % baseName
|
|
)
|
|
return pyfile
|
|
|
|
|
|
def RegisterServer(
|
|
clsid,
|
|
pythonInstString=None,
|
|
desc=None,
|
|
progID=None,
|
|
verProgID=None,
|
|
defIcon=None,
|
|
threadingModel="both",
|
|
policy=None,
|
|
catids=[],
|
|
other={},
|
|
addPyComCat=None,
|
|
dispatcher=None,
|
|
clsctx=None,
|
|
addnPath=None,
|
|
):
|
|
"""Registers a Python object as a COM Server. This enters almost all necessary
|
|
information in the system registry, allowing COM to use the object.
|
|
|
|
clsid -- The (unique) CLSID of the server.
|
|
pythonInstString -- A string holding the instance name that will be created
|
|
whenever COM requests a new object.
|
|
desc -- The description of the COM object.
|
|
progID -- The user name of this object (eg, Word.Document)
|
|
verProgId -- The user name of this version's implementation (eg Word.6.Document)
|
|
defIcon -- The default icon for the object.
|
|
threadingModel -- The threading model this object supports.
|
|
policy -- The policy to use when creating this object.
|
|
catids -- A list of category ID's this object belongs in.
|
|
other -- A dictionary of extra items to be registered.
|
|
addPyComCat -- A flag indicating if the object should be added to the list
|
|
of Python servers installed on the machine. If None (the default)
|
|
then it will be registered when running from python source, but
|
|
not registered if running in a frozen environment.
|
|
dispatcher -- The dispatcher to use when creating this object.
|
|
clsctx -- One of the CLSCTX_* constants.
|
|
addnPath -- An additional path the COM framework will add to sys.path
|
|
before attempting to create the object.
|
|
"""
|
|
|
|
### backwards-compat check
|
|
### Certain policies do not require a "class name", just the policy itself.
|
|
if not pythonInstString and not policy:
|
|
raise TypeError(
|
|
"You must specify either the Python Class or Python Policy which implement the COM object."
|
|
)
|
|
|
|
keyNameRoot = "CLSID\\%s" % str(clsid)
|
|
_set_string(keyNameRoot, desc)
|
|
|
|
# Also register as an "Application" so DCOM etc all see us.
|
|
_set_string("AppID\\%s" % clsid, progID)
|
|
# Depending on contexts requested, register the specified server type.
|
|
# Set default clsctx.
|
|
if not clsctx:
|
|
clsctx = pythoncom.CLSCTX_INPROC_SERVER | pythoncom.CLSCTX_LOCAL_SERVER
|
|
# And if we are frozen, ignore the ones that don't make sense in this
|
|
# context.
|
|
if pythoncom.frozen:
|
|
assert (
|
|
sys.frozen
|
|
), "pythoncom is frozen, but sys.frozen is not set - don't know the context!"
|
|
if sys.frozen == "dll":
|
|
clsctx = clsctx & pythoncom.CLSCTX_INPROC_SERVER
|
|
else:
|
|
clsctx = clsctx & pythoncom.CLSCTX_LOCAL_SERVER
|
|
# Now setup based on the clsctx left over.
|
|
if clsctx & pythoncom.CLSCTX_INPROC_SERVER:
|
|
# get the module to use for registration.
|
|
# nod to Gordon's installer - if sys.frozen and sys.frozendllhandle
|
|
# exist, then we are being registered via a DLL - use this DLL as the
|
|
# file name.
|
|
if pythoncom.frozen:
|
|
if hasattr(sys, "frozendllhandle"):
|
|
dllName = win32api.GetModuleFileName(sys.frozendllhandle)
|
|
else:
|
|
raise RuntimeError(
|
|
"We appear to have a frozen DLL, but I don't know the DLL to use"
|
|
)
|
|
else:
|
|
# Normal case - running from .py file, so register pythoncom's DLL.
|
|
# Although now we prefer a 'loader' DLL if it exists to avoid some
|
|
# manifest issues (the 'loader' DLL has a manifest, but pythoncom does not)
|
|
pythoncom_dir = os.path.dirname(pythoncom.__file__)
|
|
suffix = "_d" if "_d" in pythoncom.__file__ else ""
|
|
# Always register with the full path to the DLLs.
|
|
loadername = os.path.join(
|
|
pythoncom_dir,
|
|
"pythoncomloader%d%d%s.dll"
|
|
% (sys.version_info[0], sys.version_info[1], suffix),
|
|
)
|
|
dllName = loadername if os.path.isfile(loadername) else pythoncom.__file__
|
|
|
|
_set_subkeys(
|
|
keyNameRoot + "\\InprocServer32",
|
|
{
|
|
None: dllName,
|
|
"ThreadingModel": threadingModel,
|
|
},
|
|
)
|
|
else: # Remove any old InProcServer32 registrations
|
|
_remove_key(keyNameRoot + "\\InprocServer32")
|
|
|
|
if clsctx & pythoncom.CLSCTX_LOCAL_SERVER:
|
|
if pythoncom.frozen:
|
|
# If we are frozen, we write "{exe} /Automate", just
|
|
# like "normal" .EXEs do
|
|
exeName = win32api.GetShortPathName(sys.executable)
|
|
command = "%s /Automate" % (exeName,)
|
|
else:
|
|
# Running from .py sources - we need to write
|
|
# 'python.exe win32com\server\localserver.py {clsid}"
|
|
exeName = _find_localserver_exe(1)
|
|
exeName = win32api.GetShortPathName(exeName)
|
|
pyfile = _find_localserver_module()
|
|
command = '%s "%s" %s' % (exeName, pyfile, str(clsid))
|
|
_set_string(keyNameRoot + "\\LocalServer32", command)
|
|
else: # Remove any old LocalServer32 registrations
|
|
_remove_key(keyNameRoot + "\\LocalServer32")
|
|
|
|
if pythonInstString:
|
|
_set_string(keyNameRoot + "\\PythonCOM", pythonInstString)
|
|
else:
|
|
_remove_key(keyNameRoot + "\\PythonCOM")
|
|
if policy:
|
|
_set_string(keyNameRoot + "\\PythonCOMPolicy", policy)
|
|
else:
|
|
_remove_key(keyNameRoot + "\\PythonCOMPolicy")
|
|
|
|
if dispatcher:
|
|
_set_string(keyNameRoot + "\\PythonCOMDispatcher", dispatcher)
|
|
else:
|
|
_remove_key(keyNameRoot + "\\PythonCOMDispatcher")
|
|
|
|
if defIcon:
|
|
_set_string(keyNameRoot + "\\DefaultIcon", defIcon)
|
|
else:
|
|
_remove_key(keyNameRoot + "\\DefaultIcon")
|
|
|
|
if addnPath:
|
|
_set_string(keyNameRoot + "\\PythonCOMPath", addnPath)
|
|
else:
|
|
_remove_key(keyNameRoot + "\\PythonCOMPath")
|
|
|
|
if addPyComCat is None:
|
|
addPyComCat = pythoncom.frozen == 0
|
|
if addPyComCat:
|
|
catids = catids + [CATID_PythonCOMServer]
|
|
|
|
# Set up the implemented categories
|
|
if catids:
|
|
regCat = _cat_registrar()
|
|
regCat.RegisterClassImplCategories(clsid, catids)
|
|
|
|
# set up any other reg values they might have
|
|
if other:
|
|
for key, value in other.items():
|
|
_set_string(keyNameRoot + "\\" + key, value)
|
|
|
|
if progID:
|
|
# set the progID as the most specific that was given to us
|
|
if verProgID:
|
|
_set_string(keyNameRoot + "\\ProgID", verProgID)
|
|
else:
|
|
_set_string(keyNameRoot + "\\ProgID", progID)
|
|
|
|
# Set up the root entries - version independent.
|
|
if desc:
|
|
_set_string(progID, desc)
|
|
_set_string(progID + "\\CLSID", str(clsid))
|
|
|
|
# Set up the root entries - version dependent.
|
|
if verProgID:
|
|
# point from independent to the current version
|
|
_set_string(progID + "\\CurVer", verProgID)
|
|
|
|
# point to the version-independent one
|
|
_set_string(keyNameRoot + "\\VersionIndependentProgID", progID)
|
|
|
|
# set up the versioned progID
|
|
if desc:
|
|
_set_string(verProgID, desc)
|
|
_set_string(verProgID + "\\CLSID", str(clsid))
|
|
|
|
|
|
def GetUnregisterServerKeys(clsid, progID=None, verProgID=None, customKeys=None):
|
|
"""Given a server, return a list of of ("key", root), which are keys recursively
|
|
and uncondtionally deleted at unregister or uninstall time.
|
|
"""
|
|
# remove the main CLSID registration
|
|
ret = [("CLSID\\%s" % str(clsid), win32con.HKEY_CLASSES_ROOT)]
|
|
# remove the versioned ProgID registration
|
|
if verProgID:
|
|
ret.append((verProgID, win32con.HKEY_CLASSES_ROOT))
|
|
# blow away the independent ProgID. we can't leave it since we just
|
|
# torched the class.
|
|
### could potentially check the CLSID... ?
|
|
if progID:
|
|
ret.append((progID, win32con.HKEY_CLASSES_ROOT))
|
|
# The DCOM config tool may write settings to the AppID key for our CLSID
|
|
ret.append(("AppID\\%s" % str(clsid), win32con.HKEY_CLASSES_ROOT))
|
|
# Any custom keys?
|
|
if customKeys:
|
|
ret = ret + customKeys
|
|
|
|
return ret
|
|
|
|
|
|
def UnregisterServer(clsid, progID=None, verProgID=None, customKeys=None):
|
|
"""Unregisters a Python COM server."""
|
|
|
|
for args in GetUnregisterServerKeys(clsid, progID, verProgID, customKeys):
|
|
recurse_delete_key(*args)
|
|
|
|
### it might be nice at some point to "roll back" the independent ProgID
|
|
### to an earlier version if one exists, and just blowing away the
|
|
### specified version of the ProgID (and its corresponding CLSID)
|
|
### another time, though...
|
|
|
|
### NOTE: ATL simply blows away the above three keys without the
|
|
### potential checks that I describe. Assuming that defines the
|
|
### "standard" then we have no additional changes necessary.
|
|
|
|
|
|
def GetRegisteredServerOption(clsid, optionName):
|
|
"""Given a CLSID for a server and option name, return the option value"""
|
|
keyNameRoot = "CLSID\\%s\\%s" % (str(clsid), str(optionName))
|
|
return _get_string(keyNameRoot)
|
|
|
|
|
|
def _get(ob, attr, default=None):
|
|
try:
|
|
return getattr(ob, attr)
|
|
except AttributeError:
|
|
pass
|
|
# look down sub-classes
|
|
try:
|
|
bases = ob.__bases__
|
|
except AttributeError:
|
|
# ob is not a class - no probs.
|
|
return default
|
|
for base in bases:
|
|
val = _get(base, attr, None)
|
|
if val is not None:
|
|
return val
|
|
return default
|
|
|
|
|
|
def RegisterClasses(*classes, **flags):
|
|
quiet = "quiet" in flags and flags["quiet"]
|
|
debugging = "debug" in flags and flags["debug"]
|
|
for cls in classes:
|
|
clsid = cls._reg_clsid_
|
|
progID = _get(cls, "_reg_progid_")
|
|
desc = _get(cls, "_reg_desc_", progID)
|
|
spec = _get(cls, "_reg_class_spec_")
|
|
verProgID = _get(cls, "_reg_verprogid_")
|
|
defIcon = _get(cls, "_reg_icon_")
|
|
threadingModel = _get(cls, "_reg_threading_", "both")
|
|
catids = _get(cls, "_reg_catids_", [])
|
|
options = _get(cls, "_reg_options_", {})
|
|
policySpec = _get(cls, "_reg_policy_spec_")
|
|
clsctx = _get(cls, "_reg_clsctx_")
|
|
tlb_filename = _get(cls, "_reg_typelib_filename_")
|
|
# default to being a COM category only when not frozen.
|
|
addPyComCat = not _get(cls, "_reg_disable_pycomcat_", pythoncom.frozen != 0)
|
|
addnPath = None
|
|
if debugging:
|
|
# If the class has a debugging dispatcher specified, use it, otherwise
|
|
# use our default dispatcher.
|
|
dispatcherSpec = _get(cls, "_reg_debug_dispatcher_spec_")
|
|
if dispatcherSpec is None:
|
|
dispatcherSpec = "win32com.server.dispatcher.DefaultDebugDispatcher"
|
|
# And remember the debugging flag as servers may wish to use it at runtime.
|
|
debuggingDesc = "(for debugging)"
|
|
options["Debugging"] = "1"
|
|
else:
|
|
dispatcherSpec = _get(cls, "_reg_dispatcher_spec_")
|
|
debuggingDesc = ""
|
|
options["Debugging"] = "0"
|
|
|
|
if spec is None:
|
|
moduleName = cls.__module__
|
|
if moduleName == "__main__":
|
|
# Use argv[0] to determine the module name.
|
|
try:
|
|
# Use the win32api to find the case-sensitive name
|
|
moduleName = os.path.splitext(
|
|
win32api.FindFiles(sys.argv[0])[0][8]
|
|
)[0]
|
|
except (IndexError, win32api.error):
|
|
# Can't find the script file - the user must explicitely set the _reg_... attribute.
|
|
raise TypeError(
|
|
"Can't locate the script hosting the COM object - please set _reg_class_spec_ in your object"
|
|
)
|
|
|
|
spec = moduleName + "." + cls.__name__
|
|
# Frozen apps don't need their directory on sys.path
|
|
if not pythoncom.frozen:
|
|
scriptDir = os.path.split(sys.argv[0])[0]
|
|
if not scriptDir:
|
|
scriptDir = "."
|
|
addnPath = win32api.GetFullPathName(scriptDir)
|
|
|
|
RegisterServer(
|
|
clsid,
|
|
spec,
|
|
desc,
|
|
progID,
|
|
verProgID,
|
|
defIcon,
|
|
threadingModel,
|
|
policySpec,
|
|
catids,
|
|
options,
|
|
addPyComCat,
|
|
dispatcherSpec,
|
|
clsctx,
|
|
addnPath,
|
|
)
|
|
if not quiet:
|
|
print("Registered:", progID or spec, debuggingDesc)
|
|
# Register the typelibrary
|
|
if tlb_filename:
|
|
tlb_filename = os.path.abspath(tlb_filename)
|
|
typelib = pythoncom.LoadTypeLib(tlb_filename)
|
|
pythoncom.RegisterTypeLib(typelib, tlb_filename)
|
|
if not quiet:
|
|
print("Registered type library:", tlb_filename)
|
|
extra = flags.get("finalize_register")
|
|
if extra:
|
|
extra()
|
|
|
|
|
|
def UnregisterClasses(*classes, **flags):
|
|
quiet = "quiet" in flags and flags["quiet"]
|
|
for cls in classes:
|
|
clsid = cls._reg_clsid_
|
|
progID = _get(cls, "_reg_progid_")
|
|
verProgID = _get(cls, "_reg_verprogid_")
|
|
customKeys = _get(cls, "_reg_remove_keys_")
|
|
unregister_typelib = _get(cls, "_reg_typelib_filename_") is not None
|
|
|
|
UnregisterServer(clsid, progID, verProgID, customKeys)
|
|
if not quiet:
|
|
print("Unregistered:", progID or str(clsid))
|
|
if unregister_typelib:
|
|
tlb_guid = _get(cls, "_typelib_guid_")
|
|
if tlb_guid is None:
|
|
# I guess I could load the typelib, but they need the GUID anyway.
|
|
print("Have typelib filename, but no GUID - can't unregister")
|
|
else:
|
|
major, minor = _get(cls, "_typelib_version_", (1, 0))
|
|
lcid = _get(cls, "_typelib_lcid_", 0)
|
|
try:
|
|
pythoncom.UnRegisterTypeLib(tlb_guid, major, minor, lcid)
|
|
if not quiet:
|
|
print("Unregistered type library")
|
|
except pythoncom.com_error:
|
|
pass
|
|
|
|
extra = flags.get("finalize_unregister")
|
|
if extra:
|
|
extra()
|
|
|
|
|
|
#
|
|
# Unregister info is for installers or external uninstallers.
|
|
# The WISE installer, for example firstly registers the COM server,
|
|
# then queries for the Unregister info, appending it to its
|
|
# install log. Uninstalling the package will the uninstall the server
|
|
def UnregisterInfoClasses(*classes, **flags):
|
|
ret = []
|
|
for cls in classes:
|
|
clsid = cls._reg_clsid_
|
|
progID = _get(cls, "_reg_progid_")
|
|
verProgID = _get(cls, "_reg_verprogid_")
|
|
customKeys = _get(cls, "_reg_remove_keys_")
|
|
|
|
ret = ret + GetUnregisterServerKeys(clsid, progID, verProgID, customKeys)
|
|
return ret
|
|
|
|
|
|
# Attempt to 're-execute' our current process with elevation.
|
|
def ReExecuteElevated(flags):
|
|
import tempfile
|
|
|
|
import win32event # we've already checked we are running XP above
|
|
import win32process
|
|
import winxpgui
|
|
from win32com.shell import shellcon
|
|
from win32com.shell.shell import ShellExecuteEx
|
|
|
|
if not flags["quiet"]:
|
|
print("Requesting elevation and retrying...")
|
|
new_params = " ".join(['"' + a + '"' for a in sys.argv])
|
|
# If we aren't already in unattended mode, we want our sub-process to
|
|
# be.
|
|
if not flags["unattended"]:
|
|
new_params += " --unattended"
|
|
# specifying the parent means the dialog is centered over our window,
|
|
# which is a good usability clue.
|
|
# hwnd is unlikely on the command-line, but flags may come from elsewhere
|
|
hwnd = flags.get("hwnd", None)
|
|
if hwnd is None:
|
|
try:
|
|
hwnd = winxpgui.GetConsoleWindow()
|
|
except winxpgui.error:
|
|
hwnd = 0
|
|
# Redirect output so we give the user some clue what went wrong. This
|
|
# also means we need to use COMSPEC. However, the "current directory"
|
|
# appears to end up ignored - so we execute things via a temp batch file.
|
|
tempbase = tempfile.mktemp("pycomserverreg")
|
|
outfile = tempbase + ".out"
|
|
batfile = tempbase + ".bat"
|
|
|
|
# If registering from pythonwin, need to run python console instead since
|
|
# pythonwin will just open script for editting
|
|
current_exe = os.path.split(sys.executable)[1].lower()
|
|
exe_to_run = None
|
|
if current_exe == "pythonwin.exe":
|
|
exe_to_run = os.path.join(sys.prefix, "python.exe")
|
|
elif current_exe == "pythonwin_d.exe":
|
|
exe_to_run = os.path.join(sys.prefix, "python_d.exe")
|
|
if not exe_to_run or not os.path.exists(exe_to_run):
|
|
exe_to_run = sys.executable
|
|
|
|
try:
|
|
batf = open(batfile, "w")
|
|
try:
|
|
cwd = os.getcwd()
|
|
print("@echo off", file=batf)
|
|
# nothing is 'inherited' by the elevated process, including the
|
|
# environment. I wonder if we need to set more?
|
|
print("set PYTHONPATH=%s" % os.environ.get("PYTHONPATH", ""), file=batf)
|
|
# may be on a different drive - select that before attempting to CD.
|
|
print(os.path.splitdrive(cwd)[0], file=batf)
|
|
print('cd "%s"' % os.getcwd(), file=batf)
|
|
print(
|
|
'%s %s > "%s" 2>&1'
|
|
% (win32api.GetShortPathName(exe_to_run), new_params, outfile),
|
|
file=batf,
|
|
)
|
|
finally:
|
|
batf.close()
|
|
executable = os.environ.get("COMSPEC", "cmd.exe")
|
|
rc = ShellExecuteEx(
|
|
hwnd=hwnd,
|
|
fMask=shellcon.SEE_MASK_NOCLOSEPROCESS,
|
|
lpVerb="runas",
|
|
lpFile=executable,
|
|
lpParameters='/C "%s"' % batfile,
|
|
nShow=win32con.SW_SHOW,
|
|
)
|
|
hproc = rc["hProcess"]
|
|
win32event.WaitForSingleObject(hproc, win32event.INFINITE)
|
|
exit_code = win32process.GetExitCodeProcess(hproc)
|
|
outf = open(outfile)
|
|
try:
|
|
output = outf.read()
|
|
finally:
|
|
outf.close()
|
|
|
|
if exit_code:
|
|
# Even if quiet you get to see this message.
|
|
print("Error: registration failed (exit code %s)." % exit_code)
|
|
# if we are quiet then the output if likely to already be nearly
|
|
# empty, so always print it.
|
|
print(output, end=" ")
|
|
finally:
|
|
for f in (outfile, batfile):
|
|
try:
|
|
os.unlink(f)
|
|
except os.error as exc:
|
|
print("Failed to remove tempfile '%s': %s" % (f, exc))
|
|
|
|
|
|
def UseCommandLine(*classes, **flags):
|
|
unregisterInfo = "--unregister_info" in sys.argv
|
|
unregister = "--unregister" in sys.argv
|
|
flags["quiet"] = flags.get("quiet", 0) or "--quiet" in sys.argv
|
|
flags["debug"] = flags.get("debug", 0) or "--debug" in sys.argv
|
|
flags["unattended"] = flags.get("unattended", 0) or "--unattended" in sys.argv
|
|
if unregisterInfo:
|
|
return UnregisterInfoClasses(*classes, **flags)
|
|
try:
|
|
if unregister:
|
|
UnregisterClasses(*classes, **flags)
|
|
else:
|
|
RegisterClasses(*classes, **flags)
|
|
except win32api.error as exc:
|
|
# If we are on xp+ and have "access denied", retry using
|
|
# ShellExecuteEx with 'runas' verb to force elevation (vista) and/or
|
|
# admin login dialog (vista/xp)
|
|
if (
|
|
flags["unattended"]
|
|
or exc.winerror != winerror.ERROR_ACCESS_DENIED
|
|
or sys.getwindowsversion()[0] < 5
|
|
):
|
|
raise
|
|
ReExecuteElevated(flags)
|
|
|
|
|
|
def RegisterPyComCategory():
|
|
"""Register the Python COM Server component category."""
|
|
regCat = _cat_registrar()
|
|
regCat.RegisterCategories([(CATID_PythonCOMServer, 0x0409, "Python COM Server")])
|
|
|
|
|
|
if not pythoncom.frozen:
|
|
try:
|
|
win32api.RegQueryValue(
|
|
win32con.HKEY_CLASSES_ROOT,
|
|
"Component Categories\\%s" % CATID_PythonCOMServer,
|
|
)
|
|
except win32api.error:
|
|
try:
|
|
RegisterPyComCategory()
|
|
except pythoncom.error: # Error with the COM category manager - oh well.
|
|
pass
|