Essential COM by Don Box
https://www.amazon.com/Essential-COM-Don-Box/dp/0201634465
Inside COM by Dale Rogerson
https://www.amazon.com/Inside-Microsoft-Programming-Dale-Rog...
Then if you have access to public libraries, maybe one of them has one of the several Microsoft Systems Journals issues, later MSDN Magazine, with plenty of low level COM articles.
COM is from the days where good documentation was to be found in books, not on the Interwebs.
I highly recommend using them if you're ever forced to work with VBA and need to implement some non-trivial functionality.
https://archive.org/details/MSDN_Library_October_2001_Disc_1
https://archive.org/details/MSDN_Library_October_2001_Disc_2
https://archive.org/details/MSDN_Library_October_2001_Disc_3
The rest is just that interfaces are doubly indirected to get to a vtable (an array or struct of function pointers), the method is chosen by ordinal in the vtable (first three are always the IUnknown methods), and the interface is passed as the first argument.
How you construct those vtables and how you select one to return in QueryInterface, and how you implement interfaces (i.e. traits) so you can convert them into a vtable is where all the work is. You can do anything you like that works as long as it's called according to the COM conventions.
Delphi works by implementing each method in the vtable with a stub which subtracts the offset of the vtable pointer in the instance data from the passed in interface, and then jumps to the implementation method on the instance after the instance has now been adjusted. Instances look like this:
[class pointer, the native vtable] <- normal instance pointers
[vtable interface 1] <- COM interface pointers
...
[vtable interface n] <- COM interface pointers
instance field 1
...
instance field n
So you can see that in order to convert a COM interface pointer into an instance pointer that the methods expect, the COM interface pointer needs to be adjusted depending on the offset of the vtable in the instance.In a language with multiple inheritance like C++, the compiler vendor targeting Windows may choose a layout which is suitable for COM's calling convention (there's more than one way to do MI, e.g. fat pointers is another way to go that wouldn't be COM-compatible). If the vendor does that, then they make implementation of COM interfaces much easier. And if they don't, well, life isn't going to be easy. Technically you could do a bunch of stuff with reflection and code generation, but that's harder and harder these days with security restrictions around code writing code. You could write macros which create statically initialized structures of the right shape, and fields of the right type, to emulate the same effect as Delphi's scheme as I sketched above, or some other method which would work with COM's calling convention.