core.sys.windows.com.ComObject apparently has wrongly laid out Vtable

Richard (Rikki) Andrew Cattermole richard at cattermole.co.nz
Wed Mar 20 04:14:44 UTC 2024


On 20/03/2024 12:36 PM, Carl Sturtivant wrote:
>     This is why you must cast the D object to IUnknown first before
>     inspecting its reference.
> 
> I do not think this follows, either for a regular D object or a COM 
> object. For the first the class hierarchy can determine the order of the 
> virtual methods and overriding doesn't change the index of a method with 
> a given signature. For a COM object the order of the methods in the 
> Vtable is determined by the COM interface that the object is 
> implementing. In the case of this thread, it's determined by the 
> definition of IUnknown in COM.

I suspect you have misunderstood another aspect of classes in general.

First an example, a class and an interface WILL have a different vtable 
entries even if what the vtable represents is the same thing.

```d
import std.stdio : writeln;

void main() {
     C c = new C;
     I i = cast(I)c;

     printVtable(c.__vptr, 3);
     printVtable(i.__vptr, 3);

     printMember!"query"(i, c);
     printMember!"add"(i, c);
     printMember!"sub"(i, c);
}

void printVtable(immutable(void)* vtable, size_t members) {
     writeln("vtable ", vtable);

     void** ptr = cast(void**)vtable;

     foreach(i; 0 .. members) {
         writeln("- ", ptr[i]);
     }
}

void printMember(string member, T, U)(T first, U second) {
     writeln(
         member, " ",
         "First: ", (&__traits(getMember, first, member)).funcptr,
         " Second: ", (&__traits(getMember, second, member)).funcptr
     );
}

extern(C++) interface I {
     void query();
     void add();
     void sub();
}

extern(C++) class C : I {
     override void query() {}
     override void add() {}
     override void sub() {}
}
```

Will output:

```
vtable 562927D04210
- 562927C9577C
- 562927C9578C
- 562927C9579C
vtable 562927D041E8
- 562927C9566C
- 562927C9567C
- 562927C9568C
query First: 562927C9566C Second: 562927C9577C
add First: 562927C9567C Second: 562927C9578C
sub First: 562927C9568C Second: 562927C9579C
```

The reason for this is something called a thunk.

```asm
.text	segment
	assume	CS:.text
:
_THUNK0:
		sub	RDI,8
		jmp	  _ZN1C5queryEv at PLT32
		0f1f
		add	byte ptr [RAX],0
		add	[RAX],AL
_THUNK1:
		sub	RDI,8
		jmp	  _ZN1C3addEv at PLT32
		0f1f
		add	byte ptr [RAX],0
		add	[RAX],AL
_THUNK2:
		sub	RDI,8
		jmp	  _ZN1C3subEv at PLT32
		add	[RAX],AL
		add	[RAX],AL
.text	ends
.data	segment
```

These are functions that alter in this case the this pointer to align 
with what the actual function expects.

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

Without it the interface will not understand how to call the class and 
all the pointers will be a little bit off, in this case the size of a 
pointer aka the extra vtable entry.


More information about the Digitalmars-d mailing list