Exporting events from Python COM class.

Alex Martelli aleaxit at yahoo.com
Fri Nov 10 18:04:31 CET 2000


"Mark Hammond" <MarkH at ActiveState.com> wrote in message
news:3A0BE209.6010305 at ActiveState.com...
> Alex Martelli wrote:
>
> >> And actually exposes the reason for one of the hacks in win32com.
> >> Notice the second arg to the first QI.  This is to say "even though I
am
> >> QIing for an interface you dont support, give me back an object
anyway".
> >
> > But why?  If the client is trying to connect an object that
> > does *not* "sink" (=="is able to be called on") the specified
> > events-interface (which the server "sources" == "calls methods on"),
> > then we do NOT want Advise to succeed!
>
> I worded this poorly.  I should have said "if the QI succeeds but the
> PythonCOM extensions do not provide explicit support for the interface,
> return an IUnknown anyway".

Oh, I see -- sorry, that "you" was unclear to me (I now see what you
meant -- "you" as in "o Python COM infrastructure", not as in "o
object trying to connect to me"!-)


> ie, the "1" is _not_ preventing a DISP_E_NOINTERFACE error, rather some
> "internal" error telling you that Python doesnt support the interface.
>
> Actually, the "1" is the old API - the new interface is the IID of the
> object you want back instead!  1 == IUnknown for b/w compat (probably
> for this code :-)
>
> Thus, I _think_ you could get the behaviour you want by changing the "1"
> to IID_IDispatch, and removing the second QI.

Yes!  It works fine.  It suffices to substitute lines 33-34
in win32com.server.connect.py with the single line

   interface =
pUnk.QueryInterface(self._connect_interfaces_[0],pythoncom.IID_IDispatch)

and that now appears to work flawlessly.


> > Events-interfaces are almost invariably dispinterfaces: the
> > methods are exactly those of IDispatch, only the IID differs.
>
> I agree 100% - but the "almost" is what has me worried ;-)

But you can't support interfaces unless you either "know"
about them, OR they inherit from IDispatch, anyway.

Here, we're talking about a _source_ interface -- one
on which you make calls, while client-code implements it.

If it's a custom-interface you've never heard about, you
can't make calls on it anyway (and I would claim it's
silly to claim you can expose it as a sourceinterface...!).

If it's a dual-interface (it shouldn't be, but...) *you
don't care* -- if you only make calls on its Invoke anyway,
you _don't care_ if it has a zillion more methods on top
of that -- what difference would it make to you?

If it's a dispinterface, then the further QI for IID_IDispatch
is buying you nothing -- it's heads you break evens (you get
the same Invoke method you'd have anyway without the QI),
tails you lose big (you get an Invoke method with different
semantics and call upon it with dispatch id's that have a
different meaning than what you think they have... thus
introducing semi-silent, horrible-to-track bugs...!).


> > Some COM background (which I'm sure you don't need, but may come
> > in handy for some readers!):
>
> Thanks - and to my mind, is exactly the reason why I _dont_ want to
> break the rules here!

But you do, if you QI a dispinterface for IID_IDispatch.

> You QI for an IID, you get back a pointer to that
> interface.  No where does it say "oh - but you may _really_ get back an
> IDispatch that happens to implement an object model as described by that
> IID".  It is pretty clear you get a pointer to that interface.

Of course, you *can't* get an IDispatch -- the IID of what
you're getting is guaranteed to be what you've asked for.

But the point is that you can't use it anyway unless either
you know that IID in the Pythoncom internals, OR it's an
interface that inherits from IDispatch (normally, in this
case of source-interfaces, a dispinterface specifically).


> > *sigh* that silly example has done MUCH harm.  Please refer
> > to all Microsoft literature on the subject... for example,
> > "Dr. GUI and COM Events, Part 1", its August 23, 1999 column
> > (it's in MSDN, of course), rightly says "It doesn't make
> > sense for a source interface to be a dual interface".
>
> Thanks!

You're welcome.


> Jeez - MS are full of it.  On one hand, as you say, they write "It
> doesn't make sense for a source interface to be a dual interface".  But
> the _same_ paragraph says "If you want to have an event interface that
> can be high performance and compatible with Visual Basic, ...".
>
> So really, they are saying there _is_ good reason to do this, but it is
> not compatible with VB.

*sigh* not really, although I see how you could read it this
way.  There is NO good reason to have a source-interface be
dual.  If VB (and, actually, every other scripting language)
were of no importance, you could make it custom (that's OK,
you can do it now, as long as you know no scripting language
nor VB will be able to sink those events -- as long as you're
at it, why follow the connection-point protocol either -- you
can optimize connection/disconnection operations too, by
customizing them to your actual needs; connection-point is
designed with generic applicability in mind, basically to
enable connection from scripting and VB...).  If VB and other
scripting languages matter, you clearly have to make it a
dispinterface (that's what they know how to sink, of course).

If you need both, sure, implement both... just, in _separate_
source interfaces, please!  (& make sure the _default_ one
is the dispinterface -- that's important for scripting!-).
What is acknowledged is the desire to have the alternative
of both speed OR use from scripting (you won't get both at
once, clearly -- scripting ain't speedy:-).  Wouldn't we
all like to have incompatible things at the same time, such
as a Python compiler generating even better code than
typical C compilers?-)  (Dylan, Scheme, and other LISP
users may perk up at this point, I guess:-).  But we are
resigned to "separate" our code that is concerned with
speed from that which is concerned with ease of writing
(at least, us Python users are, for the most part).  Well,
COM source-interfaces require the same separation...

For a _source_ interface, being dual does _not_ mean it
can optimally serve both scripting languages & C++ (as
it does for all other interfaces you may expose), because
*the calls go the other way 'round*.  Thus, the effect is
the reverse: it's *as hard as can be* to sink a source-itf
that's defined as dual (while it's *as easy as can be* to
USE a normal-itf that's defined as dual...).

It would perhaps make sense if you wanted to make it
easiest to write *servers* generating events on that
source interface in either C++ or scripting, while
putting all implementation burden on the clients.  But
the little help for C++ writers wrt a dispinterface is
minor indeed (any COM framework will help them source
events on a dispinterface via proxies etc...) while the
extra burden on scripting implementation is crippling.

(continued...)


Alex








More information about the Python-list mailing list