com how to PumpWaitingMessages in a thread

puff rbell01824 at earthlink.net
Mon Feb 6 13:12:06 EST 2006


When interfacing to a COM object, is it possible to pump messages in a
thread?

I'm working on an application that automates IE and needs to monitor
IE events (yes I know about Pamie).  I'm able to start IE as follows:

    ie = win32com.client.DispatchWithEvents( object, YamieEvents )
    ie.event = win32event.CreateEvent(None,0,0,None)

An earlier post by Mark Hammond suggested the ie.event line as a fix to
an issue that seems to remain an issue in the current release.

The class YamieEvents catches a number of events and outputs trace
messages whenever an event occurs.  It looks like this:

class YamieEvents:

    def OnBeforeNavigate2(self,
                          pDisp=defaultNamedNotOptArg,
                          url=defaultNamedNotOptArg,
                          Flags=defaultNamedNotOptArg,
                          TargetFrameName=defaultNamedNotOptArg,
                          PostData=defaultNamedNotOptArg,
                          Headers=defaultNamedNotOptArg,
                          Cancel=defaultNamedNotOptArg):
        out = 'OnBeforeNavigate2 URL [%s] \n' % url
        out += '          Flags [%s] \n' %`Flags`
        out += '          TargetFrameName [%s]' % TargetFrameName
        print  out;
    # many more On event routines

I'm able to navigate and wait as follows seeing the event trace:

    ie.Navigate(url)
    try:
        ie.Navigate(url)
        WaitMsg()
    except pythoncom.com_error, details:
        PrintFlush( "Warning - could not open %s"%url, details )

Where WaitMsg() looks like this:


def WaitMsg(timeout=30, donetime=2000): # timeout in seconds, dontime
in milliseconds!
    timeStart = time.time()
    timeTic = timeStart
    while True:
        rc = win32event.MsgWaitForMultipleObjects(
            (ie.event,), 0, donetime, win32event.QS_ALLEVENTS)
        if rc == win32event.WAIT_OBJECT_0:
            pass
        elif rc == win32event.WAIT_OBJECT_0+1:
            pythoncom.PumpWaitingMessages()
        elif rc == win32event.WAIT_TIMEOUT:
            PrintFlush( ' WaitMsg: got donetime' )
            return True
        else:
            PrintFlush( 'Unrecognized event' )
        timeNow = time.time()
        if timeNow - timeStart > timeout:
            PrintFlush( ' ##### got timeout' )
            return False
        if timeNow - timeTic > 1:
            PrintFlush( '.', )
            timeTic = timeNow

So far everything seems to work fine.

However, I've encountered a difficulty when dealing with web pages
that have script that causes events AFTER the navigation is complete!
Since the message pump is not being run, these events are not processed
and IE appears to be hung (the script's page changes, navigations,
etc. do no occur).  I'm trying to work out how to run the message
loop on a thread so that messages are continually pumped and these
script modified pages are properly handled.

Modeling a solution after Mark Hammond's Appendix D threads example
I'm creating the message pump thread as follows:

    ie =
win32com.client.DispatchWithEvents("InternetExplorer.Application", \
                                            YamieEvents)
    ie.event = win32event.CreateEvent(None,0,0,None)
    ie.Visible = 1
    WaitMsg()
    # now IE is up and google is displayed, there is good reason to
think IE
    # is good and truely ready to be used
    #pass our ie com object to a thread to run the message loop
    # marshal the object
    object_stream = pythoncom.CoMarshalInterThreadInterfaceInStream( \
            pythoncom.IID_IDispatch, ie )
    args = (object_stream,)
    handle, id = win32process.beginthreadex( None, 0, PumpMessages,
args, 0 )

The thread function looks like this:

def PumpMessages(object_stream):
    pythoncom.CoInitialize()    # initialize com for single thread
    # unmarshal the DispatchWithEvents object
    object = pythoncom.CoGetInterfaceAndReleaseStream( \
        object_stream, pythoncom.IID_IDispatch)
    # convert to a useable DispatchWithEvents object
    ie = win32com.client.DispatchWithEvents( object, YamieEvents )
    # without this line, MsgWaitForMultipleObjects reports no attribute
event!?
    ie.event2 = win32event.CreateEvent(None,0,0,None)
    # now we should be in a position wait for events and pump messages
    timeStart = time.time()
    while True:
        rc = win32event.MsgWaitForMultipleObjects(
            (ie.event2,), 0, 1000, win32event.QS_ALLEVENTS)
        if rc == win32event.WAIT_OBJECT_0:
            PrintFlush( ' thread: event' )
        elif rc == win32event.WAIT_OBJECT_0+1:
            PrintFlush( ' thread: pump' )
            pythoncom.PumpWaitingMessages()
        elif rc == win32event.WAIT_TIMEOUT:
            PrintFlush( ' thread: tic' )
        else:
            PrintFlush( ' thread: unrecognized event' )
        if time.time() - timeStart > 40:
            PrintFlush( ' thread: got timeout' )
            break
    # be a good citizen and cleanup self
    ie = None
    pythoncom.CoUninitialize()

I've a question about this code since it was necessary to create a
second event for the ie object even though the original object and
it's event should (I believe) be available.  Am I doing something
wrong here?

Second when I test this code I see that the thread runs ( trace
messages thread: tic occur).  But events are not being pumped since
none of the event messages occur.  Eventually the thread times out.

Any clues as to what I'm missing?

I appreciate that this is a lengthy post.  I've tried to show only
the critical sections of code but the issue is complex.

Thanks for any help.




More information about the Python-list mailing list