core.sys.windows.com.ComObject apparently has wrongly laid out Vtable
Carl Sturtivant
sturtivant at gmail.com
Tue Mar 19 14:54:51 UTC 2024
On Tuesday, 19 March 2024 at 09:22:09 UTC, Kagamin wrote:
> You incorrectly declared the interface variable. Try this:
> ```
> auto COMobject = new com.ComObject();
> //auto COMobject = new ComObject();
> IUnknown ip = COMobject;
**Nonsense**. Of course that doesn't work! It seems you didn't
read or didn't understand my post.
All you've done is written circular code that confirms
compatibility with itself by remaining entirely within its own
world of D only, and only using `core.sys.windows.com`. To make
this "work" you had to `import core.sys.windows.com;` which I do
not.
You have NOT confirmed the compatibility of
`core.sys.windows.com.IUnknown` and
`core.sys.windows.com.ComObject` with the outside world of actual
COM.
Supposedly things defined using those can be passed through COM
to outside world and will work correctly there. This is exactly
what is NOT happening here.
As mentioned in my original post `core.sys.windows.com` is
*statically* included so `core.sys.windows.com.IUnknown` is NOT
`IUnknown` in `main.d` and in fact, apart from making a
`ComObject` none of `core.sys.windows.com` is used at all in the
original run.
`IUnknown` in my code is the usual COM struct defined in a
Windows header and imported from `comheaders.c`. It's called an
interface in COM terminology but it is NOT a D interface, and you
can't inherit from it.
So if I just change `main.d` to the code you quoted above it
won't compile of course.
```
main.d(11): Error: cannot implicitly convert expression
`COMobject` of type `core.sys.windows.com.ComObject` to `IUnknown`
```
The outside world in C would work with an object implementing
IUnknown (or another COM interface) as a pointer to a struct of
type IUnknown (or similar), and main.d works with COM in exactly
that way.
The documentation of the D interface IUnknown at the quoted link
in my original post
says that
*A COM interface is defined as one that derives from the
interface core.sys.windows.com.IUnknown.*
and
*A COM interface is designed to map directly onto a Windows COM
object. Any COM object can be represented by a COM interface, and
any D object with a COM interface can be used by external COM
clients.*
This is D's *internal* definition, how a COM interface (outside
world definition independent of programming language) is
represented inside D. Conflating the inside D meanings of *COM
interface* and *IUnknown* with the outside world COM definitions
of those terms is the source of confusion.
In effect the documentation deliberately conflates the two by
tacitly assuming that the D representation is correct so we need
not make that distinction.
The D class `core.sys.windows.com.ComObject` inherits from
`core.sys.windows.com.IUnknown` and so should follow those
statements. My code gets one of those and behaves as an *external
COM client*, but the result does not work.
If I fix the source of `core.sys.windows.com.ComObject` and
`core.sys.windows.com.IUnknown` to have `extern(C++)` linkage
then the self-same code works, showing that I wrote it correctly.
In order to work with COM the way that C does, when given a
ComObject from the world of D, I need to treat it as a pointer to
an IUnknown struct. The ComObject variable COMobject is already
a reference so I need to turn it into a pointer of type IUnknown*
that points to the actual ComObject that the COMobject variable
refers to, all without knowing the D type of the object.
This is just simulating what would happen if such an object was
passed by D though COM to e.g. an arbitrary C program. COM
clients do not know the native type of the objects passed them,
only the COM interface (NOT D interface) they are supposed to
implement.
All the recipient knows is that the pointer is to a struct that
contains a properly laid out Vtable with `QueryInterface`,
`AddRef` and `Release` as the first three entries in that order
in accordance with the standard layout of an IUnknown struct. The
following code correctly achieves that outcome.
```
IUnknown* ip = cast(IUnknown*)COMobject
```
Then *ip is used as the (COM not D) interface to the externally
provided COM object and it doesn't work unless the D library code
is repaired.
Then it DOES work. So IUnknown and ComObject in druntime are
broken. They do not lead to Vtables laid out according to the COM
structs defined by Windows.
More information about the Digitalmars-d
mailing list