Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate the Registrar._registry_entries method into an iterator class. #697

Merged
merged 7 commits into from
Dec 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 52 additions & 45 deletions comtypes/server/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,18 @@
python mycomobj.py /nodebug
"""

import sys
import logging
import os
import sys
import winreg
import logging
from ctypes import WinError, c_ulong, c_wchar_p, create_string_buffer, sizeof, windll
from typing import Iterator, Tuple

import comtypes
from comtypes.typeinfo import LoadTypeLibEx, UnRegisterTypeLib, REGKIND_REGISTER
import comtypes.server.inprocserver
from comtypes.hresult import *
from comtypes.server import w_getopt
import comtypes.server.inprocserver
from ctypes import windll, c_ulong, c_wchar_p, WinError, sizeof, create_string_buffer
from comtypes.typeinfo import REGKIND_REGISTER, LoadTypeLibEx, UnRegisterTypeLib

_debug = logging.getLogger(__name__).debug

Expand Down Expand Up @@ -147,8 +148,7 @@ def register(self, cls, executable=None):
self._register(cls, executable)

def _register(self, cls, executable=None):
table = self._registry_entries(cls)
table.sort()
table = sorted(RegistryEntries(cls))
_debug("Registering %s", cls)
for hkey, subkey, valuename, value in table:
_debug("[%s\\%s]", _explain(hkey), subkey)
Expand All @@ -159,7 +159,7 @@ def _register(self, cls, executable=None):
tlib = getattr(cls, "_reg_typelib_", None)
if tlib is not None:
if hasattr(sys, "frozendllhandle"):
dll = self._get_serverdll()
dll = _get_serverdll()
_debug("LoadTypeLibEx(%s, REGKIND_REGISTER)", dll)
LoadTypeLibEx(dll, REGKIND_REGISTER)
else:
Expand All @@ -184,7 +184,7 @@ def unregister(self, cls, force=False):
def _unregister(self, cls, force=False):
# If force==False, we only remove those entries that we
# actually would have written. It seems ATL does the same.
table = [t[:2] for t in self._registry_entries(cls)]
table = [t[:2] for t in RegistryEntries(cls)]
# only unique entries
table = list(set(table))
table.sort()
Expand Down Expand Up @@ -214,16 +214,26 @@ def _unregister(self, cls, force=False):
raise
_debug("Done")

def _get_serverdll(self):
"""Return the pathname of the dll hosting the COM object."""
handle = getattr(sys, "frozendllhandle", None)
if handle is not None:
buf = create_string_buffer(260)
windll.kernel32.GetModuleFileNameA(handle, buf, sizeof(buf))
return buf[:]
import _ctypes

return _ctypes.__file__
def _get_serverdll():
"""Return the pathname of the dll hosting the COM object."""
handle = getattr(sys, "frozendllhandle", None)
if handle is not None:
buf = create_string_buffer(260)
windll.kernel32.GetModuleFileNameA(handle, buf, sizeof(buf))
return buf[:]
import _ctypes

return _ctypes.__file__


class RegistryEntries(object):
def __init__(self, cls):
self._cls = cls
self._table = []

def _add(self, rootkey: int, subkey: str, name: str, value: str) -> None:
self._table.append((rootkey, subkey, name, value))

def _get_full_classname(self, cls):
"""Return <modulename>.<classname> for 'cls'."""
Expand All @@ -238,8 +248,8 @@ def _get_pythonpath(self, cls):
dirname = os.path.dirname(sys.modules[modname].__file__)
return os.path.abspath(dirname)

def _registry_entries(self, cls):
"""Return a sequence of tuples containing registry entries.
def __iter__(self) -> Iterator[Tuple[int, str, str, str]]:
"""Return a iterator of tuples containing registry entries.

The tuples must be (key, subkey, name, value).

Expand All @@ -259,16 +269,13 @@ def _registry_entries(self, cls):
Note that the first part of the progid string is typically the
IDL library name of the type library containing the coclass.
"""
cls = self._cls
HKCR = winreg.HKEY_CLASSES_ROOT

# table format: rootkey, subkey, valuename, value
table = []
append = lambda *args: table.append(args)

# basic entry - names the comobject
reg_clsid = str(
cls._reg_clsid_
) # that's the only required attribute for registration

# that's the only required attribute for registration
reg_clsid = str(cls._reg_clsid_)
reg_desc = getattr(cls, "_reg_desc_", "")
if not reg_desc:
# Simple minded algorithm to construct a description from
Expand All @@ -278,30 +285,30 @@ def _registry_entries(self, cls):
)
if reg_desc:
reg_desc = reg_desc.replace(".", " ")
append(HKCR, f"CLSID\\{reg_clsid}", "", reg_desc)
self._add(HKCR, f"CLSID\\{reg_clsid}", "", reg_desc)

reg_progid = getattr(cls, "_reg_progid_", None)
if reg_progid:
# for ProgIDFromCLSID:
append(HKCR, f"CLSID\\{reg_clsid}\\ProgID", "", reg_progid) # 1
self._add(HKCR, f"CLSID\\{reg_clsid}\\ProgID", "", reg_progid) # 1

# for CLSIDFromProgID
if reg_desc:
append(HKCR, reg_progid, "", reg_desc) # 2
append(HKCR, f"{reg_progid}\\CLSID", "", reg_clsid) # 3
self._add(HKCR, reg_progid, "", reg_desc) # 2
self._add(HKCR, f"{reg_progid}\\CLSID", "", reg_clsid) # 3

reg_novers_progid = getattr(cls, "_reg_novers_progid_", None)
if reg_novers_progid:
append(
self._add(
HKCR,
f"CLSID\\{reg_clsid}\\VersionIndependentProgID", # 1a
"",
reg_novers_progid,
)
if reg_desc:
append(HKCR, reg_novers_progid, "", reg_desc) # 2a
append(HKCR, f"{reg_novers_progid}\\CurVer", "", reg_progid) #
append(HKCR, f"{reg_novers_progid}\\CLSID", "", reg_clsid) # 3a
self._add(HKCR, reg_novers_progid, "", reg_desc) # 2a
self._add(HKCR, f"{reg_novers_progid}\\CurVer", "", reg_progid) #
self._add(HKCR, f"{reg_novers_progid}\\CLSID", "", reg_clsid) # 3a

clsctx = getattr(cls, "_reg_clsctx_", 0)

Expand All @@ -317,31 +324,31 @@ def _registry_entries(self, cls):
script = os.path.abspath(sys.modules[cls.__module__].__file__)
if " " in script:
script = f'"{script}"'
append(HKCR, rf"CLSID\{reg_clsid}\LocalServer32", "", f"{exe} {script}")
self._add(
HKCR, rf"CLSID\{reg_clsid}\LocalServer32", "", f"{exe} {script}"
)
else:
append(HKCR, rf"CLSID\{reg_clsid}\LocalServer32", "", f"{exe}")
self._add(HKCR, rf"CLSID\{reg_clsid}\LocalServer32", "", f"{exe}")

# Register InprocServer32 only when run from script or from
# py2exe dll server, not from py2exe exe server.
if clsctx & comtypes.CLSCTX_INPROC_SERVER and getattr(sys, "frozen", None) in (
None,
"dll",
):
append(
HKCR, rf"CLSID\{reg_clsid}\InprocServer32", "", self._get_serverdll()
)
self._add(HKCR, rf"CLSID\{reg_clsid}\InprocServer32", "", _get_serverdll())
# only for non-frozen inproc servers the PythonPath/PythonClass is needed.
if (
not hasattr(sys, "frozendllhandle")
or not comtypes.server.inprocserver._clsid_to_class
):
append(
self._add(
HKCR,
rf"CLSID\{reg_clsid}\InprocServer32",
"PythonClass",
self._get_full_classname(cls),
)
append(
self._add(
HKCR,
rf"CLSID\{reg_clsid}\InprocServer32",
"PythonPath",
Expand All @@ -350,7 +357,7 @@ def _registry_entries(self, cls):

reg_threading = getattr(cls, "_reg_threading_", None)
if reg_threading is not None:
append(
self._add(
HKCR,
rf"CLSID\{reg_clsid}\InprocServer32",
"ThreadingModel",
Expand All @@ -359,9 +366,9 @@ def _registry_entries(self, cls):

reg_tlib = getattr(cls, "_reg_typelib_", None)
if reg_tlib is not None:
append(HKCR, rf"CLSID\{reg_clsid}\Typelib", "", reg_tlib[0])
self._add(HKCR, rf"CLSID\{reg_clsid}\Typelib", "", reg_tlib[0])

return table
yield from self._table


################################################################
Expand Down
Loading
Loading