diff --git a/comtypes/test/test_typeinfo.py b/comtypes/test/test_typeinfo.py index a8276b56..9792cde0 100644 --- a/comtypes/test/test_typeinfo.py +++ b/comtypes/test/test_typeinfo.py @@ -165,6 +165,50 @@ 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_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) + 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") + 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() + with self.assertRaises(NameError): + tcomp.Bind("NonExistentNameForTest") + + class Test_GetModuleFileName(unittest.TestCase): @unittest.skipUnless( sys.prefix == sys.base_prefix, diff --git a/comtypes/typeinfo.py b/comtypes/typeinfo.py index f87055d1..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() @@ -454,9 +459,16 @@ 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) + # `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."""