From 2cff249f399e7e53296e5c038c173933f0cb11a4 Mon Sep 17 00:00:00 2001 From: junkmd Date: Sun, 18 Jan 2026 21:48:20 +0900 Subject: [PATCH 1/8] test: Add test for `ITypeComp.BindType` functionality. --- comtypes/test/test_typeinfo.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/comtypes/test/test_typeinfo.py b/comtypes/test/test_typeinfo.py index a8276b56..343df84e 100644 --- a/comtypes/test/test_typeinfo.py +++ b/comtypes/test/test_typeinfo.py @@ -165,6 +165,17 @@ def test_module_ITypeInfo(self): self.assertEqual(actual_addr, expected_addr) +class Test_ITypeComp_BindType(unittest.TestCase): + def test_interface(self): + IID_IFile = GUID("{C7C3F5A4-88A3-11D0-ABCB-00A0C90FFFC0}") + tlib = LoadTypeLibEx("scrrun.dll") + tcomp = tlib.GetTypeComp() + ti_file, tc_file = tcomp.BindType("IFile") + self.assertEqual(ti_file.GetDocumentation(-1)[0], "IFile") + self.assertFalse(tc_file) + self.assertEqual(ti_file.GetTypeAttr().guid, IID_IFile) + + class Test_GetModuleFileName(unittest.TestCase): @unittest.skipUnless( sys.prefix == sys.base_prefix, From 37dda60e1cd4ae747d90ee5e2be9c13530cb6aa9 Mon Sep 17 00:00:00 2001 From: junkmd Date: Sun, 18 Jan 2026 21:48:20 +0900 Subject: [PATCH 2/8] test: Add test for `ITypeComp.Bind` with non-existent name. Adds a test case to verify that calling `ITypeComp.Bind` with a non-existent name correctly raises a `NameError`. --- comtypes/test/test_typeinfo.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/comtypes/test/test_typeinfo.py b/comtypes/test/test_typeinfo.py index 343df84e..7bcf3f47 100644 --- a/comtypes/test/test_typeinfo.py +++ b/comtypes/test/test_typeinfo.py @@ -176,6 +176,14 @@ def test_interface(self): self.assertEqual(ti_file.GetTypeAttr().guid, IID_IFile) +class Test_ITypeComp_Bind(unittest.TestCase): + def test_non_existent_name(self): + tlib = LoadTypeLibEx("scrrun.dll") + tcomp = tlib.GetTypeComp() + with self.assertRaises(NameError): + tcomp.Bind("NonExistentNameForTest") + + class Test_GetModuleFileName(unittest.TestCase): @unittest.skipUnless( sys.prefix == sys.base_prefix, From 11fb16ca6c8535d3531bad6f30fa153ad06527af Mon Sep 17 00:00:00 2001 From: junkmd Date: Sun, 18 Jan 2026 21:48:20 +0900 Subject: [PATCH 3/8] test: Add test for `ITypeComp.Bind` to handle enum types. --- comtypes/test/test_typeinfo.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/comtypes/test/test_typeinfo.py b/comtypes/test/test_typeinfo.py index 7bcf3f47..ef8582d2 100644 --- a/comtypes/test/test_typeinfo.py +++ b/comtypes/test/test_typeinfo.py @@ -177,6 +177,13 @@ def test_interface(self): class Test_ITypeComp_Bind(unittest.TestCase): + def test_enum(self): + tlib = LoadTypeLibEx("stdole2.tlb") + tcomp = tlib.GetTypeComp() + tristate_kind, tristate_tcomp = tcomp.Bind("OLE_TRISTATE") # type: ignore + self.assertEqual(tristate_kind, "type") + self.assertIsInstance(tristate_tcomp, typeinfo.ITypeComp) + def test_non_existent_name(self): tlib = LoadTypeLibEx("scrrun.dll") tcomp = tlib.GetTypeComp() From f8beba70e0b6421a1953475667dc4843b3954ea7 Mon Sep 17 00:00:00 2001 From: junkmd Date: Sun, 18 Jan 2026 21:48:20 +0900 Subject: [PATCH 4/8] test: Add test for `ITypeComp.Bind` to handle interface members. --- comtypes/test/test_typeinfo.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/comtypes/test/test_typeinfo.py b/comtypes/test/test_typeinfo.py index ef8582d2..5dbd9817 100644 --- a/comtypes/test/test_typeinfo.py +++ b/comtypes/test/test_typeinfo.py @@ -184,6 +184,20 @@ def test_enum(self): self.assertEqual(tristate_kind, "type") self.assertIsInstance(tristate_tcomp, typeinfo.ITypeComp) + def test_interface(self): + tlib = LoadTypeLibEx("stdole2.tlb") + IID_Picture = GUID("{7BF80981-BF32-101A-8BBB-00AA00300CAB}") + tinfo = tlib.GetTypeInfoOfGuid(IID_Picture) + tcomp = tinfo.GetTypeComp() + handle_kind, handle_vd = tcomp.Bind("Handle") # type: ignore + self.assertEqual(handle_kind, "variable") + self.assertIsInstance(handle_vd, typeinfo.VARDESC) + self.assertEqual(handle_vd.varkind, typeinfo.VAR_DISPATCH) # type: ignore + render_kind, render_fd = tcomp.Bind("Render") # type: ignore + self.assertEqual(render_kind, "function") + self.assertIsInstance(render_fd, typeinfo.tagFUNCDESC) + self.assertEqual(render_fd.funckind, typeinfo.FUNC_DISPATCH) # type: ignore + def test_non_existent_name(self): tlib = LoadTypeLibEx("scrrun.dll") tcomp = tlib.GetTypeComp() From d416b0fa6e47d832d63e820bef5c710d5f17cf5b Mon Sep 17 00:00:00 2001 From: junkmd Date: Sun, 18 Jan 2026 21:48:20 +0900 Subject: [PATCH 5/8] test: Extend `ITypeComp.Bind` test for enum members. --- 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 5dbd9817..9792cde0 100644 --- a/comtypes/test/test_typeinfo.py +++ b/comtypes/test/test_typeinfo.py @@ -183,6 +183,10 @@ def test_enum(self): tristate_kind, tristate_tcomp = tcomp.Bind("OLE_TRISTATE") # type: ignore self.assertEqual(tristate_kind, "type") self.assertIsInstance(tristate_tcomp, typeinfo.ITypeComp) + gray_kind, gray_vd = tristate_tcomp.Bind("Gray") # type: ignore + self.assertEqual(gray_kind, "variable") + self.assertIsInstance(gray_vd, typeinfo.tagVARDESC) + self.assertEqual(gray_vd.varkind, typeinfo.VAR_CONST) # type: ignore def test_interface(self): tlib = LoadTypeLibEx("stdole2.tlb") From 431c5e6a77788bdd8eeb1a2c35c8906f5b07afb2 Mon Sep 17 00:00:00 2001 From: junkmd Date: Sun, 18 Jan 2026 21:48:20 +0900 Subject: [PATCH 6/8] docs: Add comments to `ITypeComp.Bind` regarding `DESCKIND_IMPLICITAPPOBJ`. Adds clarifying comments within the `ITypeComp.Bind` method in `typeinfo.py` to explain the nature and rarity of the `DESCKIND_IMPLICITAPPOBJ` type description kind. --- comtypes/typeinfo.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/comtypes/typeinfo.py b/comtypes/typeinfo.py index f87055d1..ae5c63df 100644 --- a/comtypes/typeinfo.py +++ b/comtypes/typeinfo.py @@ -454,6 +454,10 @@ def Bind( elif kind == DESCKIND_TYPECOMP: return "type", bindptr.lptcomp elif kind == DESCKIND_IMPLICITAPPOBJ: + # `DESCKIND_IMPLICITAPPOBJ` is rare; mainly for Office Application. + # It indicates a global application object (`TYPEFLAG_FAPPOBJECT`). + # Few other COM components use this highly specialized flag. + # Thus, this path is unlikely to be hit in common scenarios. raise NotImplementedError elif kind == DESCKIND_NONE: raise NameError("Name %s not found" % name) From d055441e90f95a127ce16346489f2825501767c0 Mon Sep 17 00:00:00 2001 From: junkmd Date: Sun, 18 Jan 2026 21:48:20 +0900 Subject: [PATCH 7/8] docs: Add comments to `ITypeComp.Bind` regarding `DESCKIND_MAX`. Adds clarifying comments within the `ITypeComp.Bind` method in `typeinfo.py` to explain that `DESCKIND_MAX` is not a valid return value from `ITypeComp::Bind` and implies a malformed type library. --- comtypes/typeinfo.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/comtypes/typeinfo.py b/comtypes/typeinfo.py index ae5c63df..b858f85a 100644 --- a/comtypes/typeinfo.py +++ b/comtypes/typeinfo.py @@ -461,6 +461,9 @@ def Bind( raise NotImplementedError elif kind == DESCKIND_NONE: raise NameError("Name %s not found" % name) + # `DESCKIND_MAX` is an end-of-enumeration marker, not a valid return + # from `ITypeComp::Bind` in COM. If returned, it implies a malformed + # type library. The current implementation implicitly returns `None`. def BindType(self, name: str, lHashVal: int = 0) -> tuple[ITypeInfo, "ITypeComp"]: """Bind a type, and return both the typeinfo and typecomp for it.""" From 6dfa71134b636ab76f0d893160377af42b74035c Mon Sep 17 00:00:00 2001 From: junkmd Date: Sun, 18 Jan 2026 21:48:20 +0900 Subject: [PATCH 8/8] refactor: Improve return type hints for `ITypeComp.Bind`. Refines the return type hints for the `ITypeComp.Bind` method in `typeinfo.py` to use `typing.Literal` for the kind string, providing more precise type information and enhancing static analysis capabilities. --- comtypes/typeinfo.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/comtypes/typeinfo.py b/comtypes/typeinfo.py index b858f85a..52a20133 100644 --- a/comtypes/typeinfo.py +++ b/comtypes/typeinfo.py @@ -31,7 +31,7 @@ WCHAR, WORD, ) -from typing import TYPE_CHECKING, Any, Optional, TypeVar, overload +from typing import TYPE_CHECKING, Any, Literal, Optional, TypeVar, overload from typing import Union as _UnionT from comtypes import BSTR, COMMETHOD, GUID, IID, STDMETHOD, IUnknown, _CData @@ -430,7 +430,12 @@ class ITypeComp(IUnknown): def Bind( self, name: str, flags: int = 0, lHashVal: int = 0 - ) -> Optional[tuple[str, _UnionT["FUNCDESC", "VARDESC", "ITypeComp"]]]: + ) -> _UnionT[ + tuple[Literal["function"], "FUNCDESC"], + tuple[Literal["variable"], "VARDESC"], + tuple[Literal["type"], "ITypeComp"], + None, + ]: """Bind to a name""" bindptr = BINDPTR() desckind = DESCKIND()