Handling Com Events in Python (was Python and Windows Scripting Host)

Alex Martelli alex at magenta.com
Thu Aug 10 05:59:09 EDT 2000


"Syver Enstad" <syver.enstad at sensewave.com> wrote in message
news:8msq5k$tn6$2 at troll.powertech.no...
> > Every file built by genpy has the following lines towards the start:
> >
> > # The following 3 lines may need tweaking for the particular server
> > # Candidates are pythoncom.Missing and pythoncom.Empty
> > defaultNamedOptArg=pythoncom.Missing
> > defaultNamedNotOptArg=pythoncom.Missing
> > defaultUnnamedArg=pythoncom.Missing
>
> I've tried all the combinations of pythoncom.Missing and Empty (example
> below), none works. Maybe Python doesn't support the datatypes in the
> parameters or something.

I don't think the default-value you set for event arguments is important
in this context.  There must be something else (and I don't know what!).

> The URL parameter is a VARIANT* as far as I can remember. Maybe that is
> causing the trouble.

Nah; they're all that way.  Typical of event-source dispinterfaces.


Anyway, I enriched your example so as to allow me to monitor several
events (many of which I don't get though I think I should) and watch for
changes in the ReadyState property (which goes to 3 when the document
is ready for interaction, to 4 when it's complete); here's this version:

#
#   Ie test
#
from win32com.client import constants, DispatchWithEvents
from pythoncom import com_error
from time import sleep

class EventHandler:
    def __init__(self):
        self.ors=-2
    def __rsc(self):
        print " ReadyState:",self.ors,'->',self.ReadyState
        self.ors=self.ReadyState
    def __crs(self):
        try:
            if self.ReadyState!=self.ors:
                self.__rsc()
            else:
                print
        except AttributeError:
            self.ors=-33
            self.__rsc()
    def OnVisible(self, visible):
        self.hasquit=0
        print "Visible now =",visible,
        self.__crs()
    def OnDownloadBegin(self):
        print "DownloadBegin",
        self.__crs()
    def OnDownloadComplete(self):
        print "DownloadComplete",
        self.__crs()
    def OnQuit(self):
        self.hasquit=1
        print "IE has quit",
        self.__crs()
    def OnNavigateComplete2(self, pDisp=None, URL=None):
        print "NavigateComplete2",URL or "None",
        self.__crs()
    def OnDocumentComplete(self, pDisp=None, URL=None):
        print "DocumentComplete",URL or "None",
        self.__crs()
    def OnNavigateComplete(self, URL=None):
        print "NavigateComplete",URL or "None",
        self.__crs()
    def OnBeforeNavigate(self, URL=None, Flags=None, TargetFrameName=None,
PostData=None, Headers=None, Cancel=None):
        print "BeforeNavigate",URL or "None",
        self.__crs()
    def OnBeforeNavigate2(self, pDisp=None, URL=None, Flags=None,
TargetFrameName=None, PostData=None, Headers=None, Cancel=None):
        print "BeforeNavigate2",URL or "None",
        self.__crs()
    def OnTitleChange(self, Text=None):
        print "TitleChange",Text or "None",
        self.__crs()
    def OnStatusTextChange(self, Text=None):
        print "StatusTextChange",Text or "None",
        self.__crs()
    def OnPropertyChange(self, szProperty=None):
        print "PropertyChange",szProperty or "None",
        self.__crs()

ie = DispatchWithEvents("InternetExplorer.Application", EventHandler)
ie.Visible = 1
print "Going to Navigate..."
ie.Navigate("http://www.python.org/index.html")

while ie.Visible:
    sleep(0.01)

print "IE not visible any more"

tries=10
while tries>0 and not ie.hasquit:
    try:
        print "Trying to quit"
        ie.Quit()
        print "OK!"
    except com_error, ex:
        print "Retry after",ex
        sleep(1)
        tries=tries-1

print "Program is over"


And here's the output on a visit to www.python.org with a single
clic on the 'python 1.6' link followed by quitting IE:

Visible now = 1  ReadyState: -33 -> 0
Going to Navigate...
PropertyChange {265b75c1-4158-11d0-90f6-00c04fd497ea}  ReadyState: 0 -> 1
DownloadBegin
PropertyChange {D0FCA420-D3F5-11CF-B211-00AA004AE837}
StatusTextChange Connecting to site 193.43.43.94
StatusTextChange Connecting to site www.python.org
StatusTextChange Start downloading from site:
http://www.python.org/index.html
StatusTextChange Downloading from site: http://www.python.org/index.html
DownloadComplete
StatusTextChange None
DownloadBegin
StatusTextChange Unknown Zone
StatusTextChange None
StatusTextChange Opening page http://www.python.org/index.html...
TitleChange Python Language Website
StatusTextChange Done  ReadyState: 1 -> 3
DownloadComplete
TitleChange Python Language Website
StatusTextChange Internet  ReadyState: 3 -> 4
StatusTextChange Done
StatusTextChange Downloading from site: http://www.python.org/index.html
StatusTextChange http://www.python.org/
StatusTextChange Downloading from site: http://www.python.org/index.html
StatusTextChange http://www.python.org/
StatusTextChange Downloading from site: http://www.python.org/index.html
StatusTextChange None
StatusTextChange http://www.python.org/1.6/
StatusTextChange http://www.python.org/1.6/
StatusTextChange http://www.python.org/1.6/
DownloadBegin
TitleChange Python Language Website
PropertyChange {265b75c0-4158-11d0-90f6-00c04fd497ea}
PropertyChange {265b75c1-4158-11d0-90f6-00c04fd497ea}
PropertyChange {118D6040-8494-11d2-BBFE-0060977B464C}
PropertyChange {04AED800-8494-11d2-BBFE-0060977B464C}
StatusTextChange None
StatusTextChange Downloading picture
http://www.python.org/pics/PyBanner051.gif...  ReadyState: 4 -> 3
StatusTextChange Done
DownloadComplete
TitleChange Python 1.6
StatusTextChange Internet  ReadyState: 3 -> 4
StatusTextChange Done
StatusTextChange Done
StatusTextChange Done
StatusTextChange Done
StatusTextChange Done
StatusTextChange Done
StatusTextChange Done
IE has quit
IE not visible any more
Program is over

The first line tells us that __init__ is not called (oh well; remember
to initialize whatever fields you need in the mixed-object right
after DispatchWithEvents creates it).  Then, later, we have exactly
one readystate change to 4 per visited document -- but no call
is ever seen to OnDocumentComplete (or any of the other very
relevant methods such as the OnBefore... -- I've put some events
in from the older web-browser events interface just in case, but
it doesn't seem to be that).

Oh well, we can 'synthesize' the document-completion by watching
for the readystate to become 4 -- add a method:

    def FakeDocumentComplete(self, pDisp=None, URL=None):
        print "Fake DocumentComplete",URL or "None"
        print "Document title is",pDisp.Title

and change the previous __rsc method to:

    def __rsc(self):
        print " ReadyState:",self.ors,'->',self.ReadyState
        self.ors=self.ReadyState
        if self.ors==constants.READYSTATE_COMPLETE:
            self.FakeDocumentComplete(self.Document,self.LocationURL)

This "polling" approach is pretty cumbersome, but at least it does
let you fake OnDocumentComplete reasonably well.

I'd like to understand why the REAL OnDocumentComplete does not
happen, if Mark Hammond or somebody else who is really an expert
can explain (& maybe help us find a better workaround...?), but even
more I'd like to do something about the *Before* events -- and in
particular to understand how, assuming I manage to get them called,
I can use their input/output Cancel argument: there seems to be no
Pythonic way to do so!  In VB, VC++, etc, I could set through Cancel to
VARIANT_TRUE (or any non-zero) to block the navigation-attempt --
a level of control that is really necessary to make full use of automating
Internet Explorer.

For example (I've done this with VB and with VC++): use IE to let
the user 'explore' a locally simulated 'site' that is made up of both
real HTML document, images, etc (with extensions in a known set
such as .htm, .html, .gif, etc), AND "pseudo-documents" which in
fact are generated on the fly by my code when the user "visits" them.

To achieve the latter effect through automation of IE, I verify the
BeforeNavigate events and, if from the URL I see that a pseudo-doc
is being 'visited', I block the navigation and instead proceed to
modify the current-document (using the various DHTML possibilities).

Of course, I could take several completely different tacks (a local
server with CGI-like capabilities, or, implementation of particular
monickers, etc, etc), but this one is quite handy & practical too -- if
the language lets me exploit the full Automation abilities of IE,
the DHTML DOM, etc, etc.  However, I'm currently not able to do
that from Python...


Alex






More information about the Python-list mailing list