comtypes 1.4.9__zip → 1.4.10__zip

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. {comtypes-1.4.9 → comtypes-1.4.10}/CHANGES.txt +19 -0
  2. {comtypes-1.4.9/comtypes.egg-info → comtypes-1.4.10}/PKG-INFO +3 -2
  3. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/GUID.py +34 -13
  4. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/__init__.py +22 -13
  5. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/_comobject.py +69 -14
  6. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/_memberspec.py +14 -6
  7. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/_meta.py +1 -0
  8. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/_post_coinit/_cominterface_meta_patcher.py +0 -1
  9. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/_post_coinit/bstr.py +12 -5
  10. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/_post_coinit/instancemethod.py +1 -2
  11. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/_post_coinit/misc.py +59 -24
  12. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/_post_coinit/unknwn.py +2 -6
  13. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/_safearray.py +2 -2
  14. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/automation.py +10 -4
  15. comtypes-1.4.10/comtypes/client/__init__.py +42 -0
  16. comtypes-1.4.10/comtypes/client/_activeobj.py +46 -0
  17. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/client/_code_cache.py +6 -1
  18. comtypes-1.4.10/comtypes/client/_create.py +147 -0
  19. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/client/_events.py +57 -12
  20. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/client/_generate.py +3 -3
  21. comtypes-1.4.10/comtypes/client/_managing.py +114 -0
  22. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/client/dynamic.py +2 -3
  23. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/client/lazybind.py +10 -11
  24. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/connectionpoints.py +2 -1
  25. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/errorinfo.py +20 -7
  26. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/git.py +4 -3
  27. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/logutil.py +15 -3
  28. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/messageloop.py +1 -1
  29. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/persist.py +3 -5
  30. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/safearray.py +8 -6
  31. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/server/__init__.py +22 -4
  32. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/server/automation.py +1 -2
  33. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/server/connectionpoints.py +5 -5
  34. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/server/inprocserver.py +6 -3
  35. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/server/localserver.py +23 -5
  36. comtypes-1.4.10/comtypes/server/register.py +478 -0
  37. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/shelllink.py +13 -7
  38. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/stream.py +7 -4
  39. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/TestComServer.py +6 -7
  40. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/TestDispServer.py +4 -2
  41. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/__init__.py +1 -1
  42. comtypes-1.4.10/comtypes/test/find_memleak.py +72 -0
  43. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/runtests.py +1 -0
  44. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/setup.py +2 -1
  45. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_BSTR.py +7 -17
  46. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_GUID.py +1 -0
  47. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_QueryService.py +1 -0
  48. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_agilent.py +4 -3
  49. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_basic.py +12 -6
  50. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_clear_cache.py +1 -1
  51. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_client.py +2 -2
  52. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_client_dynamic.py +2 -2
  53. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_client_regenerate_modules.py +2 -2
  54. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_collections.py +1 -1
  55. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_comobject.py +4 -4
  56. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_comserver.py +31 -1
  57. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_dispifc_records.py +1 -1
  58. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_dispifc_safearrays.py +1 -1
  59. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_dyndispatch.py +1 -0
  60. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_findgendir.py +1 -2
  61. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_getactiveobj.py +4 -4
  62. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_ie.py +19 -6
  63. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_ienum.py +2 -2
  64. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_imfattributes.py +13 -3
  65. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_inout_args.py +1 -1
  66. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_midl_safearray_create.py +6 -4
  67. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_monikers.py +23 -9
  68. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_msscript.py +2 -1
  69. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_npsupport.py +4 -4
  70. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_outparam.py +22 -6
  71. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_persist.py +2 -4
  72. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_recordinfo.py +1 -1
  73. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_sapi.py +4 -1
  74. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_server_register.py +113 -125
  75. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_shelllink.py +2 -3
  76. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_storage.py +12 -6
  77. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_stream.py +16 -5
  78. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_subinterface.py +4 -2
  79. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_typeannotator.py +1 -2
  80. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_typeinfo.py +19 -7
  81. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_urlhistory.py +7 -4
  82. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_variant.py +3 -2
  83. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_word.py +1 -1
  84. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/tools/codegenerator/codegenerator.py +167 -435
  85. comtypes-1.4.10/comtypes/tools/codegenerator/comments.py +79 -0
  86. comtypes-1.4.10/comtypes/tools/codegenerator/heads.py +192 -0
  87. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/tools/codegenerator/helpers.py +3 -6
  88. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/tools/codegenerator/namespaces.py +13 -5
  89. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/tools/codegenerator/typeannotator.py +13 -3
  90. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/tools/tlbparser.py +22 -23
  91. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/tools/typedesc.py +3 -2
  92. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/tools/typedesc_base.py +2 -2
  93. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/typeinfo.py +149 -42
  94. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/viewobject.py +2 -4
  95. {comtypes-1.4.9 → comtypes-1.4.10/comtypes.egg-info}/PKG-INFO +3 -2
  96. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes.egg-info/SOURCES.txt +5 -0
  97. {comtypes-1.4.9 → comtypes-1.4.10}/pyproject.toml +11 -19
  98. comtypes-1.4.9/comtypes/client/__init__.py +0 -323
  99. comtypes-1.4.9/comtypes/server/register.py +0 -419
  100. comtypes-1.4.9/comtypes/test/find_memleak.py +0 -76
  101. {comtypes-1.4.9 → comtypes-1.4.10}/LICENSE.txt +0 -0
  102. {comtypes-1.4.9 → comtypes-1.4.10}/MANIFEST.in +0 -0
  103. {comtypes-1.4.9 → comtypes-1.4.10}/README.md +0 -0
  104. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/_npsupport.py +0 -0
  105. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/_post_coinit/__init__.py +0 -0
  106. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/_tlib_version_checker.py +0 -0
  107. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/_vtbl.py +0 -0
  108. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/clear_cache.py +0 -0
  109. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/client/_constants.py +0 -0
  110. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/hints.pyi +0 -0
  111. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/hresult.py +0 -0
  112. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/patcher.py +0 -0
  113. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/server/w_getopt.py +0 -0
  114. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/TestComServer.idl +0 -0
  115. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/TestComServer.tlb +0 -0
  116. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/TestDispServer.idl +0 -0
  117. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/TestDispServer.tlb +0 -0
  118. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/mylib.idl +0 -0
  119. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/mylib.tlb +0 -0
  120. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/mytypelib.idl +0 -0
  121. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_DISPPARAMS.py +0 -0
  122. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_avmc.py +0 -0
  123. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_casesensitivity.py +0 -0
  124. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_createwrappers.py +0 -0
  125. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_dict.py +0 -0
  126. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_dispinterface.py +0 -0
  127. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_excel.py +0 -0
  128. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_jscript.js +0 -0
  129. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_pump_events.py +0 -0
  130. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_safearray.py +0 -0
  131. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_server.py +0 -0
  132. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_showevents.py +0 -0
  133. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_w_getopt.py +0 -0
  134. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_win32com_interop.py +0 -0
  135. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/test_wmi.py +0 -0
  136. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/test/urlhist.tlb +0 -0
  137. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/tools/__init__.py +0 -0
  138. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/tools/codegenerator/__init__.py +0 -0
  139. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/tools/codegenerator/modulenamer.py +0 -0
  140. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/tools/codegenerator/packing.py +0 -0
  141. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes/util.py +0 -0
  142. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes.egg-info/dependency_links.txt +0 -0
  143. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes.egg-info/entry_points.txt +0 -0
  144. {comtypes-1.4.9 → comtypes-1.4.10}/comtypes.egg-info/top_level.txt +0 -0
  145. {comtypes-1.4.9 → comtypes-1.4.10}/setup.cfg +0 -0
  146. {comtypes-1.4.9 → comtypes-1.4.10}/setup.py +0 -0
@@ -1,13 +1,15 @@
1
- import threading
2
1
  import array
2
+ import threading
3
+ from ctypes import POINTER, Structure, byref, c_long, cast, memmove, pointer, sizeof
3
4
  from typing import TYPE_CHECKING
5
+
4
6
  import comtypes
5
- from ctypes import POINTER, Structure, byref, cast, c_long, memmove, pointer, sizeof
6
- from comtypes import _safearray, IUnknown, com_interface_registry
7
+ from comtypes import IUnknown, _safearray, com_interface_registry
7
8
  from comtypes.patcher import Patch
8
9
 
9
10
  if TYPE_CHECKING:
10
11
  from typing import Type, TypeVar
12
+
11
13
  from comtypes import hints # type: ignore
12
14
 
13
15
  _CT = TypeVar("_CT", bound=comtypes._CData)
@@ -73,12 +75,12 @@ def _midlSAFEARRAY(itemtype: "Type[_CT]") -> "Type[hints.LP_SAFEARRAY[_CT]]":
73
75
  def _make_safearray_type(itemtype):
74
76
  # Create and return a subclass of tagSAFEARRAY
75
77
  from comtypes.automation import (
76
- _ctype_to_vartype,
78
+ VT_DISPATCH,
79
+ VT_HRESULT,
77
80
  VT_RECORD,
78
81
  VT_UNKNOWN,
79
82
  IDispatch,
80
- VT_DISPATCH,
81
- VT_HRESULT,
83
+ _ctype_to_vartype,
82
84
  )
83
85
 
84
86
  meta = type(_safearray.tagSAFEARRAY)
@@ -1,5 +1,6 @@
1
1
  import ctypes
2
- from ctypes import HRESULT, POINTER, byref
2
+ from ctypes import HRESULT, POINTER, OleDLL, byref, c_void_p
3
+ from ctypes.wintypes import DWORD, LPVOID
3
4
  from typing import TYPE_CHECKING, Any, Optional, Type
4
5
 
5
6
  import comtypes
@@ -7,6 +8,7 @@ import comtypes.client
7
8
  import comtypes.client.dynamic
8
9
  from comtypes import GUID, STDMETHOD, IUnknown
9
10
  from comtypes.automation import IDispatch
11
+ from comtypes.GUID import REFCLSID
10
12
 
11
13
  if TYPE_CHECKING:
12
14
  from ctypes import _Pointer
@@ -67,7 +69,23 @@ class IClassFactory(IUnknown):
67
69
  ACTIVEOBJECT_STRONG = 0x0
68
70
  ACTIVEOBJECT_WEAK = 0x1
69
71
 
70
- oleaut32 = ctypes.oledll.oleaut32
72
+ _oleaut32 = OleDLL("oleaut32")
73
+
74
+ _RegisterActiveObject = _oleaut32.RegisterActiveObject
75
+ _RegisterActiveObject.argtypes = [
76
+ c_void_p,
77
+ REFCLSID,
78
+ DWORD,
79
+ POINTER(DWORD),
80
+ ]
81
+ _RegisterActiveObject.restype = HRESULT
82
+
83
+ _RevokeActiveObject = _oleaut32.RevokeActiveObject
84
+ _RevokeActiveObject.argtypes = [
85
+ DWORD,
86
+ LPVOID,
87
+ ]
88
+ _RevokeActiveObject.restype = HRESULT
71
89
 
72
90
 
73
91
  def RegisterActiveObject(comobj: comtypes.COMObject, weak: bool = True) -> int:
@@ -78,9 +96,9 @@ def RegisterActiveObject(comobj: comtypes.COMObject, weak: bool = True) -> int:
78
96
  else:
79
97
  flags = ACTIVEOBJECT_STRONG
80
98
  handle = ctypes.c_ulong()
81
- oleaut32.RegisterActiveObject(punk, byref(clsid), flags, byref(handle))
99
+ _RegisterActiveObject(punk, byref(clsid), flags, byref(handle))
82
100
  return handle.value
83
101
 
84
102
 
85
103
  def RevokeActiveObject(handle: int) -> None:
86
- oleaut32.RevokeActiveObject(handle, None)
104
+ _RevokeActiveObject(handle, None)
@@ -1,10 +1,9 @@
1
1
  import logging
2
-
3
2
  from ctypes import *
4
- from comtypes.hresult import *
5
3
 
6
4
  from comtypes import COMObject, IUnknown
7
5
  from comtypes.automation import IDispatch, IEnumVARIANT
6
+ from comtypes.hresult import *
8
7
 
9
8
  logger = logging.getLogger(__name__)
10
9
 
@@ -1,11 +1,11 @@
1
+ import logging
1
2
  from ctypes import *
2
- from comtypes import IUnknown, COMObject, COMError
3
+
4
+ from comtypes import COMError, COMObject, IUnknown
5
+ from comtypes.automation import IDispatch
6
+ from comtypes.connectionpoints import IConnectionPoint
3
7
  from comtypes.hresult import *
4
8
  from comtypes.typeinfo import LoadRegTypeLib
5
- from comtypes.connectionpoints import IConnectionPoint
6
- from comtypes.automation import IDispatch
7
-
8
- import logging
9
9
 
10
10
  logger = logging.getLogger(__name__)
11
11
 
@@ -2,11 +2,14 @@ import ctypes
2
2
  import logging
3
3
  import sys
4
4
  import winreg
5
- from typing import Any, Literal, Optional, Type
5
+ from typing import TYPE_CHECKING, Any, Literal, Optional, Type
6
6
 
7
7
  from comtypes import GUID, COMObject, IUnknown, hresult
8
8
  from comtypes.server import IClassFactory
9
9
 
10
+ if TYPE_CHECKING:
11
+ from ctypes import _Pointer
12
+
10
13
  logger = logging.getLogger(__name__)
11
14
  _debug = logger.debug
12
15
  _critical = logger.critical
@@ -24,8 +27,8 @@ class ClassFactory(COMObject):
24
27
  def IClassFactory_CreateInstance(
25
28
  self,
26
29
  this: Any,
27
- punkOuter: Optional[Type["ctypes._Pointer[IUnknown]"]],
28
- riid: "ctypes._Pointer[GUID]",
30
+ punkOuter: Optional[Type["_Pointer[IUnknown]"]],
31
+ riid: "_Pointer[GUID]",
29
32
  ppv: ctypes.c_void_p,
30
33
  ) -> int:
31
34
  _debug("ClassFactory.CreateInstance(%s)", riid[0])
@@ -1,11 +1,18 @@
1
1
  import logging
2
2
  import queue
3
- import sys
4
- from ctypes import byref, c_ulong, c_void_p, oledll
3
+ from ctypes import (
4
+ HRESULT,
5
+ POINTER,
6
+ OleDLL,
7
+ byref,
8
+ c_ulong,
9
+ c_void_p,
10
+ )
11
+ from ctypes.wintypes import DWORD, LPDWORD
5
12
  from typing import TYPE_CHECKING, Any, Literal, Optional, Sequence, Type
6
13
 
7
14
  import comtypes
8
- from comtypes import hresult
15
+ from comtypes import GUID, hresult
9
16
  from comtypes.server import IClassFactory
10
17
 
11
18
  if TYPE_CHECKING:
@@ -21,6 +28,17 @@ REGCLS_MULTI_SEPARATE = 2 # multiple use, but separate control over each
21
28
  REGCLS_SUSPENDED = 4 # register it as suspended, will be activated
22
29
  REGCLS_SURROGATE = 8 # must be used when a surrogate process
23
30
 
31
+ _ole32 = OleDLL("ole32")
32
+
33
+ REFCLSID = POINTER(GUID)
34
+ _CoRegisterClassObject = _ole32.CoRegisterClassObject
35
+ _CoRegisterClassObject.argtypes = [REFCLSID, c_void_p, DWORD, DWORD, LPDWORD]
36
+ _CoRegisterClassObject.restype = HRESULT
37
+
38
+ _CoRevokeClassObject = _ole32.CoRevokeClassObject
39
+ _CoRevokeClassObject.argtypes = [DWORD]
40
+ _CoRevokeClassObject.restype = HRESULT
41
+
24
42
 
25
43
  def run(classes: Sequence[Type[comtypes.COMObject]]) -> None:
26
44
  classobjects = [ClassFactory(cls) for cls in classes]
@@ -52,7 +70,7 @@ class ClassFactory(comtypes.COMObject):
52
70
  ptr = self._com_pointers_[comtypes.IUnknown._iid_]
53
71
  clsctx = self._cls._reg_clsctx_
54
72
  clsctx &= ~comtypes.CLSCTX_INPROC # reset the inproc flags
55
- oledll.ole32.CoRegisterClassObject(
73
+ _CoRegisterClassObject(
56
74
  byref(comtypes.GUID(self._cls._reg_clsid_)),
57
75
  ptr,
58
76
  clsctx,
@@ -62,7 +80,7 @@ class ClassFactory(comtypes.COMObject):
62
80
  self.cookie = cookie
63
81
 
64
82
  def _revoke_class(self) -> None:
65
- oledll.ole32.CoRevokeClassObject(self.cookie)
83
+ _CoRevokeClassObject(self.cookie)
66
84
 
67
85
  def CreateInstance(
68
86
  self,
@@ -0,0 +1,478 @@
1
+ """comtypes.server.register - register and unregister a COM object.
2
+
3
+ Exports the UseCommandLine function. UseCommandLine is called with
4
+ the COM object classes that a module exposes. It parses the Windows
5
+ command line and takes the appropriate actions.
6
+ These command line options are supported:
7
+
8
+ /regserver - register the classes with COM.
9
+ /unregserver - unregister the classes with COM.
10
+
11
+ /nodebug - remove all logging configuration from the registry.
12
+
13
+ /l <name>=<level> - configure the logging level for the standard Python loggind module,
14
+ this option may be used several times.
15
+
16
+ /f <formatter> - specify the formatter string.
17
+
18
+ Note: Registering and unregistering the objects does remove logging
19
+ entries. Configuring the logging does not change other registry
20
+ entries, so it is possible to freeze a comobject with py2exe, register
21
+ it, then configure logging afterwards to debug it, and delete the
22
+ logging config afterwards.
23
+
24
+ Sample usage:
25
+
26
+ Register the COM object:
27
+
28
+ python mycomobj.py /regserver
29
+
30
+ Configure logging info:
31
+
32
+ python mycomobj.py /l comtypes=INFO /l comtypes.server=DEBUG /f %(message)s
33
+
34
+ Now, debug the object, and when done delete logging info:
35
+
36
+ python mycomobj.py /nodebug
37
+ """
38
+
39
+ import _ctypes
40
+ import abc
41
+ import logging
42
+ import os
43
+ import sys
44
+ import winreg
45
+ from ctypes import WinDLL, WinError
46
+ from ctypes.wintypes import HKEY, LONG, LPCWSTR
47
+ from typing import Iterable, Iterator, List, Optional, Tuple, Type, Union
48
+ from winreg import HKEY_CLASSES_ROOT as HKCR
49
+ from winreg import HKEY_CURRENT_USER as HKCU
50
+ from winreg import HKEY_LOCAL_MACHINE as HKLM
51
+
52
+ import comtypes.server.inprocserver # noqa
53
+ from comtypes import CLSCTX_INPROC_SERVER, CLSCTX_LOCAL_SERVER
54
+ from comtypes.hresult import TYPE_E_CANTLOADLIBRARY, TYPE_E_REGISTRYACCESS
55
+ from comtypes.server.localserver import run as run_localserver
56
+ from comtypes.server.w_getopt import w_getopt
57
+ from comtypes.typeinfo import (
58
+ REGKIND_REGISTER,
59
+ GetModuleFileName,
60
+ LoadTypeLibEx,
61
+ UnRegisterTypeLib,
62
+ )
63
+
64
+ _debug = logging.getLogger(__name__).debug
65
+
66
+
67
+ def get_winerror(exception: OSError) -> Optional[int]:
68
+ try:
69
+ return exception.winerror
70
+ except AttributeError:
71
+ return exception.errno
72
+
73
+
74
+ # a SHDeleteKey function, will remove a registry key with all subkeys.
75
+ def _non_zero(retval, func, args):
76
+ if retval:
77
+ raise WinError(retval)
78
+ return retval
79
+
80
+
81
+ _shlwapi = WinDLL("shlwapi")
82
+ LSTATUS = LONG
83
+ SHDeleteKey = _shlwapi.SHDeleteKeyW
84
+ SHDeleteKey.errcheck = _non_zero
85
+ SHDeleteKey.argtypes = HKEY, LPCWSTR
86
+ SHDeleteKey.restype = LSTATUS
87
+
88
+
89
+ _KEYS = {
90
+ HKCR: "HKCR",
91
+ HKLM: "HKLM",
92
+ HKCU: "HKCU",
93
+ }
94
+
95
+
96
+ def _explain(hkey: int) -> Union[str, int]:
97
+ return _KEYS.get(hkey, hkey)
98
+
99
+
100
+ def _delete_key(hkey: int, subkey: str, *, force: bool) -> None:
101
+ try:
102
+ if force:
103
+ _debug("SHDeleteKey %s\\%s", _explain(hkey), subkey)
104
+ SHDeleteKey(hkey, subkey)
105
+ else:
106
+ _debug("DeleteKey %s\\%s", _explain(hkey), subkey)
107
+ winreg.DeleteKey(hkey, subkey)
108
+ except WindowsError as detail:
109
+ if get_winerror(detail) != 2:
110
+ raise
111
+
112
+
113
+ _Entry = Tuple[int, str, str, str]
114
+
115
+
116
+ class Registrar(object):
117
+ """COM class registration.
118
+
119
+ The COM class can override what this does by implementing
120
+ _register and/or _unregister class methods. These methods will be
121
+ called with the calling instance of Registrar, and so can call the
122
+ Registrars _register and _unregister methods which do the actual
123
+ work.
124
+ """
125
+
126
+ _frozen: Union[None, int, str]
127
+ _frozendllhandle: Optional[int]
128
+
129
+ def __init__(self) -> None:
130
+ self._frozen = getattr(sys, "frozen", None)
131
+ self._frozendllhandle = getattr(sys, "frozendllhandle", None)
132
+
133
+ def _generate_reg_entries(self, cls: Type) -> Iterable[_Entry]:
134
+ if self._frozen is None:
135
+ return InterpRegistryEntries(cls)
136
+ return FrozenRegistryEntries(cls, self._frozen, self._frozendllhandle)
137
+
138
+ def nodebug(self, cls: Type) -> None:
139
+ """Delete logging entries from the registry."""
140
+ clsid = cls._reg_clsid_
141
+ try:
142
+ _debug('DeleteKey( %s\\CLSID\\%s\\Logging"' % (_explain(HKCR), clsid))
143
+ hkey = winreg.OpenKey(HKCR, rf"CLSID\{clsid}")
144
+ winreg.DeleteKey(hkey, "Logging")
145
+ except WindowsError as detail:
146
+ if get_winerror(detail) != 2:
147
+ raise
148
+
149
+ def debug(self, cls: Type, levels: List[str], format: Optional[str]) -> None:
150
+ """Write entries in the registry to setup logging for this clsid."""
151
+ # handlers
152
+ # format
153
+ clsid = cls._reg_clsid_
154
+ _debug('CreateKey( %s\\CLSID\\%s\\Logging"' % (_explain(HKCR), clsid))
155
+ hkey = winreg.CreateKey(HKCR, rf"CLSID\{clsid}\Logging")
156
+ for item in levels:
157
+ name, value = item.split("=")
158
+ v = getattr(logging, value)
159
+ assert isinstance(v, int)
160
+ _debug("SetValueEx(levels, %s)" % levels)
161
+ winreg.SetValueEx(hkey, "levels", None, winreg.REG_MULTI_SZ, levels)
162
+ if format:
163
+ _debug("SetValueEx(format, %s)" % format)
164
+ winreg.SetValueEx(hkey, "format", None, winreg.REG_SZ, format)
165
+ else:
166
+ _debug("DeleteValue(format)")
167
+ try:
168
+ winreg.DeleteValue(hkey, "format")
169
+ except WindowsError as detail:
170
+ if get_winerror(detail) != 2:
171
+ raise
172
+
173
+ def register(self, cls: Type, executable: Optional[str] = None) -> None:
174
+ """Register the COM server class."""
175
+ # First, we unregister the object with force=True, to force removal
176
+ # of all registry entries, even if we would not write them.
177
+ # Second, we create new entries.
178
+ # It seems ATL does the same.
179
+ mth = getattr(cls, "_register", None)
180
+ if mth is not None:
181
+ mth(self)
182
+ else:
183
+ self._unregister(cls, force=True)
184
+ self._register(cls, executable)
185
+
186
+ def _register(self, cls: Type, executable: Optional[str] = None) -> None:
187
+ table = sorted(self._generate_reg_entries(cls))
188
+ _debug("Registering %s", cls)
189
+ for hkey, subkey, valuename, value in table:
190
+ _debug("[%s\\%s]", _explain(hkey), subkey)
191
+ _debug('%s="%s"', valuename or "@", value)
192
+ k = winreg.CreateKey(hkey, subkey)
193
+ winreg.SetValueEx(k, valuename, None, winreg.REG_SZ, str(value))
194
+
195
+ tlib = getattr(cls, "_reg_typelib_", None)
196
+ if tlib is not None:
197
+ if self._frozendllhandle is not None:
198
+ frozen_dll = _get_serverdll(self._frozendllhandle)
199
+ _debug("LoadTypeLibEx(%s, REGKIND_REGISTER)", frozen_dll)
200
+ LoadTypeLibEx(frozen_dll, REGKIND_REGISTER)
201
+ else:
202
+ if executable:
203
+ path = executable
204
+ elif self._frozen is not None:
205
+ path = sys.executable
206
+ else:
207
+ path = cls._typelib_path_
208
+ _debug("LoadTypeLibEx(%s, REGKIND_REGISTER)", path)
209
+ LoadTypeLibEx(path, REGKIND_REGISTER)
210
+ _debug("Done")
211
+
212
+ def unregister(self, cls: Type, force: bool = False) -> None:
213
+ """Unregister the COM server class."""
214
+ mth = getattr(cls, "_unregister", None)
215
+ if mth is not None:
216
+ mth(self)
217
+ else:
218
+ self._unregister(cls, force=force)
219
+
220
+ def _unregister(self, cls: Type, force: bool = False) -> None:
221
+ # If force==False, we only remove those entries that we
222
+ # actually would have written. It seems ATL does the same.
223
+ table = [t[:2] for t in self._generate_reg_entries(cls)]
224
+ # reversed and only unique entries
225
+ table = reversed(sorted(list(set(table))))
226
+ _debug("Unregister %s", cls)
227
+ for hkey, subkey in table:
228
+ _delete_key(hkey, subkey, force=force)
229
+ tlib = getattr(cls, "_reg_typelib_", None)
230
+ if tlib is not None:
231
+ try:
232
+ _debug("UnRegisterTypeLib(%s, %s, %s)", *tlib)
233
+ UnRegisterTypeLib(*tlib)
234
+ except WindowsError as detail:
235
+ if not get_winerror(detail) in (
236
+ TYPE_E_REGISTRYACCESS,
237
+ TYPE_E_CANTLOADLIBRARY,
238
+ ):
239
+ raise
240
+ _debug("Done")
241
+
242
+
243
+ def _get_serverdll(handle: int) -> str:
244
+ """Return the pathname of the dll hosting the COM object."""
245
+ return GetModuleFileName(handle, 260)
246
+
247
+
248
+ class RegistryEntries(abc.ABC):
249
+ """Iterator of tuples containing registry entries.
250
+
251
+ The tuples must be (key, subkey, name, value).
252
+
253
+ Required entries:
254
+ =================
255
+ _reg_clsid_ - a string or GUID instance
256
+ _reg_clsctx_ - server type(s) to register
257
+
258
+ Optional entries:
259
+ =================
260
+ _reg_desc_ - a string
261
+ _reg_progid_ - a string naming the progid, typically 'MyServer.MyObject.1'
262
+ _reg_novers_progid_ - version independend progid, typically 'MyServer.MyObject'
263
+ _reg_typelib_ - an tuple (libid, majorversion, minorversion) specifying a typelib.
264
+ _reg_threading_ - a string specifying the threading model
265
+
266
+ Note that the first part of the progid string is typically the
267
+ IDL library name of the type library containing the coclass.
268
+ """
269
+
270
+ @abc.abstractmethod
271
+ def __iter__(self) -> Iterator[_Entry]: ...
272
+
273
+
274
+ class FrozenRegistryEntries(RegistryEntries):
275
+ def __init__(
276
+ self,
277
+ cls: Type,
278
+ frozen: Union[str, int],
279
+ frozendllhandle: Optional[int] = None,
280
+ ) -> None:
281
+ self._cls = cls
282
+ self._frozen = frozen
283
+ self._frozendllhandle = frozendllhandle
284
+
285
+ def __iter__(self) -> Iterator[_Entry]:
286
+ # that's the only required attribute for registration
287
+ reg_clsid = str(self._cls._reg_clsid_)
288
+ yield from _iter_reg_entries(self._cls, reg_clsid)
289
+ clsctx: int = getattr(self._cls, "_reg_clsctx_", 0)
290
+ localsvr_ctx = bool(clsctx & CLSCTX_LOCAL_SERVER)
291
+ inprocsvr_ctx = bool(clsctx & CLSCTX_INPROC_SERVER)
292
+ if localsvr_ctx and self._frozendllhandle is None:
293
+ yield from _iter_frozen_local_ctx_entries(self._cls, reg_clsid)
294
+ if inprocsvr_ctx and self._frozen == "dll":
295
+ assert self._frozendllhandle is not None
296
+ frozen_dll = _get_serverdll(self._frozendllhandle)
297
+ yield from _iter_inproc_ctx_entries(reg_clsid, frozen_dll)
298
+ yield from _iter_inproc_threading_model_entries(self._cls, reg_clsid)
299
+ yield from _iter_tlib_entries(self._cls, reg_clsid)
300
+
301
+
302
+ class InterpRegistryEntries(RegistryEntries):
303
+ def __init__(self, cls: Type) -> None:
304
+ self._cls = cls
305
+
306
+ def __iter__(self) -> Iterator[_Entry]:
307
+ # that's the only required attribute for registration
308
+ reg_clsid = str(self._cls._reg_clsid_)
309
+ yield from _iter_reg_entries(self._cls, reg_clsid)
310
+ clsctx: int = getattr(self._cls, "_reg_clsctx_", 0)
311
+ localsvr_ctx = bool(clsctx & CLSCTX_LOCAL_SERVER)
312
+ inprocsvr_ctx = bool(clsctx & CLSCTX_INPROC_SERVER)
313
+ if localsvr_ctx:
314
+ yield from _iter_interp_local_ctx_entries(self._cls, reg_clsid)
315
+ if inprocsvr_ctx:
316
+ yield from _iter_inproc_ctx_entries(reg_clsid, _ctypes.__file__)
317
+ # only for non-frozen inproc servers the PythonPath/PythonClass is needed.
318
+ yield from _iter_inproc_python_entries(self._cls, reg_clsid)
319
+ yield from _iter_inproc_threading_model_entries(self._cls, reg_clsid)
320
+ yield from _iter_tlib_entries(self._cls, reg_clsid)
321
+
322
+
323
+ def _get_full_classname(cls: Type) -> str:
324
+ """Return <modulename>.<classname> for 'cls'."""
325
+ modname = cls.__module__
326
+ if modname == "__main__":
327
+ modname = os.path.splitext(os.path.basename(sys.argv[0]))[0]
328
+ return f"{modname}.{cls.__name__}"
329
+
330
+
331
+ def _get_pythonpath(cls: Type) -> str:
332
+ """Return the filesystem path of the module containing 'cls'."""
333
+ modname = cls.__module__
334
+ dirname = os.path.dirname(sys.modules[modname].__file__) # type: ignore
335
+ return os.path.abspath(dirname)
336
+
337
+
338
+ def _iter_reg_entries(cls: Type, reg_clsid: str) -> Iterator[_Entry]:
339
+ # basic entry - names the comobject
340
+ reg_desc = getattr(cls, "_reg_desc_", "")
341
+ if not reg_desc:
342
+ # Simple minded algorithm to construct a description from the progid:
343
+ reg_desc = getattr(cls, "_reg_novers_progid_", getattr(cls, "_reg_progid_", ""))
344
+ if reg_desc:
345
+ reg_desc = reg_desc.replace(".", " ")
346
+ yield (HKCR, rf"CLSID\{reg_clsid}", "", reg_desc)
347
+
348
+ reg_progid = getattr(cls, "_reg_progid_", None)
349
+ if reg_progid:
350
+ # for ProgIDFromCLSID:
351
+ yield (HKCR, rf"CLSID\{reg_clsid}\ProgID", "", reg_progid) # 1
352
+
353
+ # for CLSIDFromProgID
354
+ if reg_desc:
355
+ yield (HKCR, reg_progid, "", reg_desc) # 2
356
+ yield (HKCR, rf"{reg_progid}\CLSID", "", reg_clsid) # 3
357
+
358
+ reg_novers_progid = getattr(cls, "_reg_novers_progid_", None)
359
+ if reg_novers_progid:
360
+ yield (
361
+ HKCR,
362
+ rf"CLSID\{reg_clsid}\VersionIndependentProgID", # 1a
363
+ "",
364
+ reg_novers_progid,
365
+ )
366
+ if reg_desc:
367
+ yield (HKCR, reg_novers_progid, "", reg_desc) # 2a
368
+ yield (HKCR, rf"{reg_novers_progid}\CurVer", "", reg_progid) #
369
+ yield (HKCR, rf"{reg_novers_progid}\CLSID", "", reg_clsid) # 3a
370
+
371
+
372
+ def _iter_interp_local_ctx_entries(cls: Type, reg_clsid: str) -> Iterator[_Entry]:
373
+ exe = sys.executable
374
+ exe = f'"{exe}"' if " " in exe else exe
375
+ exe = f"{exe} -O" if not __debug__ else exe
376
+ script = os.path.abspath(sys.modules[cls.__module__].__file__) # type: ignore
377
+ script = f'"{script}"' if " " in script else script
378
+ yield (HKCR, rf"CLSID\{reg_clsid}\LocalServer32", "", f"{exe} {script}")
379
+
380
+
381
+ def _iter_frozen_local_ctx_entries(cls: Type, reg_clsid: str) -> Iterator[_Entry]:
382
+ exe = sys.executable
383
+ exe = f'"{exe}"' if " " in exe else exe
384
+ yield (HKCR, rf"CLSID\{reg_clsid}\LocalServer32", "", f"{exe}")
385
+
386
+
387
+ def _iter_inproc_ctx_entries(reg_clsid: str, dllfile: str) -> Iterator[_Entry]:
388
+ # Register InprocServer32 only when run from script or from
389
+ # py2exe dll server, not from py2exe exe server.
390
+ yield (HKCR, rf"CLSID\{reg_clsid}\InprocServer32", "", dllfile)
391
+
392
+
393
+ def _iter_inproc_python_entries(cls: Type, reg_clsid: str) -> Iterator[_Entry]:
394
+ # only for non-frozen inproc servers the PythonPath/PythonClass is needed.
395
+ yield (
396
+ HKCR,
397
+ rf"CLSID\{reg_clsid}\InprocServer32",
398
+ "PythonClass",
399
+ _get_full_classname(cls),
400
+ )
401
+ yield (
402
+ HKCR,
403
+ rf"CLSID\{reg_clsid}\InprocServer32",
404
+ "PythonPath",
405
+ _get_pythonpath(cls),
406
+ )
407
+
408
+
409
+ def _iter_inproc_threading_model_entries(cls: Type, reg_clsid: str) -> Iterator[_Entry]:
410
+ reg_threading = getattr(cls, "_reg_threading_", None)
411
+ if reg_threading is not None:
412
+ yield (
413
+ HKCR,
414
+ rf"CLSID\{reg_clsid}\InprocServer32",
415
+ "ThreadingModel",
416
+ reg_threading,
417
+ )
418
+
419
+
420
+ def _iter_tlib_entries(cls: Type, reg_clsid: str) -> Iterator[_Entry]:
421
+ reg_tlib = getattr(cls, "_reg_typelib_", None)
422
+ if reg_tlib is not None:
423
+ yield (HKCR, rf"CLSID\{reg_clsid}\Typelib", "", reg_tlib[0])
424
+
425
+
426
+ ################################################################
427
+
428
+
429
+ def register(cls: Type) -> None:
430
+ Registrar().register(cls)
431
+
432
+
433
+ def unregister(cls: Type) -> None:
434
+ Registrar().unregister(cls)
435
+
436
+
437
+ def UseCommandLine(*classes: Type) -> int:
438
+ usage = f"""Usage: {sys.argv[0]} [-regserver] [-unregserver] [-nodebug] [-f logformat] [-l loggername=level]"""
439
+ opts, args = w_getopt(sys.argv[1:], "regserver unregserver embedding l: f: nodebug")
440
+ if not opts:
441
+ sys.stderr.write(usage + "\n")
442
+ return 0 # nothing for us to do
443
+
444
+ levels = []
445
+ format = None
446
+ nodebug = False
447
+ runit = False
448
+ for option, value in opts:
449
+ if option == "regserver":
450
+ for cls in classes:
451
+ register(cls)
452
+ elif option == "unregserver":
453
+ for cls in classes:
454
+ unregister(cls)
455
+ elif option == "embedding":
456
+ runit = True
457
+ elif option == "f":
458
+ format = value
459
+ elif option == "l":
460
+ levels.append(value)
461
+ elif option == "nodebug":
462
+ nodebug = True
463
+
464
+ if levels or format is not None:
465
+ for cls in classes:
466
+ Registrar().debug(cls, levels, format)
467
+ if nodebug:
468
+ for cls in classes:
469
+ Registrar().nodebug(cls)
470
+
471
+ if runit:
472
+ run_localserver(classes)
473
+
474
+ return 1 # we have done something
475
+
476
+
477
+ if __name__ == "__main__":
478
+ UseCommandLine()