comtypes 1.4.9__zip → 1.4.11__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.
- {comtypes-1.4.9 → comtypes-1.4.11}/CHANGES.txt +41 -0
- {comtypes-1.4.9/comtypes.egg-info → comtypes-1.4.11}/PKG-INFO +4 -2
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/GUID.py +34 -13
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/__init__.py +25 -16
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/_comobject.py +92 -39
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/_memberspec.py +112 -78
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/_meta.py +2 -1
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/_post_coinit/_cominterface_meta_patcher.py +3 -6
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/_post_coinit/bstr.py +12 -5
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/_post_coinit/instancemethod.py +1 -2
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/_post_coinit/misc.py +59 -24
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/_post_coinit/unknwn.py +30 -25
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/_safearray.py +2 -2
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/_vtbl.py +52 -55
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/automation.py +19 -14
- comtypes-1.4.11/comtypes/client/__init__.py +42 -0
- comtypes-1.4.11/comtypes/client/_activeobj.py +46 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/client/_code_cache.py +15 -24
- comtypes-1.4.11/comtypes/client/_create.py +147 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/client/_events.py +140 -57
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/client/_generate.py +3 -5
- comtypes-1.4.11/comtypes/client/_managing.py +114 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/client/dynamic.py +2 -3
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/client/lazybind.py +10 -11
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/connectionpoints.py +38 -3
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/errorinfo.py +72 -21
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/git.py +4 -3
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/hints.pyi +15 -2
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/hresult.py +16 -6
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/logutil.py +15 -3
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/messageloop.py +15 -13
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/persist.py +3 -5
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/safearray.py +8 -6
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/server/__init__.py +22 -4
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/server/automation.py +1 -2
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/server/connectionpoints.py +71 -70
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/server/inprocserver.py +14 -8
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/server/localserver.py +28 -16
- comtypes-1.4.11/comtypes/server/register.py +478 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/shelllink.py +13 -7
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/stream.py +7 -4
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/TestComServer.py +6 -7
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/TestDispServer.py +4 -2
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/__init__.py +1 -1
- comtypes-1.4.11/comtypes/test/find_memleak.py +72 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/runtests.py +1 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/setup.py +2 -1
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_BSTR.py +7 -17
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_GUID.py +7 -7
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_QueryService.py +1 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_agilent.py +4 -3
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_basic.py +18 -9
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_clear_cache.py +1 -1
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_client.py +2 -2
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_client_dynamic.py +2 -2
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_client_regenerate_modules.py +2 -2
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_collections.py +7 -4
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_comobject.py +22 -9
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_comserver.py +56 -3
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_dict.py +2 -1
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_dispifc_records.py +1 -1
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_dispifc_safearrays.py +1 -1
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_dyndispatch.py +5 -2
- comtypes-1.4.11/comtypes/test/test_errorinfo.py +115 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_findgendir.py +1 -2
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_getactiveobj.py +4 -4
- comtypes-1.4.11/comtypes/test/test_hresult.py +48 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_ie.py +19 -6
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_ienum.py +2 -2
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_imfattributes.py +13 -3
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_inout_args.py +10 -3
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_midl_safearray_create.py +6 -4
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_monikers.py +23 -9
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_msscript.py +2 -1
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_npsupport.py +4 -4
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_outparam.py +22 -6
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_persist.py +2 -4
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_recordinfo.py +1 -1
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_safearray.py +2 -1
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_sapi.py +4 -1
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_server_register.py +114 -125
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_shelllink.py +2 -3
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_storage.py +12 -6
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_stream.py +16 -5
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_subinterface.py +4 -2
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_typeannotator.py +1 -2
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_typeinfo.py +36 -7
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_urlhistory.py +7 -4
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_variant.py +5 -3
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_w_getopt.py +4 -4
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_word.py +1 -1
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/tools/codegenerator/codegenerator.py +167 -435
- comtypes-1.4.11/comtypes/tools/codegenerator/comments.py +79 -0
- comtypes-1.4.11/comtypes/tools/codegenerator/heads.py +192 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/tools/codegenerator/helpers.py +3 -6
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/tools/codegenerator/namespaces.py +13 -5
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/tools/codegenerator/typeannotator.py +13 -3
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/tools/tlbparser.py +25 -25
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/tools/typedesc.py +3 -2
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/tools/typedesc_base.py +2 -2
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/typeinfo.py +163 -51
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/viewobject.py +2 -4
- {comtypes-1.4.9 → comtypes-1.4.11/comtypes.egg-info}/PKG-INFO +4 -2
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes.egg-info/SOURCES.txt +7 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/pyproject.toml +10 -23
- comtypes-1.4.9/comtypes/client/__init__.py +0 -323
- comtypes-1.4.9/comtypes/server/register.py +0 -419
- comtypes-1.4.9/comtypes/test/find_memleak.py +0 -76
- {comtypes-1.4.9 → comtypes-1.4.11}/LICENSE.txt +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/MANIFEST.in +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/README.md +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/_npsupport.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/_post_coinit/__init__.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/_tlib_version_checker.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/clear_cache.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/client/_constants.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/patcher.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/server/w_getopt.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/TestComServer.idl +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/TestComServer.tlb +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/TestDispServer.idl +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/TestDispServer.tlb +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/mylib.idl +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/mylib.tlb +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/mytypelib.idl +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_DISPPARAMS.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_avmc.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_casesensitivity.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_createwrappers.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_dispinterface.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_excel.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_jscript.js +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_pump_events.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_server.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_showevents.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_win32com_interop.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/test_wmi.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/test/urlhist.tlb +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/tools/__init__.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/tools/codegenerator/__init__.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/tools/codegenerator/modulenamer.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/tools/codegenerator/packing.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes/util.py +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes.egg-info/dependency_links.txt +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes.egg-info/entry_points.txt +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/comtypes.egg-info/top_level.txt +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/setup.cfg +0 -0
- {comtypes-1.4.9 → comtypes-1.4.11}/setup.py +0 -0
|
@@ -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, MAX_PATH
|
|
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, MAX_PATH)
|
|
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()
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
from ctypes import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
from ctypes import (
|
|
2
|
+
POINTER,
|
|
3
|
+
byref,
|
|
4
|
+
c_char_p,
|
|
5
|
+
c_int,
|
|
6
|
+
c_short,
|
|
7
|
+
c_wchar_p,
|
|
8
|
+
create_string_buffer,
|
|
9
|
+
create_unicode_buffer,
|
|
10
|
+
)
|
|
11
|
+
from ctypes.wintypes import DWORD, MAX_PATH, WIN32_FIND_DATAA, WIN32_FIND_DATAW
|
|
12
|
+
from typing import TYPE_CHECKING, Literal, Tuple
|
|
8
13
|
|
|
14
|
+
from comtypes import COMMETHOD, GUID, HRESULT, CoClass, IUnknown
|
|
9
15
|
|
|
10
16
|
if TYPE_CHECKING:
|
|
11
17
|
from comtypes import hints # type: ignore
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
from ctypes import
|
|
2
|
-
from typing import
|
|
1
|
+
from ctypes import HRESULT, POINTER, c_ubyte, c_ulong, pointer
|
|
2
|
+
from typing import TYPE_CHECKING, Tuple
|
|
3
3
|
|
|
4
4
|
from comtypes import COMMETHOD, GUID, IUnknown
|
|
5
5
|
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from ctypes import Array as _CArrayType
|
|
8
|
+
|
|
6
9
|
|
|
7
10
|
class ISequentialStream(IUnknown):
|
|
8
11
|
"""Defines methods for the stream objects in sequence."""
|
|
@@ -38,7 +41,7 @@ class ISequentialStream(IUnknown):
|
|
|
38
41
|
),
|
|
39
42
|
]
|
|
40
43
|
|
|
41
|
-
def RemoteRead(self, cb: int) -> Tuple["
|
|
44
|
+
def RemoteRead(self, cb: int) -> Tuple["_CArrayType[c_ubyte]", int]:
|
|
42
45
|
"""Reads a specified number of bytes from the stream object into memory
|
|
43
46
|
starting at the current seek pointer.
|
|
44
47
|
"""
|
|
@@ -51,7 +54,7 @@ class ISequentialStream(IUnknown):
|
|
|
51
54
|
|
|
52
55
|
if TYPE_CHECKING:
|
|
53
56
|
|
|
54
|
-
def RemoteWrite(self, pv: "
|
|
57
|
+
def RemoteWrite(self, pv: "_CArrayType[c_ubyte]", cb: int) -> int:
|
|
55
58
|
"""Writes a specified number of bytes into the stream object starting at
|
|
56
59
|
the current seek pointer.
|
|
57
60
|
"""
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import sys, os
|
|
2
1
|
import logging
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
3
4
|
|
|
4
5
|
logging.basicConfig()
|
|
5
6
|
##logging.basicConfig(level=logging.DEBUG)
|
|
@@ -7,14 +8,14 @@ logging.basicConfig()
|
|
|
7
8
|
|
|
8
9
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), r"..\..")))
|
|
9
10
|
|
|
10
|
-
import ctypes
|
|
11
11
|
import comtypes
|
|
12
|
-
from comtypes.hresult import *
|
|
13
12
|
import comtypes.client
|
|
13
|
+
import comtypes.connectionpoints
|
|
14
14
|
import comtypes.errorinfo
|
|
15
15
|
import comtypes.server
|
|
16
16
|
import comtypes.server.connectionpoints
|
|
17
17
|
import comtypes.typeinfo
|
|
18
|
+
from comtypes.hresult import S_OK
|
|
18
19
|
|
|
19
20
|
################################################################
|
|
20
21
|
|
|
@@ -30,6 +31,7 @@ if not hasattr(sys, "frozen"):
|
|
|
30
31
|
|
|
31
32
|
# Import the wrapper
|
|
32
33
|
from comtypes.gen import TestComServerLib
|
|
34
|
+
from comtypes.gen.TestComServerLib import ITestComServer # noqa
|
|
33
35
|
|
|
34
36
|
################################################################
|
|
35
37
|
|
|
@@ -151,7 +153,4 @@ if __name__ == "__main__":
|
|
|
151
153
|
import traceback
|
|
152
154
|
|
|
153
155
|
traceback.print_exc()
|
|
154
|
-
|
|
155
|
-
input()
|
|
156
|
-
else:
|
|
157
|
-
raw_input()
|
|
156
|
+
input()
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import sys, os
|
|
2
1
|
import logging
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
3
4
|
|
|
4
5
|
logging.basicConfig()
|
|
5
6
|
##logging.basicConfig(level=logging.DEBUG)
|
|
@@ -9,8 +10,9 @@ logging.basicConfig()
|
|
|
9
10
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), r"..\..")))
|
|
10
11
|
|
|
11
12
|
import comtypes
|
|
12
|
-
|
|
13
|
+
import comtypes.connectionpoints
|
|
13
14
|
import comtypes.server.connectionpoints
|
|
15
|
+
from comtypes.hresult import S_OK
|
|
14
16
|
|
|
15
17
|
################################################################
|
|
16
18
|
|
|
@@ -119,8 +119,8 @@ def usage():
|
|
|
119
119
|
|
|
120
120
|
def test_with_refcounts(runner, verbosity, testcase):
|
|
121
121
|
"""Run testcase several times, tracking reference counts."""
|
|
122
|
-
import gc
|
|
123
122
|
import ctypes
|
|
123
|
+
import gc
|
|
124
124
|
|
|
125
125
|
ptc = ctypes._pointer_type_cache.copy()
|
|
126
126
|
cfc = ctypes._c_functype_cache.copy()
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import gc
|
|
2
|
+
from ctypes import POINTER, Structure, WinDLL, WinError, byref, c_size_t, sizeof
|
|
3
|
+
from ctypes.wintypes import BOOL, DWORD, HANDLE
|
|
4
|
+
|
|
5
|
+
################################################################
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PROCESS_MEMORY_COUNTERS(Structure):
|
|
9
|
+
_fields_ = [
|
|
10
|
+
("cb", DWORD),
|
|
11
|
+
("PageFaultCount", DWORD),
|
|
12
|
+
("PeakWorkingSetSize", c_size_t),
|
|
13
|
+
("WorkingSetSize", c_size_t),
|
|
14
|
+
("QuotaPeakPagedPoolUsage", c_size_t),
|
|
15
|
+
("QuotaPagedPoolUsage", c_size_t),
|
|
16
|
+
("QuotaPeakNonPagedPoolUsage", c_size_t),
|
|
17
|
+
("QuotaNonPagedPoolUsage", c_size_t),
|
|
18
|
+
("PagefileUsage", c_size_t),
|
|
19
|
+
("PeakPagefileUsage", c_size_t),
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
def __init__(self):
|
|
23
|
+
self.cb = sizeof(self)
|
|
24
|
+
|
|
25
|
+
def dump(self):
|
|
26
|
+
for n, _ in self._fields_[2:]:
|
|
27
|
+
print(n, getattr(self, n) / 1e6)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
_psapi = WinDLL("psapi")
|
|
31
|
+
|
|
32
|
+
_GetProcessMemoryInfo = _psapi.GetProcessMemoryInfo
|
|
33
|
+
_GetProcessMemoryInfo.argtypes = [HANDLE, POINTER(PROCESS_MEMORY_COUNTERS), DWORD]
|
|
34
|
+
_GetProcessMemoryInfo.restype = BOOL
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def wss():
|
|
38
|
+
# Return the working set size (memory used by process)
|
|
39
|
+
pmi = PROCESS_MEMORY_COUNTERS()
|
|
40
|
+
if not _GetProcessMemoryInfo(-1, byref(pmi), sizeof(pmi)):
|
|
41
|
+
raise WinError()
|
|
42
|
+
return pmi.WorkingSetSize
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
LOOPS = 10, 1000
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def find_memleak(func, loops=LOOPS):
|
|
49
|
+
# call 'func' several times, so that memory consumption
|
|
50
|
+
# stabilizes:
|
|
51
|
+
for j in range(loops[0]):
|
|
52
|
+
for k in range(loops[1]):
|
|
53
|
+
func()
|
|
54
|
+
gc.collect()
|
|
55
|
+
gc.collect()
|
|
56
|
+
gc.collect()
|
|
57
|
+
bytes = wss()
|
|
58
|
+
# call 'func' several times, recording the difference in
|
|
59
|
+
# memory consumption before and after the call. Repeat this a
|
|
60
|
+
# few times, and return a list containing the memory
|
|
61
|
+
# consumption differences.
|
|
62
|
+
for j in range(loops[0]):
|
|
63
|
+
for k in range(loops[1]):
|
|
64
|
+
func()
|
|
65
|
+
gc.collect()
|
|
66
|
+
gc.collect()
|
|
67
|
+
gc.collect()
|
|
68
|
+
# return the increased in process size
|
|
69
|
+
result = wss() - bytes
|
|
70
|
+
# Sometimes the process size did decrease, we do not report leaks
|
|
71
|
+
# in this case:
|
|
72
|
+
return max(result, 0)
|