Thread overview
[Issue 23819] defining your own interface IUnknown messes up vtable without any warning
Apr 04, 2023
Felix Hufnagel
April 04, 2023
https://issues.dlang.org/show_bug.cgi?id=23819

--- Comment #1 from Felix Hufnagel <puremagic@zoadian.de> ---
To add to that, my initial problem was, that when using two mixins, somehow the
order i mix them in is important. but shouldn't the vtable automatically be
correct when i inherit an interface?
in my mixin IMPLEMENT_REFCOUNT; and mixin QUERY_INTERFACE!(FUnknown, IMessage);
will generate the wrong vtable, unless swapped.


extern (C++)
class HostMessage : IMessage
{
public:
  // somehow the order of the two mixins matters
  mixin IMPLEMENT_REFCOUNT;
  mixin QUERY_INTERFACE!(FUnknown, IMessage);
}

mixin template IMPLEMENT_REFCOUNT() {
  override uint addRef()  { return 1; }
  override uint release() { return 1; }
}

mixin template QUERY_INTERFACE(Interfaces...) {
  override tresult queryInterface (ref const TUID _iid, void** obj) { return 1;
}
}

interface IMessage : FUnknown
{
public:
nothrow:
// @nogc:
        /** Returns the message ID (for example "TextMessage"). */
        FIDString getMessageID();

        /** Sets a message ID (for example "TextMessage"). */
        void setMessageID(FIDString id /*in*/);

        /** Returns the attribute list associated to the message. */
        IAttributeList getAttributes();

    __gshared immutable TUID iid = INLINE_UID(0x936F033B, 0xC6C047DB,
0xBB0882F8, 0x13C1E613);
}

interface FUnknown : IUnknown {
    __gshared immutable TUID iid = INLINE_UID(0x00000000, 0x00000000,
0xC0000000, 0x00000046);
}


interface IUnknown {
    tresult queryInterface(ref const(TUID) _iid, void** obj);
    uint addRef();
    uint release();
}

--
March 20
https://issues.dlang.org/show_bug.cgi?id=23819

Richard Cattermole <alphaglosined@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |alphaglosined@gmail.com

--- Comment #2 from Richard Cattermole <alphaglosined@gmail.com> ---
Changing the calling convention to extern(Windows) and conversion to being a C++ interface is a documented behavior of the interface IUnknown (and in line with the definition provided by the Windows API).

https://dlang.org/spec/interface.html#com-interfaces

https://dlang.org/spec/abi.html#interfaces

As for not warning you, it would if you marked your class Test method with override. This appears to be a requirement in general for classes and is not COM-specific.


```
onlineapp.d(2): Error: function `extern (C) void onlineapp.Test.test()` does
not override any function, did you mean to override `extern (Windows) void
onlineapp.IUnknown.test()`?
onlineapp.d(1): Error: class `onlineapp.Test` interface function `extern
(Windows) void test()` is not implemented
```

As for the mixin templates orders, the order in the vtable shouldn't be the same order as defined in IUnknown. When you cast your object to IUnknown then it'll line up, but for all other children, it won't.

--
March 20
https://issues.dlang.org/show_bug.cgi?id=23819

--- Comment #3 from Felix Hufnagel <puremagic@zoadian.de> ---
"A COM interface is defined as one that derives from the interface core.sys.win­dows.com.IUnknown. A COM interface differs from a regular D interface in that:

It derives from the interface core.sys.windows.com.IUnknown."

I never derived from core.sys.windows.com.IUnknown.
I just named my own interface IUnknown, and the compiler did some magic.
if I don't define it, it even says it's undefined.

--