[python-win32] DispatchWithEvents design question

Richard Bell rbell01824 at earthlink.net
Sat Jun 9 17:55:03 CEST 2007


Tim,

Thanks again.  Your assistance has been extremely helpful.

I've modified the test routine to illustrate the substance of our exchange
and am posting it by way of giving something back to the group and in the
off chance that it may prove useful to others.  

BTW, I'm still a bit concerned about the Python COM related crash on
trailing events but will leave it to others more skilled than me to decide
what if anything should be done.

By way of explanation for others the output, included below after the code,
illustrates the following:

At 2.185 when the main sets ie.Visible = 1, the OnVisible event code runs
BEFORE control returns to the main routine at 2.272 and absent a
PumpWaitingMessages.  Clearly setting this IE property will cause the
On-event code to run and some events are synchronous with main's interaction
with COM.

At 2.272 when the main sets ie.StatusBar = 0, both the OnCommandStateChange
and OnStatus bar on event routines run BEFORE control returns to the main
routine at 2.275 and absent a PumpWaitingMessages.  While later
OnCommandStateChange events must be pumped, there are some conditions when a
given On-event routine WILL EXECUTE absent a pump but later WILL REQUIRE a
pump.

At 2.324 when the main initiates a navigation both the OnCommandStateChange
and OnBeforeNavigate2 event routines run BEFORE control returns to the main
at 2.400 and absent a PumpWaitingMessages.  Clearly event routines can and
do run when some (but not necessarily all) COM object properties are changed
or methods executed. 

At 2.401 when the main spins in Python for 30 seconds WITHOUT pumping NO
On-event code is run.  In an apartment-threaded model absent a bug in the
object's COM interface event code should never be executed unless COM
properties are changed or methods executed.

At 32.413 when the main does 'one pump' the OnCommandStateChange event coded
is executed 3 times BEFORE control returns to the main at 32.419.  Clearly
events can and do occur while the main is off doing whatever and the COM
object is off doing its own whatever.  These events will queue and
subsequent pumping or other interactions (not illustrated but true) that
cause the pythoncom message queue to be processed will cause the associated
On-event routines to execute.  

FWIW, if the test is run again the number and of events here will vary since
the processes (the test and IE) are entirely separate.

At 32.419 when the main spins for 10 seconds NO On-event routines are
executed.  This verifies and supports the NO EVENTS UNLESS PROPERTIES OR
METHODS rule.

At 42.429 when the main does another pump there are no pending events.
FWIW, in other runs of this test occasionally there will be another event or
two reflecting the flow of the separate test and IE processes.

At 47.460 when the main starts pumping messages for 30 seconds events start
to occur and the IE window begins to display.  The IE window does NOT paint
before then even though some 40+ seconds have elapsed.  Clearly IE is 'hung'
waiting disposition of some events.  It should be noted that in the case of
IE some events, for example NewWindow2, expect a return value so that IE can
be expected to (and as far as I know does) wait for the event to be
processed before proceeding.  While it is not clear what event this
particular Navigate2 is waiting on, it is clearly waiting since subsequent
to starting to pump messages the page displays in roughly another 17
seconds.  Clearly, in a DispatchWithEvents world when events are caught they
MUST be pumped to allow the correspondent process (IE) to proceed.  Absent
pumping the correspondent can 'hang' waiting for the event to be processed.

At 97.461 subsequent pumping yields NO events.  This page is now stable.  It
should be noted that this particular stable behavior is a function of this
particular page and NOT a general characteristic of IE or COM.  

It is entirely possible for HTML pages to go off and do whatever whenever
and cause events.  For instance, on this page in particular the right hand
frame paints and a topmost window document complete event occurs (not
illustrated in this example, but an indication that all the bytes from the
original navigation request are now received) before all events have
occurred and the page is completely rendered.  On this page, when IE begins
to process the page after the topmost window document complete event occurs
there is a bit of script attached to the pages OnLoad event that runs to
build the left hand frame.  This script goes back to the web to get the left
frame contents and generates a stream of events that occur after the initial
navigation is (in some sense) complete.  This illustrates that a page CAN go
off and generate events without interaction from the main automation
routine.  While on this page the script is triggered by the pages OnLoad
event, it is entirely possible that on page script will wait around a bit or
be attached to some other page feature and then start to do things that
generate events.  A slight modification of this test that stops pumping
after the topmost window document complete event will leave the left frame
NOT displayed until messages are once again pumped.



--- Begin Code ---
import win32com.client
import pythoncom
import time

class events():
    def OnVisible(self, vis):
        self.eventMsg( 'OnVisible is %s'%(vis))
    def OnBeforeNavigate2(self,pDisp,url,flags,
                          targetFrameName,postData,
                          headers,cancel):
        self.eventMsg( "OnBeforeNavigate2 on url [%s] on frame [%s]"% 
              (url, targetFrameName))
    def OnCommandStateChange(self,
                             Command,
                             Enable):
        self.eventMsg( 'OnCommandStateChange: %s %s'%(Command,Enable))
    def OnProgressChange(self, nProgress, nProgressMax):
        msg = 'OnProgressChange progress %d max %d' % (
            nProgress, nProgressMax) 
        self.eventMsg( msg )
    def OnStatusBar(self,
                      status):
        self.eventMsg('OnStatusBar')
    def eventMsg( self, msg ):
        print 'At %6.3f Event tag [%s] %s'%(time.clock(), tag, msg)

tag = 'No tag'
def mainMsg( t, msg = ''):
    global tag
    tag = t
    print 'At %6.3f Main  tag [%s] %s'%(time.clock(), t, msg)
    return

def spin( t, msg ):
    mainMsg( 'Spin for %s seconds, no pump'%t, msg)
    t0 = time.clock()
    while time.clock()-t0 < t: pass

def spinPump( t, msg ):
    mainMsg( 'Pump %s seconds'%t, msg)
    t0 = time.clock()
    while time.clock()-t0 < t: pythoncom.PumpWaitingMessages()
    
time.clock()        # just get clock running
mainMsg( 'Begin test' )
mainMsg( 'win32com.client.DispatchWithEvents' )
ie = win32com.client.DispatchWithEvents(
    'InternetExplorer.Application',
    events)
# NO PUMP MESSAGE
mainMsg( 'Set ie.Visible', 'On-event routines run')
ie.Visible = 1
mainMsg( 'Set ie.StatusBar false', 'On-event routines run' )
ie.StatusBar = 0
mainMsg( 'Set ie.StatusBar true', 'On-event routines run' )
ie.StatusBar = 1
mainMsg( 'Navigate to ... aa752574.aspx',
         'OnBeforeNavigate2 event routine runs' )
url = 'http://msdn2.microsoft.com/en-us/library/aa752574.aspx'
ie.Navigate(url)
mainMsg( 'Back from Navigate', 'The page should NOT be displayed')
spin(30, 'The page should not display and there should be no events' )
mainMsg( 'One pump',
         'The page should still NOT be displayed, ' + \
         'but we should see a few messages and ' + \
         'IE is hung waiting for events to be processed' )
pythoncom.PumpWaitingMessages()
spin( 10, 'The page should still NOT be displayed, ' + \
      'there should be no events and '
      'IE is hung waiting for events to be processed' )
mainMsg( 'Another pump',
         'The page should still NOT be displayed, ' + \
         'but we may see a few more messages and ' + \
         'IE is still hung waiting for events to be processed' )
pythoncom.PumpWaitingMessages()
spin( 5, 'The page should still NOT be displayed, ' + \
      'there should be no events and '
      'IE is hung waiting for events to be processed' )
spinPump( 30, 'The page should NOW start displaying')
spinPump( 20, 'The page should be stable and there should be no trailing
events' )
mainMsg( 'End test' )
--- End Code ---

--- Begin Output ---
At  0.000 Main  tag [Begin test]
At  0.000 Main  tag [win32com.client.DispatchWithEvents]
At  2.185 Main  tag [Set ie.Visible] On-event routines run
At  2.189 Event tag [Set ie.Visible] OnCommandStateChange: 2 False
At  2.272 Event tag [Set ie.Visible] OnVisible is True
At  2.272 Main  tag [Set ie.StatusBar false] On-event routines run
At  2.275 Event tag [Set ie.StatusBar false] OnCommandStateChange: 1 False
At  2.275 Event tag [Set ie.StatusBar false] OnStatusBar
At  2.275 Main  tag [Set ie.StatusBar true] On-event routines run
At  2.321 Event tag [Set ie.StatusBar true] OnStatusBar
At  2.324 Main  tag [Navigate to ... aa752574.aspx] OnBeforeNavigate2 event
routine runs
At  2.328 Event tag [Navigate to ... aa752574.aspx] OnCommandStateChange: 2
False
At  2.353 Event tag [Navigate to ... aa752574.aspx] OnBeforeNavigate2 on url
[http://msdn2.microsoft.com/en-us/library/aa752574.aspx] on frame []
At  2.400 Main  tag [Back from Navigate] The page should NOT be displayed
At  2.401 Main  tag [Spin for 30 seconds, no pump] The page should not
display and there should be no events
At 32.413 Main  tag [One pump] The page should still NOT be displayed, but
we should see a few messages and IE is hung waiting for events to be
processed
At 32.413 Event tag [One pump] OnCommandStateChange: 1 False
At 32.416 Event tag [One pump] OnCommandStateChange: 2 False
At 32.417 Event tag [One pump] OnCommandStateChange: 1 False
At 32.419 Main  tag [Spin for 10 seconds, no pump] The page should still NOT
be displayed, there should be no events and IE is hung waiting for events to
be processed
At 42.429 Main  tag [Another pump] The page should still NOT be displayed,
but we may see a few more messages and IE is still hung waiting for events
to be processed
At 42.429 Main  tag [Spin for 5 seconds, no pump] The page should still NOT
be displayed, there should be no events and IE is hung waiting for events to
be processed
At 47.460 Main  tag [Pump 30 seconds] The page should NOW start displaying
At 47.461 Event tag [Pump 30 seconds] OnProgressChange progress 100 max
10000
At 47.484 Event tag [Pump 30 seconds] OnProgressChange progress 100 max
10000
At 47.655 Event tag [Pump 30 seconds] OnProgressChange progress 150 max
10000
At 47.657 Event tag [Pump 30 seconds] OnProgressChange progress 200 max
10000
At 47.660 Event tag [Pump 30 seconds] OnProgressChange progress 250 max
10000
At 47.661 Event tag [Pump 30 seconds] OnProgressChange progress 300 max
10000
At 47.666 Event tag [Pump 30 seconds] OnProgressChange progress 350 max
10000
At 47.667 Event tag [Pump 30 seconds] OnProgressChange progress 400 max
10000
At 47.667 Event tag [Pump 30 seconds] OnProgressChange progress 450 max
10000
At 47.668 Event tag [Pump 30 seconds] OnProgressChange progress 500 max
10000
At 47.669 Event tag [Pump 30 seconds] OnProgressChange progress 550 max
10000
At 47.671 Event tag [Pump 30 seconds] OnProgressChange progress 600 max
10000
At 47.700 Event tag [Pump 30 seconds] OnProgressChange progress 666800 max
1000000
At 48.256 Event tag [Pump 30 seconds] OnCommandStateChange: -1 False
At 48.447 Event tag [Pump 30 seconds] OnCommandStateChange: 2 False
At 48.448 Event tag [Pump 30 seconds] OnCommandStateChange: 1 False
At 48.497 Event tag [Pump 30 seconds] OnCommandStateChange: -1 False
At 48.654 Event tag [Pump 30 seconds] OnProgressChange progress 1060000 max
1000000
At 54.270 Event tag [Pump 30 seconds] OnProgressChange progress 1000000 max
1000000
At 54.271 Event tag [Pump 30 seconds] OnProgressChange progress -1 max
1000000
At 54.286 Event tag [Pump 30 seconds] OnProgressChange progress 1000000 max
1000000
At 54.584 Event tag [Pump 30 seconds] OnBeforeNavigate2 on url
[http://msdn2.microsoft.com/en-us/library/aa752574(d=toc).aspx] on frame []
At 54.610 Event tag [Pump 30 seconds] OnProgressChange progress 1000000 max
10000
At 54.616 Event tag [Pump 30 seconds] OnCommandStateChange: -1 False
At 54.769 Event tag [Pump 30 seconds] OnProgressChange progress 0 max 0
At 56.209 Event tag [Pump 30 seconds] OnCommandStateChange: 2 False
At 56.210 Event tag [Pump 30 seconds] OnCommandStateChange: 1 False
At 57.316 Event tag [Pump 30 seconds] OnProgressChange progress 70800 max
1000000
At 64.288 Event tag [Pump 30 seconds] OnProgressChange progress 1000000 max
1000000
At 64.345 Event tag [Pump 30 seconds] OnProgressChange progress -1 max
1000000
At 64.526 Event tag [Pump 30 seconds] OnCommandStateChange: -1 False
At 64.854 Event tag [Pump 30 seconds] OnProgressChange progress 0 max 0
At 77.460 Main  tag [Pump 20 seconds] The page should be stable and there
should be no trailing events
At 97.461 Main  tag [End test]

C:\rbell\Bell Curve Group\IE Testing>
--- End Output ---




More information about the Python-win32 mailing list