Events in win32com

Alex Martelli aleaxit at yahoo.com
Fri Dec 22 11:23:48 CET 2000


"Mark Hammond" <MarkH at ActiveState.com> wrote in message
news:3A42C1F7.7040407 at ActiveState.com...
    [snip]
> The module is _always_ created at makepy time, and references to all
> subordinate items created at that time.

You mean that makepy doesn't use bForDemand, or am I misreading
you?


> > But how do I instantiate and call genpy.Generator, or arrange for
> > it to be instantiated and called, to ensure the HTMLDocumentEvents2.py
> > gets built in the bForDemand setting...?  Is there a canonical/
> > recommended way...?
>
> gencache.GetClassForProgID() and GetClassForCLSIDID - however, the

But HTMLDocumentEvents2 doesn't have a ProgID -- it's an interface,
not a coclass, much less a creatable one.  So how to best find out
the CLSID (IID, really -- but it's still a GUID anyway, so I guess
that would work just as well) from the interface-name, which is
what one normally gets out of the docs?  Linear search through the
keys of CLSIDToPackageMap in the module...?  That's over 400
entries for MSHTML -- it could be a bit slow.  Would it be better
to search for it manually and hardwire it as a constant in one's
Python code?

> "EnsureModule" will already need to have been run.  There is no
> single-step generate-module-for-demand-and-get-a-specific-class.

I don't really see this as a problem in this context -- it's
pretty easy to roll up one's own.  Anyway, this version works
just fine (and thanks for your advice!):


import sys, win32com.client.gencache, pythoncom

def EnsureModuleForTypelibOfObject(obj, bForDemand=1):
    try:
        ti = obj._oleobj_.GetTypeInfo()
        clsid = ti.GetTypeAttr()[0]
        tlb, index = ti.GetContainingTypeLib()
        tla = tlb.GetLibAttr()
        return win32com.client.gencache.EnsureModule(
            tla[0], tla[1], tla[3], tla[4], bForDemand=bForDemand)
    except pythoncom.com_error:
            raise TypeError, "This COM object can not automate the makepy
process - please run makepy manually for this object"

ie = win32com.client.gencache.EnsureDispatch("InternetExplorer.Application")
ie.Navigate('c:/index.htm')
ie.Visible=-1

html_module = EnsureModuleForTypelibOfObject(ie.Document)

itf = win32com.client.gencache.GetClassForCLSID(
    '{3050F613-98B5-11CF-BB82-00AA00BDCE0B}')

class Handler(itf):
    def __init__(self, obj):
        self.seen = 0
        itf.__init__(self, obj)
    def Ononclick(self, ev):
        self.seen += 1
        ev = win32com.client.gencache.EnsureDispatch(ev)
        el = ev.srcElement
        print 'clic on %s(%s) "%s", at (%d,%d)' % (
            el.tagName, el.id, el.innerText,
            ev.clientX, ev.clientY)

h = Handler(ie.Document)

while h.seen < 3:
    pythoncom.PumpWaitingMessages()

ie.Quit()


The hard-wiring of '{3050F613-98B5-11CF-BB82-00AA00BDCE0B}' is
the only part of this that seems like a doubtful idea -- though
maybe it's actually better than it 'feels', since IID's *are*
'hardwired forever' in COM's architecture.  But it sure would
be nice to be able to say 'HTMLDocumentEvents2' (in the context
of the typelibrary in which it is thus named, i.e. the
html_module object in this case).  Maybe I'm just worrying
uselessly!  The variant...:

html_module = EnsureModuleForTypelibOfObject(ie.Document)

import time
start = time.clock()
itfName = 'HTMLDocumentEvents2'
for guid, name in html_module.CLSIDToPackageMap.items():
    if itfName==name:
        itfID = guid
        break
else:
    raise KeyError, "%s not found in %s" % (itfName, html_module)
stend = time.clock()

print "GUID found, time %f" % (stend-start)

itf = win32com.client.gencache.GetClassForCLSID(itfID)


seems to also work just fine, with less than 6 milliseconds
"wasted" looking for the interface's GUID (which happens
to be around the middle of the package map's 400 entries,
so this should be rather typical).  So one could just, worst
case, package this up as a name-to-GUID-within-module helper
function, say:

def name2GUID(module, classname):
    for guid, name in html_module.CLSIDToPackageMap.items():
        if class==name: return guid
    return None

and live happily ever after (without real performance-driven
need to actually provide a reverse-mapping data structure).


One thing I really don't understand is why the above script
doesn't work right any more if I comment out the statement:

        ev = win32com.client.gencache.EnsureDispatch(ev)

in Ononclick -- the srcElement &c properties of the event
object are then not found any more (the resulting exception
is correctly caught in upper levels, so the script runs
until three clicks have been recorded, but I don't get
the print at each click any more).  Shouldn't the "pure
dispatch" ev object be _equivalent_ to the genpy-covered
one which I get through this statement, at least for
such simple stuff as getting object-properties...?


> I would be more than happy for us to step back and look at the
> makepy/gencache exposed API - I agree it sucks, as it "just grew" and
> was really never designed for direct use by client code - it just
> happened that way.

That's the sign of successful software -- it grows and develops
beyond what its creator had originally conceived!  And win32com
most definitely IS successful -- it spans the powerful worlds of
COM and Python to deliver truly awesome power... a whole even
greater than the already-impressive sum of its parts.

I also fully agree that some refactoring of the API's exposed
to the application programmer would be great.  The HTML/XML
DOM's, and Office applications' object models, stress any
Automation framework's design parameters, being so huge and
rich (the bForDemand innovation was GREAT from this POV!).

Examining typical IE/HTML/XML use cases, we might come up
with some reasonably palatable packaging.  The 'meat' IS
all there, after all.


> I would be happy for discussion either here, or on the Python COM
> developers list at
> http://mailman.pythonpros.com/mailman/listinfo/pycom-dev - that is very
> quiet tho - not much COM work is going on at the moment.

I guess pycom-dev would be more appropriate, to avoid boring
all of the c.l.p readers ... I'll wait for you to 'open the
dances' there!-)


Alex






More information about the Python-list mailing list