[python-win32] DispatchWithEvents design questionRichard Bell rbell01824 at earthlink.netTue Jun 5 19:38:36 CEST 2007
I'm continuing to work on a class to automate IE in an apartment-threaded early binding class. The current design is relatively conventional and looks like this: class Yie(object): def __init__(self, eventClass): --- code deleted --- self._ie = win32com.client.DispatchWithEvents( "InternetExplorer.Application", YieEvents) self._ie.Visible = 1 _yamies[self._ie.HWND] = self --- code deleted --- --- class methods that automate IE --- class YieEvents(object): --- class methods that respond to IE events --- It is necessary for several reasons for the event class event handlers to occasionally reference attributes and methods in the Yie instance holding self._ie. To support this requirement I keep a dictionary of Yie instances indexed by self._ie.HWND which is known to self._ie and its YieEvents On-event handlers. But there is a catch. When DispatchWithEvents creates self._ie it first initializes the IE com object, then an internal object, and finally YieEvents. Depending on timing, it is possible for IE to start throwing events BEFORE control returns to the Yie's __init__ routine and BEFORE _yamies is set. When this occurs the event routines are unable to find the Yie instance they are servicing. As a purely practical matter, this doesn't really cause much difficulty because the early events are OnVisible and OnCommandStateChange both of which can be safely ignored as IE comes up. Even so, I'm troubled by the behavior. In casting around for a solution I poked into the code for DispatchWithEvents. If I understand it correctly, it is possible to include IE automation methods as part of the event class something like this: class Yie: --- class variables --- def __init__(self): --- instance variables --- def NavigateWait1( self, url): --- code to navigate and wait for completion --- --- methods to provide other IE automation services --- def OnVisible(self, vis): --- code to handle OnVisible event --- --- methods to handle other IE events --- Using this design it is possible to use the class something like this: ie = win32com.client.DispatchWithEvents( 'InternetExplorer.Application',Yie) ie.Visible=True ie.NavigateWait1('www.google.com') Since the revised Yie class holds BOTH the event and automation methods the need for the dictionary goes away and the On-event routines can reference whatever instance attributes/methods are needed. A bit of experimentation shows that this does indeed work (if anyone is interested, I'll gladly post the code). Question 1: Is this a good design/idea/advisable? I'm still learning about COM automation and there are many that understand it rather better than I. What do you think? One of the things that has to happen for this to work is that a navigating IE automation method (Navigate2, GoBack, etc.) MUST pump messages or IE hangs. The net of this is that the IE automation code to support navigation needs to look something like this: def NavigateWait1( self, url, timeout): startTime = time.time() self.Navigate2( url ) while True: pythoncom.PumpWaitingMessages() rc = win32event.MsgWaitForMultipleObjects( (self._eventNone,), 0, 250, win32event.QS_ALLEVENTS) if (rc == win32event.WAIT_TIMEOUT): if time.clock() - startTime >= timeout: return During navigation the while loop pumps messages every 250 MS. As events occur the event routines are invoked and do whatever they do including, possibly, changing attributes within the IE automation instance. Eventually, the timeout elapses and the while loop terminates. But ... Question 2: Is this safe? Python source gets translated into byte code. I understand that a single byte code is atomic and can not be interrupted (is this true?). But how about a statement? There are a number of places in the automation code where several statements have to execute entirely or the internal state of the automation instance is inconsistent. Do I need to protect these with a Mutex? Alternately, can I depend on pythoncom.PumpWaitingMessages to be atomic such that the event routines CAN ONLY occur while the while loop is invoking PumpWaitingMessages? One of the things the IE automation class needs to deal with is an IE NewWindow event. The nub of this is that when a page wants to generate a new window is causes an OnNewWindow3 event. The event has the following template: def OnNewWindow3(self, ppDisp=defaultNamedNotOptArg, cancel=defaultNamedNotOptArg, dwFlags=defaultNamedNotOptArg, bstrUrlContext=defaultNamedNotOptArg, bstrUrl=defaultNamedNotOptArg): The event routine can return nothing, in which case IE does whatever the browser's configuration defines. Alternately, the event routine can return True to cancel the NewWindow request (see http://msdn2.microsoft.com/en-us/library/aa768288.aspx). Finally, the event routine can return the IDispatch interface pointer of an InternetExplorer object that will host the new window. This latter bit means that the event routine must instantiate a new instance something like this: new_ie = win32com.client.DispatchWithEvents( 'InternetExplorer.Application',Yie) Based on my knowledge of Python, I think this should work just fine. It is, after all, just a method in a class creating a new instance of the class. Some experimentation shows that the OnNewWindow3 event routine can do this: new_ie = win32com.client.DispatchWithEvents( 'InternetExplorer.Application',Yie) return new_ie, false and the new IE instance is created. But I'm a bit concerned that the new IE may start generating events BEFORE I return from the original IE's NewWindow3 event routine! That is, after all, the problem this new design is intended to address. If it does, this means that the Python COM routines can potentially be re-entered while processing an event. Alternately, does PumpWaitingMessages insure that at most one message at a time occurs so that the NewWindow3 event routine is guaranteed to complete BEFORE another event on the original or new IE can occur? Question 3: Does PumpWaitingMessages insure that at MOST one message/event is pumped such that reentrancy is not an issue? My apologies for the length of this post but I wanted to be as clear as possible in hopes that I can get some feedback. I appreciate that these are rather subtle questions. If I'm able to satisfy myself on the answers, I'll gladly provide a demo to add to the distribution for the benefit of others. Thanks in advance for any help. Regards, Richard |
|