From d3f79a66d6fbc5f27c2778e03cb268ff78ca02b6 Mon Sep 17 00:00:00 2001 From: junkmd Date: Sun, 18 Jan 2026 13:08:59 +0900 Subject: [PATCH 1/4] 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. --- comtypes/test/test_typeinfo.py | 19 +++++++++++++++++++ comtypes/typeinfo.py | 8 +++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/comtypes/test/test_typeinfo.py b/comtypes/test/test_typeinfo.py index 76f25bbb..60cac20c 100644 --- a/comtypes/test/test_typeinfo.py +++ b/comtypes/test/test_typeinfo.py @@ -131,6 +131,25 @@ def test_dual_interface_ITypeInfo(self): self.assertEqual(refta.typekind, typeinfo.TKIND_INTERFACE) self.assertEqual(ti, refti.GetRefTypeInfo(refti.GetRefTypeOfImplType(-1))) + def test_module_ITypeInfo(self): + # `AddressOfMember` method retrieves the addresses of static functions + # or variables defined in a module. We will test this functionality by + # using the 'StdFunctions' module within 'stdole2.tlb', which contains + # static functions like 'LoadPicture' or 'SavePicture'. + tlib = LoadTypeLibEx("stdole2.tlb") + stdfuncs_info = tlib.FindName("StdFunctions") + self.assertIsNotNone(stdfuncs_info) + _, tinfo = stdfuncs_info # type: ignore + memid, *_ = tinfo.GetIDsOfNames("LoadPicture") + self.assertEqual(tinfo.GetDocumentation(memid)[0], "LoadPicture") + # 'LoadPicture' is the alias used within the type library. + dll_name, func_name, _ = tinfo.GetDllEntry(memid, typeinfo.INVOKE_FUNC) + _oleaut32 = ctypes.WinDLL(dll_name) + load_picture = getattr(_oleaut32, func_name) # type: ignore + expected_addr = ctypes.cast(load_picture, ctypes.c_void_p).value + actual_addr = tinfo.AddressOfMember(memid, typeinfo.INVOKE_FUNC) + self.assertEqual(actual_addr, expected_addr) + class Test_GetModuleFileName(unittest.TestCase): @unittest.skipUnless( diff --git a/comtypes/typeinfo.py b/comtypes/typeinfo.py index 97377c22..f87055d1 100644 --- a/comtypes/typeinfo.py +++ b/comtypes/typeinfo.py @@ -405,13 +405,11 @@ def GetIDsOfNames(self, *names: str) -> list[int]: self.__com_GetIDsOfNames(rgsznames, len(names), ids) # type: ignore return ids[:] - def AddressOfMember(self, memid, invkind): + def AddressOfMember(self, memid: int, invkind: int) -> int: """Get the address of a function in a dll""" - raise RuntimeError("Check Me") p = c_void_p() - self.__com_AddressOfMember(memid, invkind, byref(p)) - # XXX Would the default impl return the value of p? - return p.value + self.__com_AddressOfMember(memid, invkind, byref(p)) # type: ignore + return p.value # type: ignore def CreateInstance( self, From 478993fa6b28a80b740d229be90bcf657d7550f0 Mon Sep 17 00:00:00 2001 From: junkmd Date: Sun, 18 Jan 2026 13:09:00 +0900 Subject: [PATCH 2/4] 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. --- comtypes/test/test_typeinfo.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/comtypes/test/test_typeinfo.py b/comtypes/test/test_typeinfo.py index 60cac20c..317ea609 100644 --- a/comtypes/test/test_typeinfo.py +++ b/comtypes/test/test_typeinfo.py @@ -136,7 +136,11 @@ def test_module_ITypeInfo(self): # or variables defined in a module. We will test this functionality by # using the 'StdFunctions' module within 'stdole2.tlb', which contains # static functions like 'LoadPicture' or 'SavePicture'. + # NOTE: The name 'stdole2' refers to OLE 2.0; it is a core Windows + # component that has remained unchanged for decades to ensure + # compatibility, making any future name changes highly improbable. tlib = LoadTypeLibEx("stdole2.tlb") + # Same as `tinfo = GetTypeInfoOfGuid(GUID('{91209AC0-60F6-11CF-9C5D-00AA00C1489E}'))` stdfuncs_info = tlib.FindName("StdFunctions") self.assertIsNotNone(stdfuncs_info) _, tinfo = stdfuncs_info # type: ignore From 3989544773e4d5ce1d52109bee6a03d05ac03eb0 Mon Sep 17 00:00:00 2001 From: junkmd Date: Sun, 18 Jan 2026 13:09:00 +0900 Subject: [PATCH 3/4] test: Add assertions for `GetTypeAttr` in `test_module_ITypeInfo` Adds assertions for `tattr.cImplTypes` and `tattr.typekind` within the `test_module_ITypeInfo`. --- comtypes/test/test_typeinfo.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/comtypes/test/test_typeinfo.py b/comtypes/test/test_typeinfo.py index 317ea609..b000b8a3 100644 --- a/comtypes/test/test_typeinfo.py +++ b/comtypes/test/test_typeinfo.py @@ -144,6 +144,9 @@ def test_module_ITypeInfo(self): stdfuncs_info = tlib.FindName("StdFunctions") self.assertIsNotNone(stdfuncs_info) _, tinfo = stdfuncs_info # type: ignore + tattr = tinfo.GetTypeAttr() + self.assertEqual(tattr.cImplTypes, 0) + self.assertEqual(tattr.typekind, typeinfo.TKIND_MODULE) memid, *_ = tinfo.GetIDsOfNames("LoadPicture") self.assertEqual(tinfo.GetDocumentation(memid)[0], "LoadPicture") # 'LoadPicture' is the alias used within the type library. From 815034c1df210739d9d9046d2a682d182d64ee0c Mon Sep 17 00:00:00 2001 From: junkmd Date: Sun, 18 Jan 2026 13:09:00 +0900 Subject: [PATCH 4/4] 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. --- comtypes/test/test_typeinfo.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/comtypes/test/test_typeinfo.py b/comtypes/test/test_typeinfo.py index b000b8a3..a8276b56 100644 --- a/comtypes/test/test_typeinfo.py +++ b/comtypes/test/test_typeinfo.py @@ -150,7 +150,14 @@ def test_module_ITypeInfo(self): memid, *_ = tinfo.GetIDsOfNames("LoadPicture") self.assertEqual(tinfo.GetDocumentation(memid)[0], "LoadPicture") # 'LoadPicture' is the alias used within the type library. - dll_name, func_name, _ = tinfo.GetDllEntry(memid, typeinfo.INVOKE_FUNC) + # `GetDllEntry` returns the actual exported name from the DLL, which + # may be different. + dll_name, func_name, ordinal = tinfo.GetDllEntry(memid, typeinfo.INVOKE_FUNC) + # For functions exported by name, `GetDllEntry` returns a 3-tuple: + # (DLL name, function name, ordinal of 0). + self.assertIn("oleaut32.dll", dll_name.lower()) # type: ignore + self.assertEqual(func_name, "OleLoadPictureFileEx") + self.assertEqual(ordinal, 0) _oleaut32 = ctypes.WinDLL(dll_name) load_picture = getattr(_oleaut32, func_name) # type: ignore expected_addr = ctypes.cast(load_picture, ctypes.c_void_p).value