Skip to content

Commit 1aab6ae

Browse files
authored
Implement ITypeInfo.AddressOfMember with tests and clarifications. (#905)
* feat: Implement `ITypeInfo.AddressOfMember`, add type hints, and new test. Implements the `ITypeInfo.AddressOfMember` method to support retrieving addresses of static functions and variables. This allows access to member addresses of COM objects. Type hints and tests have also been added. * docs: Add clarifying comments to `test_module_ITypeInfo`. Adds comments to the `test_module_ITypeInfo` to clarify the usage of `stdole2.tlb` and provide an alternative method for obtaining typeinfo. * test: Add assertions for `GetTypeAttr` in `test_module_ITypeInfo` Adds assertions for `tattr.cImplTypes` and `tattr.typekind` within the `test_module_ITypeInfo`. * test: Validate `GetDllEntry` return values and add comments. Adds assertions to verify the `dll_name`, `func_name`, and `ordinal` returned by `tinfo.GetDllEntry`. Also includes clarifying comments regarding the behavior of `GetDllEntry` and the `stdole2.tlb` component.
1 parent 921b699 commit 1aab6ae

File tree

2 files changed

+36
-5
lines changed

2 files changed

+36
-5
lines changed

comtypes/test/test_typeinfo.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,39 @@ def test_dual_interface_ITypeInfo(self):
131131
self.assertEqual(refta.typekind, typeinfo.TKIND_INTERFACE)
132132
self.assertEqual(ti, refti.GetRefTypeInfo(refti.GetRefTypeOfImplType(-1)))
133133

134+
def test_module_ITypeInfo(self):
135+
# `AddressOfMember` method retrieves the addresses of static functions
136+
# or variables defined in a module. We will test this functionality by
137+
# using the 'StdFunctions' module within 'stdole2.tlb', which contains
138+
# static functions like 'LoadPicture' or 'SavePicture'.
139+
# NOTE: The name 'stdole2' refers to OLE 2.0; it is a core Windows
140+
# component that has remained unchanged for decades to ensure
141+
# compatibility, making any future name changes highly improbable.
142+
tlib = LoadTypeLibEx("stdole2.tlb")
143+
# Same as `tinfo = GetTypeInfoOfGuid(GUID('{91209AC0-60F6-11CF-9C5D-00AA00C1489E}'))`
144+
stdfuncs_info = tlib.FindName("StdFunctions")
145+
self.assertIsNotNone(stdfuncs_info)
146+
_, tinfo = stdfuncs_info # type: ignore
147+
tattr = tinfo.GetTypeAttr()
148+
self.assertEqual(tattr.cImplTypes, 0)
149+
self.assertEqual(tattr.typekind, typeinfo.TKIND_MODULE)
150+
memid, *_ = tinfo.GetIDsOfNames("LoadPicture")
151+
self.assertEqual(tinfo.GetDocumentation(memid)[0], "LoadPicture")
152+
# 'LoadPicture' is the alias used within the type library.
153+
# `GetDllEntry` returns the actual exported name from the DLL, which
154+
# may be different.
155+
dll_name, func_name, ordinal = tinfo.GetDllEntry(memid, typeinfo.INVOKE_FUNC)
156+
# For functions exported by name, `GetDllEntry` returns a 3-tuple:
157+
# (DLL name, function name, ordinal of 0).
158+
self.assertIn("oleaut32.dll", dll_name.lower()) # type: ignore
159+
self.assertEqual(func_name, "OleLoadPictureFileEx")
160+
self.assertEqual(ordinal, 0)
161+
_oleaut32 = ctypes.WinDLL(dll_name)
162+
load_picture = getattr(_oleaut32, func_name) # type: ignore
163+
expected_addr = ctypes.cast(load_picture, ctypes.c_void_p).value
164+
actual_addr = tinfo.AddressOfMember(memid, typeinfo.INVOKE_FUNC)
165+
self.assertEqual(actual_addr, expected_addr)
166+
134167

135168
class Test_GetModuleFileName(unittest.TestCase):
136169
@unittest.skipUnless(

comtypes/typeinfo.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -405,13 +405,11 @@ def GetIDsOfNames(self, *names: str) -> list[int]:
405405
self.__com_GetIDsOfNames(rgsznames, len(names), ids) # type: ignore
406406
return ids[:]
407407

408-
def AddressOfMember(self, memid, invkind):
408+
def AddressOfMember(self, memid: int, invkind: int) -> int:
409409
"""Get the address of a function in a dll"""
410-
raise RuntimeError("Check Me")
411410
p = c_void_p()
412-
self.__com_AddressOfMember(memid, invkind, byref(p))
413-
# XXX Would the default impl return the value of p?
414-
return p.value
411+
self.__com_AddressOfMember(memid, invkind, byref(p)) # type: ignore
412+
return p.value # type: ignore
415413

416414
def CreateInstance(
417415
self,

0 commit comments

Comments
 (0)