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.win­dows.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.win­dows.com.ComObject` inherits from 
`core.sys.win­dows.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.win­dows.com.ComObject` and 
`core.sys.win­dows.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