[python-win32] DispatchWithEvents design question

Richard Bell rbell01824 at earthlink.net
Thu Jun 7 17:33:39 CEST 2007



Tim Roberts wrote
|These are synchronous events.  That is, these events are generated
|directly by the requests you made.  I believe you will find that the
|"OnVisible" callback occurs before "ie.Visible = 1" returns.  It's being
|handled by that same thread, which is suspended waiting for IE to
|finish.  A message pump is only required for events that arrive
|asynchronously.  It's still single threaded.
|
|If you're familiar with the Windows GUI, this is similar to the
|difference between PostMessage and SendMessage (ignoring interprocess
|calls for now).  SendMessage calls directly into the window procedure of
|the target window without going through the message queue; when it
|returns, the message is handled.  PostMessage is fire-and-forget; it
|will be processed later, when the message loop gets around to pulling
|the message.


That clears up a lot.  Thanks.

I rewrote the test, see below.  When run it behaves as you suggest (see the
test output below).  In particular the OnVisible event occurs synchronously
before return from 'ie.Visible = 1' as you suggested.  I checked StatusBar
and it appears to be synchronous as well.  I assume then that any events
associated with setting IE's properties would also be synchronous, wouldn't
they?  

But note that when I set StatusBar false the OnStatusBar event occurs.  Then
when I set StatusBar true both OnCommandStateChange and OnStatusBar events
occur.  I expected the OnStatusBar event but NOT the OnCommandStateChange
event as later in the test (after 21.407) OnCommandStateChange behaves
asynchronously.  Is there some bit of behavior such that when a synchronous
event occurs other queued events are also processed?  About a year ago, Mark
Hammond helped me in this area and memory serves that when any events are
processed they are all processed.  Is this true?  Is this behavior a
function of COM or of the Python COM interface?  

More generally, how do I know what COM interactions will generate
synchronous events and which will cause asynchronous events?  

Interestingly if I comment out the code starting at line 47 (the trailing
PUMP for the first navigation) I get a pythoncom error and trace like this:

--- begin output --- 

Spin for 20 seconds
pythoncom error: Python error invoking COM method.

Traceback (most recent call last):
  File "C:\Python25\Lib\site-packages\win32com\server\policy.py", line 285,
in _Invoke_
    return self._invoke_(dispid, lcid, wFlags, args)
  File "C:\Python25\Lib\site-packages\win32com\server\policy.py", line 290,
in _invoke_
    return S_OK, -1, self._invokeex_(dispid, lcid, wFlags, args, None, None)
  File "C:\Python25\Lib\site-packages\win32com\server\policy.py", line 588,
in _invokeex_
    return func(*args)
  File "C:\rbell\Bell Curve Group\IE Testing\test070607a.py", line 17, in
OnCommandStateChange
    print 'At %6.3f OnCommandStateChange: %s
%s'%(time.clock(),Command,Enable)
<type 'exceptions.AttributeError'>: 'NoneType' object has no attribute
'clock'

--- end output ---

At that point IE is still running and as the test is coming down there
appears to be a window where events start flowing into the event routines
but there is not enough of the Python/test application still available to
handle them.  I'm not sure that this is a bug or just an unfortunate
boundary condition.  Based on what I think I understand about COM automation
I'm inclined to think that this condition can occur on any COM interface
with event support.

At any event thanks again for your input.  It was very helpful.

FWIW, anyone looking at this code should know that it is NOT best/good/OK
practice.  It's just something I hacked together to explore this issue and
should NOT be used as a base for anything but experimentation.

--- begin code ---
# test events WITHOUT a message pump
import win32com.client
import pythoncom
import time

class events():
    def OnVisible(self, vis):
        print 'At %6.3f OnVisible is %s'%(time.clock(),vis)
    def OnBeforeNavigate2(self,pDisp,url,flags,
                          targetFrameName,postData,
                          headers,cancel):
        print "At %6.3f OnBeforeNavigate2 on url [%s] on frame [%s]"% \
              (time.clock(), url, targetFrameName)
    def OnCommandStateChange(self,
                             Command,
                             Enable):
        print 'At %6.3f OnCommandStateChange: %s %s'% \
              (time.clock(),Command,Enable)
    def OnProgressChange(self, nProgress, nProgressMax):
        msg = 'At %6.3f OnProgressChange progress %d max %d' % (
            time.clock(),nProgress, nProgressMax) 
        print msg
    def OnStatusBar(self,
                      status):
        msg = 'At %6.3f OnStatusBar %s'%(time.clock(),status)
        print msg
        
time.clock()        # just get clock running
print 'At %6.3f win32com.client.DispatchWithEvents'%time.clock()
ie = win32com.client.DispatchWithEvents(
    'InternetExplorer.Application',
    events)
# NO PUMP MESSAGE
print 'At %6.3f set ie.Visible'%time.clock()
ie.Visible = 1
print 'At %6.3f set ie.StatusBar false'%time.clock()
ie.StatusBar = 0
print 'At %6.3f set ie.StatusBar true'%time.clock()
ie.StatusBar = 1
print 'At %6.3f Navigate to
http://msdn2.microsoft.com/en-us/library/aa752574.aspx'%time.clock()
url = 'http://msdn2.microsoft.com/en-us/library/aa752574.aspx'
ie.Navigate(url)
print 'At %6.3f back from Navigate'%time.clock()
print 'The page should NOT be displayed'
print 'Spin for 20 seconds'
t0 = time.clock()
while time.clock()-t0 < 20: pass
print 'At %6.3f pump any trailing messages'%time.clock()
print 'The page should NOW display'
t0 = time.clock()
while time.clock()-t0 < 20: pythoncom.PumpWaitingMessages()

# PUMP MESSAGES
print '*'*50
print 'At %6.3f Navigate WITH PUMP to
http://msdn2.microsoft.com/en-us/library/aa752514.aspx'%time.clock()
url = 'http://msdn2.microsoft.com/en-us/library/aa752514.aspx'
ie.Navigate(url)
print 'At %6.3f back from Navigate and PUMPING'%time.clock()
t0 = time.clock()
while time.clock()-t0 < 20: pythoncom.PumpWaitingMessages()
print 'At %6.3f terminate'%time.clock()
--- end code ---

--- begin complete test output ---
At  0.000 win32com.client.DispatchWithEvents
At  1.250 set ie.Visible
At  1.299 OnVisible is True
At  1.305 set ie.StatusBar false
At  1.324 OnStatusBar False
At  1.329 set ie.StatusBar true
At  1.343 OnCommandStateChange: 2 False
At  1.355 OnStatusBar True
At  1.356 Navigate to http://msdn2.microsoft.com/en-us/library/aa752574.aspx
At  1.358 OnCommandStateChange: 1 False
At  1.374 OnBeforeNavigate2 on url
[http://msdn2.microsoft.com/en-us/library/aa752574.aspx] on frame []
At  1.407 back from Navigate
The page should NOT be displayed
Spin for 20 seconds
At 21.408 pump any trailing messages
The page should NOW display
At 21.408 OnCommandStateChange: 2 False
At 21.409 OnCommandStateChange: 1 False
At 21.444 OnProgressChange progress 0 max 10000
At 21.715 OnCommandStateChange: -1 False
At 21.791 OnCommandStateChange: 2 False
At 21.798 OnCommandStateChange: 1 False
At 21.849 OnCommandStateChange: -1 False
At 21.942 OnProgressChange progress 50 max 10000
At 22.441 OnProgressChange progress 100 max 10000
At 22.471 OnProgressChange progress 258300 max 1000000
At 22.472 OnProgressChange progress 272200 max 1000000
At 22.486 OnProgressChange progress 339200 max 1000000
At 22.494 OnProgressChange progress 422900 max 1000000
At 22.557 OnProgressChange progress 1006800 max 1000000
At 27.806 OnProgressChange progress 1000000 max 1000000
At 27.807 OnProgressChange progress -1 max 1000000
At 27.824 OnProgressChange progress 1000000 max 1000000
At 28.115 OnBeforeNavigate2 on url
[http://msdn2.microsoft.com/en-us/library/aa752574(d=toc).aspx] on frame []
At 28.138 OnProgressChange progress 1000000 max 10000
At 28.167 OnCommandStateChange: -1 False
At 28.306 OnProgressChange progress 0 max 0
At 29.552 OnProgressChange progress 54300 max 1000000
At 29.555 OnProgressChange progress 129300 max 1000000
At 29.557 OnProgressChange progress 214900 max 1000000
At 29.560 OnProgressChange progress 338300 max 1000000
At 29.584 OnProgressChange progress 449600 max 1000000
At 29.587 OnProgressChange progress 476400 max 1000000
At 29.594 OnProgressChange progress 554300 max 1000000
At 29.820 OnCommandStateChange: 2 False
At 29.820 OnCommandStateChange: 1 False
At 37.530 OnProgressChange progress 1000000 max 1000000
At 37.531 OnProgressChange progress -1 max 1000000
At 37.672 OnCommandStateChange: -1 False
At 38.053 OnProgressChange progress 0 max 0
**************************************************
At 41.408 Navigate WITH PUMP to
http://msdn2.microsoft.com/en-us/library/aa752514.aspx
At 41.414 back from Navigate and PUMPING
At 41.430 OnBeforeNavigate2 on url
[http://msdn2.microsoft.com/en-us/library/aa752514.aspx] on frame []
At 41.600 OnProgressChange progress 0 max 10000
At 41.607 OnCommandStateChange: -1 False
At 42.097 OnProgressChange progress 50 max 10000
At 42.212 OnProgressChange progress 11000 max 1000000
At 42.335 OnCommandStateChange: -1 False
At 42.479 OnCommandStateChange: 2 True
At 42.480 OnCommandStateChange: 1 False
At 42.485 OnCommandStateChange: -1 False
At 42.791 OnProgressChange progress 287700 max 1000000
At 42.803 OnProgressChange progress 465400 max 1000000
At 42.806 OnProgressChange progress 995300 max 1000000
At 46.091 OnProgressChange progress 1000000 max 1000000
At 46.092 OnProgressChange progress -1 max 1000000
At 46.102 OnProgressChange progress 1000000 max 1000000
At 46.400 OnBeforeNavigate2 on url
[http://msdn2.microsoft.com/en-us/library/aa752514(d=toc).aspx] on frame []
At 46.425 OnProgressChange progress 1000000 max 10000
At 46.431 OnCommandStateChange: -1 False
At 46.590 OnProgressChange progress 0 max 0
At 47.680 OnProgressChange progress 96400 max 1000000
At 47.684 OnProgressChange progress 171000 max 1000000
At 47.686 OnProgressChange progress 249300 max 1000000
At 47.700 OnProgressChange progress 424700 max 1000000
At 47.886 OnCommandStateChange: 2 True
At 47.887 OnCommandStateChange: 1 False
At 54.426 OnProgressChange progress 1000000 max 1000000
At 54.434 OnProgressChange progress -1 max 1000000
At 54.611 OnCommandStateChange: -1 False
At 54.955 OnProgressChange progress 0 max 0
At 61.423 terminate
--- end complete test output ---



More information about the Python-win32 mailing list