diff --git a/comtypes/test/test_eventinterface.py b/comtypes/test/test_eventinterface.py
index 9bfca80d..d30c0339 100644
--- a/comtypes/test/test_eventinterface.py
+++ b/comtypes/test/test_eventinterface.py
@@ -1,7 +1,11 @@
+import gc
+import time
import unittest as ut
-from ctypes import byref
+from ctypes import HRESULT, byref
from ctypes.wintypes import MSG
+from comtypes import COMMETHOD, GUID, IUnknown
+from comtypes.automation import DISPID
from comtypes.client import CreateObject, GetEvents
from comtypes.messageloop import (
PM_REMOVE,
@@ -10,42 +14,39 @@
TranslateMessage,
)
-# FIXME: External test dependencies like this seem bad. Find a different
-# built-in win32 API to use.
# The primary goal is to verify how `GetEvents` behaves when the
# `interface` argument is explicitly specified versus when it is omitted,
# using an object that has multiple outgoing event interfaces.
+class IPropertyNotifySink(IUnknown):
+ # https://learn.microsoft.com/en-us/windows/win32/api/ocidl/nn-ocidl-ipropertynotifysink
+ _iid_ = GUID("{9BFBBC02-EFF1-101A-84ED-00AA00341D07}")
+ _methods_ = [
+ # Called when a property has changed.
+ COMMETHOD([], HRESULT, "OnChanged", (["in"], DISPID, "dispid")),
+ # Called when an object wants to know if it's okay to change a property.
+ COMMETHOD([], HRESULT, "OnRequestEdit", (["in"], DISPID, "dispid")),
+ ]
+
+
class EventSink:
def __init__(self):
self._events = []
- # some DWebBrowserEvents
- def OnVisible(self, this, *args):
- # print "OnVisible", args
- self._events.append("OnVisible")
-
- def BeforeNavigate(self, this, *args):
- # print "BeforeNavigate", args
- self._events.append("BeforeNavigate")
+ # Events from the default dispatch interface
+ def onreadystatechange(self, this, *args):
+ self._events.append("onreadystatechange")
- def NavigateComplete(self, this, *args):
- # print "NavigateComplete", args
- self._events.append("NavigateComplete")
+ def ondataavailable(self, this, *args):
+ self._events.append("ondataavailable")
- # some DWebBrowserEvents2
- def BeforeNavigate2(self, this, *args):
- # print "BeforeNavigate2", args
- self._events.append("BeforeNavigate2")
+ # Events from `IPropertyNotifySink`
+ def OnChanged(self, this, *args):
+ self._events.append("OnChanged")
- def NavigateComplete2(self, this, *args):
- # print "NavigateComplete2", args
- self._events.append("NavigateComplete2")
-
- def DocumentComplete(self, this, *args):
- # print "DocumentComplete", args
- self._events.append("DocumentComplete")
+ def OnRequestEdit(self, this, *args):
+ self._events.append("OnRequestEdit")
def PumpWaitingMessages():
@@ -55,70 +56,59 @@ def PumpWaitingMessages():
DispatchMessage(byref(msg))
-class Test(ut.TestCase):
- def tearDown(self):
- import gc
+class Test_MSXML(ut.TestCase):
+ def setUp(self):
+ # We use `Msxml2.DOMDocument` because it is a built-in Windows
+ # component that supports both a default source interface and the
+ # `IPropertyNotifySink` connection point.
+ self.doc = CreateObject("Msxml2.DOMDocument")
+ self.doc.async_ = True
+ def tearDown(self):
+ del self.doc
+ # Force garbage collection and wait slightly to ensure COM resources
+ # are released properly between tests.
gc.collect()
- import time
-
time.sleep(2)
- @ut.skip(
- "External test dependencies like this seem bad. Find a different built-in "
- "win32 API to use."
- )
def test_default_eventinterface(self):
+ # Verify that `GetEvents` automatically connects to the default source
+ # interface (dispatch events like `onreadystatechange`) when no
+ # interface is explicitly requested.
sink = EventSink()
- ie = CreateObject("InternetExplorer.Application")
- conn = GetEvents(ie, sink=sink)
- ie.Visible = True
- ie.Navigate2(URL="http://docs.python.org/", Flags=0)
- import time
+ conn = GetEvents(self.doc, sink)
+ self.doc.loadXML("")
- for i in range(50):
+ # Give the message loop time to process incoming events.
+ for _ in range(50):
PumpWaitingMessages()
time.sleep(0.1)
- ie.Visible = False
- ie.Quit()
-
- self.assertEqual(
- sink._events,
- [
- "OnVisible",
- "BeforeNavigate2",
- "NavigateComplete2",
- "DocumentComplete",
- "OnVisible",
- ],
- )
-
- del ie
+ # Should receive events from the default dispatch interface, but not
+ # events from `IPropertyNotifySink`.
+ self.assertIn("onreadystatechange", sink._events)
+ self.assertNotIn("OnChanged", sink._events)
+
del conn
- @ut.skip(
- "External test dependencies like this seem bad. Find a different built-in "
- "win32 API to use."
- )
def test_nondefault_eventinterface(self):
+ # Verify that `GetEvents` can connect to a non-default interface
+ # (like `IPropertyNotifySink`) when it is explicitly provided.
sink = EventSink()
- ie = CreateObject("InternetExplorer.Application")
- import comtypes.gen.SHDocVw as mod
- conn = GetEvents(ie, sink, interface=mod.DWebBrowserEvents)
+ conn = GetEvents(self.doc, sink, interface=IPropertyNotifySink)
- ie.Visible = True
- ie.Navigate2(Flags=0, URL="http://docs.python.org/")
- import time
+ self.doc.loadXML("")
- for i in range(50):
+ # Give the message loop time to process incoming events.
+ for _ in range(50):
PumpWaitingMessages()
time.sleep(0.1)
- ie.Visible = False
- ie.Quit()
+ # Should receive events from `IPropertyNotifySink`, but not events from
+ # the default dispatch interface.
+ self.assertNotIn("onreadystatechange", sink._events)
+ self.assertIn("OnChanged", sink._events)
- self.assertEqual(sink._events, ["BeforeNavigate", "NavigateComplete"])
- del ie
+ del conn
if __name__ == "__main__":