COM/CORBA/DCOP (was: Hello people. I have some questions)

Alex Martelli aleax at aleax.it
Thu Sep 6 06:37:03 EDT 2001


"Neil Hodgson" <nhodgson at bigpond.net.au> wrote in message
news:LBwl7.17467$bY5.96652 at news-server.bigpond.net.au...
    ...
> > No way, the Automation reference is adamant about it: Invoke
> > *MUST* return DISP_E_MEMBERNOTFOUND if it's called with a
> > dispatch-id that is not valid for this object -- it's NOT
> > allowed to crash or return other random HRESULT's.
>
>    I don't believe this, callers must provide valid arguments. An
incorrect
> dispid is the same as passing an invalid pointer as pDispParams. Reference
> please.

Sure, it's the same as passing an invalid pointer, except that it must be
diagnosed by a different HRESULT -- that's what E_POINTER is *FOR*, you
know.  In either case, crashing (technically, "propagating an exception" --
remember "crashes" are really exceptions in NT's SEH model) is NOT
permitted.

Get http://www.microsoft.com/COM/resources/COM1598B.ZIP, unzip it somewhere
convenient, open "CH03 Object and Interfaces.doc", and read the section
peculiarly numbered 1.4, "Error Codes and Error Handling", starting towards
the end of page 12.  "In short, <italic>it is strictly illegal to throw an
exception across an interface invocation</italic>; all such cross-interface
exceptions which are thrown are in fact <underscore>bugs</underscore> in the
offending interface implementation."  There's a paragraph before this to put
this in context, and a few pages afterwards explaining the reasons for this
specification and the use of the various different HRESULTS to diagnose many
possible problems including those that are due to invalid arguments (no
details about the various specific DISP_E_*, of course, as those are
facility-ITF, i.e. interface-specific HRESULT's of IDispatch in particular).
This is such a fundamental part of the COM specs that I'm astonished to see
somebody apparently COM-savvy request a reference to it.

In short, yes, callers must provide valid arguments, and, if they don't,
callees must diagnose this by the appropriate error-HRESULT return codes --
most details are up to the specific interface, but crashing ("throwing an
exception across the interface invocation") is right out, and anyway
IDispatch is already designed and you're not allowed to redesign it if you
claim you use/implement it.  The interface-specific details of IDispatch
aren't in COM1598B.ZIP, but for example if you have a recent MSDN
installation on your PC, then:
mk:@MSITStore:\\BOBDC\MSDN\MSDN\inole.chm::/html/S1227.HTM
has some notes on EXCEPINFO ("rich error information"), of which some more
at http://support.microsoft.com/support/kb/articles/Q139/0/73.ASP.
mk:@MSITStore:\\BOBDC\MSDN\MSDN\inole.chm::/html/S1235.HTM has some very
elementary performance considerations (it does support your quibbling on
terminology use -- it says, e.g., "In contrast, making a direct call to a
vtable interface function allows the client to push values on the stack
instead of first stuffing them into other structures", steadfastly refusing
to call this "stuffing arguments into other structures" by my favourite
abbreviation "marshaling":-), and so on, and so forth.
For some more detail and examples, see
mk:@MSITStore:\\BOBDC\MSDN\MSDN\wcesdkr.chm::/html/_wcesdk_oa96_IDispatch_In
voke.htm,
etc, etc.

If you're programming your Invoke implementation in C or C++ on NT or other
similar Win32 API, you may of course decide to rely on NT's SEH to catch
exceptions on your behalf.  Take care, though, because you may well be
violating IDispatch's specs if you do so, unless you exercise extreme care
in your memory layout choices -- I think this should really be done in
assembler or machine-code.  Say a client calls your Invoke with a standard
dispatch-ID such as -4, just trying to find out whether your interface can
be enumerated (==can supply an IEnumVariant*) -- this is perfectly OK
behavior by an Automation client, by the way.  If you don't check the
dispID, you may well end up addressing a vtable with an out-of-bounds
negative index, and get a "random" (sort-of) procedure address to dispatch
to.  If you're lucky enough that those four bytes point into the ether, SEH
may yet save your skin.  But, unless you're carefully controlling your
memory layout, segment/page usage and permissions, etc, it's quite possible
that those four random-ish bytes point to ANY accidentally valid piece of
machine code in your address space -- and, what then?  You may happen to end
up performing any action whatsoever of which some piece of your executable
address space is capable of -- e.g., you might return an S_OK (it's 0, a
likely value to happen to return by mistake:-) with the blob of memory that
should be your result variant left accidentally containing a VT_UNKNOWN with
a random IUnknown*.

*THE CLIENT DOES _NOT_ HAVE TO CHECK* -- you should have returned
DISP_E_MEMBERNOTFOUND, your returning S_OK says you have indeed returned
valid data.  The client may validly try to QI that IUnknown* for
IEnumVariants and crash mysteriously -- and it's YOUR fault: your
implementation of IDispatch::Invoke is buggy, it does not meet IDispatch's
specs.

No doubt COM could have been designed differently, with other choices for
allocation of responsibility for "valid" arguments, but, it wasn't.  If you
claim to be doing COM, and Automation in particular, then you'd better stick
to the COM and IDispatch specs.  Of course, there's a lot of buggy
Automation stuff around, produced by designers whose idea of "specs" would
appear to be "well, it seems to work with my unit-testing VB4 client" (many
such controls broke when VB5 came out, and a few more when VB6 did...
actually, of course, they always were broken -- testing with a *specific*
client implementation does not validate that a reusable component [server]
is actually meeting the componentization-framework specs!-).


Alex



Alex






More information about the Python-list mailing list