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