Exporting events from Python COM class.

Alex Martelli aleaxit at yahoo.com
Fri Nov 10 12:04:34 EST 2000


(...continued):

> *sigh*.  Other Python COM stuff exists purely for VB, and I guess this
> qualifies as the documentation I asked Toby for...
>
> OTOH, I see their point.  This exact same limitation is exactly what
> Python needs to do to work.

...except that, in an emergency, one can let custom
interfaces become known by working at the C level in
Pythoncom, right?  I've never had the need to, but --
it sort of provides a warm fuzzy feeling to know that,
if worse comes to worst... [actually, if it happens,
I'll probably kludge together a custom ATL wrapper
instead, as a separate COM object to do the translation
between dispatch-level and the Unknown Custom Itf; but,
that's just me -- I got badly burned maintaining a
home-tweaked version of the MS C runtime libraries for
years, so I'm hypersensitized against forking _any_
kind of externally provided code just because I have
its sources lying around...!-)].


> > COM QI rules are inviolable, and the Box-and-others
> > article I quote above is one of the clearest I know
> > on the subject.
>
> Well I am still missing something :-(  I thought it was pretty clear
> that when you did a QI, you got back what was effectively a vtable
> describing that interface.  I thought COM was a binary standard, and
> this was fundamental.

It is.  The vtables of all dispinterfaces just 'happen' to
be laid out in the same ways:-).  But the Invoke member
of the vtables you get from QI for two different dispinterfaces
to the same object need not point to the same executable
code, or code with equal semantics for both (nor do the
others -- the _QueryInterface_ member of *any* interface
[vtable] is the only one thus constrained, by COM's identity
rules).

> Am I free to return an IDispatch in _any_ QI for _any_ IID, as long as I
> return a faithful IDispatch for the interface?  Or does this only apply
> to sinks?

sinks are no different than others.  If the binary layout
of the vtable associated to a given IID comprises, say, 25
entries, then 25 valid entries is what the vtable you return
must have (and they must be callable according to what
that binary layout prescribes).  [You may have further
entries beyond the 25 -- which is to say, you may well
choose to return a vtable for an interfaces that is DERIVED
from the one you were asked for -- but the extra entries
will be ignored, typical proxy/stubs implementations will
not even marshal them, etc, etc].

All dispinterfaces 'happen to' correspond to vtables with
exactly 7 entries (the 3 for IUnknown, then the 4 that are
typical of IDispatch).  And (decently designed) event
interfaces are dispinterfaces.

Yeah, yeah, sigh, this is *NOT* the good part of COM:-(.

Go see Don Box's site -- he's always claimed that IUnknown
is COM's greatest strength, IDispatch its greatest
weakness... and he's convinced me.  Corba has a better
architecture for the specific purpose of allowing
late-binding (scripting) of any interface -- the broker
must do the work, rather than the server.  Oh well -- THIS
part you can lay squarely on the shoulders of the VB
and VBA teams within Microsoft, blamewise... if it's
any relief!-).  It happens to be true -- VB &c were
rushing ahead with their half-baked solutions, while
the COM team was busy designing excellent, solid
foundations, and at some point the VB-team kludges
had to be grandfathered-in -- usual compatibility
issues with the by-now-huge installed codebase...:-(.

(I gather the .NET "solution" to "how do I script just
ANY interface" is yet another, i.e., that the _interpreter_
has to take the burden [using the rich metadata supplied,
and with some modest assist from the runtime] since there
is no real "broker" layer as in Corba -- but you know a
zillion more times than I do on .NET, so maybe you can
confirm or deny this impression!-).


> >> Hence we have the second QI for specifically IDispatch.
> >
> > But you won't necessarily get the "same Dispatch" (Invoke
> > with the same semantics) as you should have in the
> > dispinterface you're QI'ing.  Again, I hope the Box-et-al
> > article makes that cristal-clear.
>
> I definitely see the point you are making.  I just see it as a gross
> violation of clear rules.  However, you have given me plenty of
> references to make it clear it must just be me!

Actually, this part of the rules may not be crystal clear.

As Box & friends say in their quoted article, "What is not
explicitly stated in the COM specification is that trouble
will ensue when you give out different logical results for
identical QueryInterface requests" -- as would happen if
two different dispinterfaces of the same object gave out
logically-different IDispatch implementations when QI'd
for them.  That not-explicitly-stated rule is why the
hypothetical listener implementing two dispinterfaces
MUST choose one of them as 'primary' in case it's ever
QI'd for IDispatch.  ("Effective COM", the Box-et-al book
from which the quoted article is take, is almost as much
of a must-read as his "Essential COM"...).


> > I _think_ the right approach would be as follows: if
> > the interface is "known" (from previous makepy's) it
>  > can be used anyway; else, it must be a dispinterface
>
> The behaviour really should be identical regardless of makepy.  Not
> having makepy will mean we have trouble _implementing_ the interface,
> and _may_ have trouble calling it in some cases, the but basic behaviour
> shouldnt really be dependent?
>
> ie, from what you pointed me at above and from your and Toby's
> assertions, it must be a dispinterface, period.  Otherwise it is in
> "gross violation" of "conventions" (as opposed to "rules" :-)

Assuming VB6 is still unable to sink custom event-interfaces,
then it's no problem if Python can't sink them either, of
course.

It would just be _slightly_ preferable, I believe, if that
inability was diagnosed as clearly and early as possible.

E.g., at __init__ of ConnectableServer... maybe some
diagnostics and exception-raising could be done then, IF
the (one and only!) interface listed in _connect_interfaces_
was known not to be a dispinterface.  (I _would_ also add
a check that _connect_interfaces_ has a len of exactly 1,
since ConnectableServer can't really cope with any other
number).


> > Consider the following example (ATL 3.0) -- the IDL
> > being:
>
> As I said, I always understood the concern.  However, I now yield,
> because as Toby says it has come up before, and you chiming in leaves me
> with no reasonable defence.
>
> So patches welcome ;-)

That change to lines 33-34 of connect.py, which you suggested
at the start, fixes the worst of my concerns -- the possibility
of creating a subtle, silent semantic malfunctioning.  I
guess it's better to leave _connect_interfaces_ as a list,
for compatibility, but check it only has one item...

(Where does one submit win32all patches, and in what diff
format...?  The only diff programs I have easily available
on Windows are GUI-oriented, but I can of course download
whatever is required, just point...!-).


Alex








More information about the Python-list mailing list