I've defined my own interface IUnknown, but IUnknown is somehow treated specially and leads to hidden bugs (in my case a defect vtable) where the compiler doesn't warn me that the calling convention is somehow changed to extern(Windows).
https://run.dlang.io/is/YuouXd
class Test : IUnknown {
void test() {}
}
interface IUnknown {
void test();
}
void main() {
auto hm = new Test();
hm.test();
}
vs
https://run.dlang.io/is/nxRwN0
class Test : IUnknown_x {
void test() {}
}
interface IUnknown_x {
void test();
}
void main() {
auto hm = new Test();
hm.test();
}
Comment #1 by puremagic — 2023-04-04T16:38:46Z
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();
}
Comment #2 by alphaglosined — 2024-03-20T11:58:01Z
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-interfaceshttps://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.
Comment #3 by puremagic — 2024-03-20T13:26:52Z
"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.
Comment #4 by robert.schadek — 2024-12-13T19:28:08Z