win32com -- the IE-events mistery continues

Alex Martelli alex at magenta.com
Sun Aug 13 05:33:02 EDT 2000


The win32com events mechanisms seem to selectively "cull",
among all of IE's events, a few and just not present it to the
(Python) handler.  I've tried to reproduce this in a precise and
controlled way, looking exactly at which events are lost, and
it's highly repeatable.  So here is what I've done so far.

A side note: I have this auxiliary "pluggable-listener" object
(which I developed while back, when I was COM-enabling
another language, to help me keep track of what events were
actually happening) which has a SourceObj property, which,
being a COM object, is naturally subject to get and putref
(not get and put).  I was unable to use the putref from Python;
apparently Python always turns the attribute-setting into a put,
and if a putref is needed, one's SOL.  In this case I fixed it
by adding a put for the property, parallel to its putref, to the
pluggable-listener interface; but how does one putref in the
general case?  One day or another I'll have to putref some
property on an object whose interface I can't control.  Is
there a way out...?  (In that other language, besides adding
specific ways to access put and putref explicitly [called
let and set by analogy to VB], I handled ambiguous put
attempts by retrying, with a putref, it the put try fails).

Anyway -- here's the Python code I ended up with (please
excuse any random linebreaks my newsreader may
insert here or there...).  I've worked at as low a level as
came to mind, implementing _invoke_ in my Python
listener so as to bypass higher translation levels that might
have some problems -- it made no difference: the events
are already lost at this level.  And similarly it made no
difference that I bypassed the helper code in DispatchWithEvents,
etc, etc.

#
# Test reception of IE events in Python and outside it
#
import win32com.client,pythoncom,time

# Microsoft Internet Controls
tlib, lcid, major, minor = '{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}', 0, 1, 1
# Auto-generate .py support
import win32com.client.gencache
junk=win32com.client.gencache.EnsureModule(tlib, lcid, major, minor)
browser_events_class=win32com.client.getevents("InternetExplorer.Application
")

class EventHandler1(browser_events_class):
    def __init__(self,ie,listener=None):
        self.listener=listener
        browser_events_class.__init__(self,ie)
    def _invoke_(self, dispid, lcid, wFlags, args):
        if self.listener:
            print self.listener.GetEvts(),
        print dispid, lcid, wFlags, args
        return S_OK, -1, None

def do():
    # ie = DispatchWithEvents("InternetExplorer.Application", EventHandler)
    ie = win32com.client.Dispatch("InternetExplorer.Application")
    listener = win32com.client.Dispatch("AuSupp.PluggableListener")
    listener.SourceObj=ie
    handler=EventHandler1(ie,listener)
    print 'handler:',handler
    print 'ie:',ie
    print 'listener:',listener
    ie.Visible = 1
    print "Starting: Navigate"
    #ie.Navigate("http://www.python.org/index.html")
    ie.Navigate(r"c:\pippo.htm")

    while ie.Visible:
        pythoncom.PumpWaitingMessages()
        time.sleep(0.2)
    print "IE not visible any more"
    try:
        print "Trying to quit IE"
        ie.Quit()
        print "OK!"
    except pythoncom.com_error, ex:
        print "Quit failed:",ex
    print "Program is over"

if __name__=='__main__':
    do()

The AuSupp.PluggableListener hooks itself up as an event listener
to whatever COM object is set (or let:-) as its SourceObj property,
along its default source interface (1st connection point returned by
the IEnumConnectionPoints given by EnumConnectionPoints of
the IConnectionPointContainer interface of SourceObj, actually),
and accumulates the dispids of events into an internal array; when
its GetEvts method is called, the array is returned (as a safearray
of VT_I4 wrapped into a VARIANT) and cleared.

So here's the output (I quit IE by clicking on the upper-right X as
soon as the page being navigated to stabilizes; the starting '0'
"event" shown by the pluggable-listener is an implementation
artefact -- pls ignore it):

>>> Ie_Events.do()
handler: <Ie_Events.EventHandler1 instance at 164b7c0>
ie: Microsoft Internet Explorer
listener: <COMObject AuSupp.PluggableListener>
(0, 105) 105 0L 1 [-1, 0]
(254,) 254 0L 1 [1]
Starting: Navigate
(105, 112) 112 0L 1 ['{265b75c1-4158-11d0-90f6-00c04fd497ea}']
(250, 106) 106 0L 1 []
(112,) 112 0L 1 ['{D0FCA420-D3F5-11CF-B211-00AA004AE837}']
(102,) 102 0L 1 ['Connecting to site ']
(108,) 108 0L 1 [0, 10000]
(102,) 102 0L 1 ['Start downloading from site: file://C:\\pippo.htm']
(102,) 102 0L 1 ['Downloading from site: file://C:\\pippo.htm']
(270,) 270 0L 1 [1, 0]
(104,) 104 0L 1 []
(102,) 102 0L 1 ['']
() 105 0L 1 [2, 0]
(105,) 105 0L 1 [1, 0]
(106,) 106 0L 1 []
(102,) 102 0L 1 ['Unknown Zone']
(102,) 102 0L 1 ['']
(102,) 102 0L 1 ['Opening page file://C:\\pippo.htm...']
(105,) 105 0L 1 [-1, 0]
(113,) 113 0L 1 ['pippo.htm']
(252, 102) 102 0L 1 ['Done']
(108,) 108 0L 1 [10000, 10000]
(108,) 108 0L 1 [-1, 10000]
(104,) 104 0L 1 []
(113,) 113 0L 1 ['C:\\pippo.htm']
(102,) 102 0L 1 ['My Computer']
(108,) 108 0L 1 [10000, 10000]
(259, 105) 105 0L 1 [2, 0]
(105,) 105 0L 1 [1, 0]
(102,) 102 0L 1 ['Done']
(102,) 102 0L 1 ['Done']
(102,) 102 0L 1 ['Done']
(105,) 105 0L 1 [-1, 0]
(108,) 108 0L 1 [0, 0]
(102,) 102 0L 1 ['Done']
(253,) 253 0L 1 []
IE not visible any more
Trying to quit IE
Quit failed: (-2147417848, 'The object invoked has disconnected from its
clients.', None, None)
Program is over
>>>

We notice one case where the pluggable-listener has not seen an event that
is received by the Python listener (a 105, CommandStateChange), which is
not very reproducible between runs; and several fully reproducible cases
where the pluggable listener sees 2 events while the Python listener gets
only one -- here are the relevant lines again:

(105, 112) 112 0L 1 ['{265b75c1-4158-11d0-90f6-00c04fd497ea}']
(250, 106) 106 0L 1 []
(252, 102) 102 0L 1 ['Done']
(259, 105) 105 0L 1 [2, 0]

The 105 again (this time seen by the puggable listener and not by Python;
not very significant, I suspect, basically compensating the other 105
that only goes to Python later), plus 3 more messages that are _always_
'lost' to Python: 250 (OnBeforeNavigate2), 252 (OnNavigateComplete2),
259 (OnDocumentComplete).  What these 3 specific messages have in
common, that's not also true of some of the several messages that are
successfully received on both sides -- I don't know!


Anybody has suggestions about how I could proceed from here...?


I guess I could workaround by a little tweak to the PluggableListener
so that it will callback to another object when it receives events.  This
would basically mean giving up on Python event support and rolling
my own (I guess I would still get to use the handy higher levels, just as
for any other call to a Python-written COM server along its IDispatch
interface).  Any other idea...?


Alex






More information about the Python-list mailing list