分享

[python-win32] DispatchWithEvents design ques...

 weicat 2011-04-19

[python-win32] DispatchWithEvents design question

Richard Bell rbell01824 at earthlink.net
Tue 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

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多