Skip to content

Commit baef773

Browse files
authored
Introduce malloc module (part 1) (#900)
* feat: Introduce `malloc.py` for COM memory management. Introduces `malloc.py` which provides `IMalloc` interface definition. Renamed `test_outparam.py` to `test_from_outparam.py`. Update `pyproject.toml` for renamed test file. * test: Add blank `test_malloc.py` for `IMalloc` interface. * fix: Eliminate the testcase.
1 parent 925c5cb commit baef773

File tree

4 files changed

+84
-1
lines changed

4 files changed

+84
-1
lines changed

comtypes/malloc.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import logging
2+
from ctypes import (
3+
HRESULT,
4+
POINTER,
5+
OleDLL,
6+
WinDLL,
7+
byref,
8+
c_int,
9+
c_size_t,
10+
c_ulong,
11+
c_void_p,
12+
c_wchar,
13+
c_wchar_p,
14+
cast,
15+
memmove,
16+
sizeof,
17+
wstring_at,
18+
)
19+
from ctypes.wintypes import DWORD, LPVOID
20+
21+
from comtypes import COMMETHOD, GUID, IUnknown
22+
from comtypes.GUID import _CoTaskMemFree
23+
24+
logger = logging.getLogger(__name__)
25+
26+
27+
class IMalloc(IUnknown):
28+
_iid_ = GUID("{00000002-0000-0000-C000-000000000046}")
29+
_methods_ = [
30+
COMMETHOD([], c_void_p, "Alloc", ([], c_ulong, "cb")),
31+
COMMETHOD([], c_void_p, "Realloc", ([], c_void_p, "pv"), ([], c_ulong, "cb")),
32+
COMMETHOD([], None, "Free", ([], c_void_p, "py")),
33+
COMMETHOD([], c_ulong, "GetSize", ([], c_void_p, "pv")),
34+
COMMETHOD([], c_int, "DidAlloc", ([], c_void_p, "pv")),
35+
COMMETHOD([], None, "HeapMinimize"), # 25
36+
]
37+
38+
39+
_ole32 = OleDLL("ole32")
40+
41+
_CoGetMalloc = _ole32.CoGetMalloc
42+
_CoGetMalloc.argtypes = [DWORD, POINTER(POINTER(IMalloc))]
43+
_CoGetMalloc.restype = HRESULT
44+
45+
_ole32_nohresult = WinDLL("ole32")
46+
47+
SIZE_T = c_size_t
48+
_CoTaskMemAlloc = _ole32_nohresult.CoTaskMemAlloc
49+
_CoTaskMemAlloc.argtypes = [SIZE_T]
50+
_CoTaskMemAlloc.restype = LPVOID
51+
52+
malloc = POINTER(IMalloc)()
53+
_CoGetMalloc(1, byref(malloc))
54+
assert bool(malloc)
55+
56+
57+
def from_outparam(self):
58+
if not self:
59+
return None
60+
result = wstring_at(self)
61+
# `DidAlloc` method returns;
62+
# * 1 (allocated)
63+
# * 0 (not allocated)
64+
# * -1 (cannot determine or NULL)
65+
# https://learn.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-imalloc-didalloc
66+
assert malloc.DidAlloc(self), "memory was NOT allocated by CoTaskMemAlloc"
67+
_CoTaskMemFree(self)
68+
return result
69+
70+
71+
def comstring(text, typ=c_wchar_p):
72+
size = (len(text) + 1) * sizeof(c_wchar)
73+
mem = _CoTaskMemAlloc(size)
74+
logger.debug("malloc'd 0x%x, %d bytes" % (mem, size))
75+
ptr = cast(mem, typ)
76+
memmove(mem, text, size)
77+
return ptr

comtypes/test/test_malloc.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import unittest as ut
2+
3+
from comtypes.malloc import IMalloc # noqa
4+
5+
6+
class Test(ut.TestCase): ...

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ ignore = ["E402"]
8989
"comtypes/test/test_client.py" = ["F401"]
9090
"comtypes/test/test_dict.py" = ["F841"]
9191
"comtypes/test/test_eventinterface.py" = ["F841"]
92-
"comtypes/test/test_outparam.py" = ["F841"]
92+
"comtypes/test/test_from_outparam.py" = ["F841"]
9393
"comtypes/test/test_sapi.py" = ["E401"]
9494
"comtypes/test/test_server.py" = ["F401", "F841"]
9595
"comtypes/test/test_subinterface.py" = ["E401", "F401", "F403", "F405"]

0 commit comments

Comments
 (0)