Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion comtypes/client/_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ def FindOutgoingInterface(source: IUnknown) -> type[IUnknown]:
except COMError:
pass
else:
if guid == comtypes.GUID():
# Some COM servers, even if they implement `IProvideClassInfo2`,
# may return GUID_NULL instead of the default source interface's GUID.
raise NotImplementedError("retrieved outgoing interface IID is GUID_NULL")
# another try: block needed?
try:
interface = comtypes.com_interface_registry[str(guid)]
Expand Down Expand Up @@ -258,7 +262,15 @@ def _get_method_finder_(self, itf: type[IUnknown]) -> _MethodFinder:
# Can dispid be at a different index? Should check code generator...
# ...but hand-written code should also work...
dispid = m.idlflags[0]
assert isinstance(dispid, comtypes.dispid)
if not isinstance(dispid, comtypes.dispid):
# The interface is a subclass of `IDispatch` but its methods do not
# have DISPIDs, indicating it's not an interface suitable for event
# handling.
raise NotImplementedError(
"Event receiver creation requires event methods to have DISPIDs "
f"for dispatching, but '{interface.__name__}' ({interface._iid_}) "
"lacks them, even though it inherits from 'IDispatch'."
)
impl = finder.get_impl(interface, m.name, m.paramflags, m.idlflags)
# XXX Wouldn't work for 'propget', 'propput', 'propputref'
# methods - are they allowed on event interfaces?
Expand Down
40 changes: 40 additions & 0 deletions comtypes/test/test_eventinterface.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import gc
import tempfile
import time
import unittest as ut
from ctypes import HRESULT, byref
from ctypes.wintypes import MSG
from pathlib import Path

from comtypes import COMMETHOD, GUID, IUnknown
from comtypes.automation import DISPID
Expand Down Expand Up @@ -111,5 +113,43 @@ def test_nondefault_eventinterface(self):
del conn


class Test_MSHTML(ut.TestCase):
def test_retrieved_outgoing_iid_is_guid_null(self):
doc = CreateObject("htmlfile")
sink = object()
# MSHTML's HTMLDocument (which is what `CreateObject('htmlfile')`
# returns) does not expose a valid default source interface through
# `IProvideClassInfo2`.
with self.assertRaises(NotImplementedError):
GetEvents(doc, sink)


class Test_IMAPI2FS(ut.TestCase):
def setUp(self):
CLSID_MsftFileSystemImage = GUID("{2C941FC5-975B-59BE-A960-9A2A262853A5}")
self.image = CreateObject(CLSID_MsftFileSystemImage)
self.image.FileSystemsToCreate = 1 # FsiFileSystemISO9660
td = tempfile.TemporaryDirectory()
self.tmp_dir = Path(td.name)
self.addCleanup(td.cleanup)

def tearDown(self):
del self.image
# Force garbage collection and wait slightly to ensure COM resources
# are released properly between tests.
gc.collect()
time.sleep(2)

def test_event_methods_lack_dispids(self):
sink = object()
# The default event interface for IMAPI2's FileSystemImage is
# `DFileSystemImageEvents`. Although it inherits from `IDispatch`,
# it is a custom interface (`TKIND_INTERFACE`), not a dual or pure
# dispatch interface (`TKIND_DISPATCH`); therefore, its methods
# do not have DISPIDs.
with self.assertRaises(NotImplementedError):
GetEvents(self.image, sink)


if __name__ == "__main__":
ut.main()