Skip to content
44 changes: 44 additions & 0 deletions comtypes/test/test_typeinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
16 changes: 14 additions & 2 deletions comtypes/typeinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -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."""
Expand Down