March 20
On 3/19/2024 10:56 PM, Carl Sturtivant wrote:
> On Wednesday, 20 March 2024 at 05:42:29 UTC, Carl Sturtivant wrote:
>> What are the Vtable differences that cause an extern(C++) ComObject to work when calling AddRef when the druntime ComObject with extern(Windows) does not?
> 
> I mean this of course in the context of the code in my original post in this thread.
> There casting a druntime ComObject to a pointer leads to a non-working AddRef, but casting a extern(C++) ComObject to a pointer leads to a working AddRef. I could speculate, but what are the actual rules operating that lead to this?
> 

A com object is an interface, not a class. All com objects must derive from core.sys.windows.unknwn.IUknown, or from an interface that derives from IUnknown.

IUnknown uses the extern (Windows) calling convention.

For com objects, slot 0 in the vtbl[] is for QueryInterface(), not the classinfo.
March 20
On 3/20/2024 7:38 AM, Carl Sturtivant wrote:
> So why isn't ComObject declared extern(C++) in druntime?

Because COM objects need to follow the extern(Windows) calling convention. It's a mistake to try to cast it to extern(C++).

I didn't invent this. Microsoft did. COM was invented around 1990 or so. It's cast in concrete.

March 21

On Thursday, 21 March 2024 at 03:48:04 UTC, Walter Bright wrote:

>

On 3/20/2024 7:38 AM, Carl Sturtivant wrote:

>

So why isn't ComObject declared extern(C++) in druntime?

Because COM objects need to follow the extern(Windows) calling convention. It's a mistake to try to cast it to extern(C++).

I didn't invent this. Microsoft did. COM was invented around 1990 or so. It's cast in concrete.

I was hoping that the special treatment by the D language of IUnknown in core.sys.windows.com would apply to any class that inherited from it, even an extern(C++) class, so I could have my cake and eat it, and

extern(C++) class ComObject : IUnknown { /* ... */ }

would produce a class with extern(Windows) calling and an extern(C++) COM compatible vtable.

This does compile. Does the extern(C++) remove the special treatment of IUnknown and revert the class to the wrong calling convention?

March 21

On Thursday, 21 March 2024 at 03:38:18 UTC, Walter Bright wrote:

>

A com object is an interface, not a class. All com objects must derive from core.sys.windows.unknwn.IUknown, or from an interface that derives from IUnknown.

IUnknown uses the extern (Windows) calling convention.

For com objects, slot 0 in the vtbl[] is for QueryInterface(), not the classinfo.

It's nice that the D language itself makes this happen for core.sys.windows.unknwn.IUnknown specially so as to make writing a COM client using a class possible and having it pass on its extern(Windows) calling convention is nice too.

As you say, all COM objects must inherit from this --- otherwise they don't get the twin benefits of extern(Windows) calling and a COM compatible vtbl[] for their IUnknown interface.

Can we get those benefits out here without core.sys.windows.unknwn.IUnknown by definining a class extern(C++) to get a COM compatible vtbl[] and then qualifying each of its methods as extern(Windows) to get COM compatible calling, and writing our methods in the correct COM order, starting with QueryInterface, AddRef, Release, and relying on D to place the methods in the vtbl[] in that order?

(Of course, this won't then bless the descendants of such an interface or class, the way the descendents of core.sys.windows.unknwn.IUnknown are blessed by a special dispensation in the D language definition, but still. It would be very nice if we could so bless an interface or class ourselves with extern(COM), fixing the vtbl[], calling convention, and method ordering in the vtable rules, for self and any descendant.)

I ask if we can get these benefits ourselves as above because there's an external world difficulty using core.sys.windows.unknwn.IUnknown with the rest of Windows programming outside of just using core.sys.windows.windows and suchlike.

core.sys.windows.unknwn.IUnknown forces the definition of core.sys.windows.basetyps.GUID into any COM code written using it because the first parameter of core.sys.windows.unknwn.IUnknown.QueryInterface is defined to be of type IID* and IID is defined to be const(GUID), all in core.sys.windows.basetyps.

Now if ImportC is used to bring in Windows COM related headers so as to use the wide collection COM machinery there, a binary-identical GUID type is defined in guidef.h and used throughout. Inheriting from core.sys.windows.unknwn.IUnknown to make COM objects of type that interface means that QueryInterface will have the wrong type for its first parameter to work with all of Windows COM machinery. Working around this is awkward.

March 21

On Thursday, 21 March 2024 at 14:30:24 UTC, Carl Sturtivant wrote:

>

It's nice that the D language itself makes this happen for core.sys.windows.unknwn.IUnknown specially so as to make writing a COM client server using a class possible and having it pass on its extern(Windows) calling convention is nice too.

March 21
On 3/21/2024 6:22 AM, Carl Sturtivant wrote:
> On Thursday, 21 March 2024 at 03:48:04 UTC, Walter Bright wrote:
>> On 3/20/2024 7:38 AM, Carl Sturtivant wrote:
>>> So why isn't ComObject declared extern(C++) in druntime?
>>
>> Because COM objects need to follow the extern(Windows) calling convention. It's a mistake to try to cast it to extern(C++).
>>
>> I didn't invent this. Microsoft did. COM was invented around 1990 or so. It's cast in concrete.
> 
> I was hoping that the special treatment by the D language of IUnknown in core.sys.windows.com would apply to any class that inherited from it, even an extern(C++) class, so I could have my cake and eat it, and
> 
> ```D
> extern(C++) class ComObject : IUnknown { /* ... */ }
> ```
> would produce a class with extern(Windows) calling and an extern(C++) COM compatible vtable.
> 
> This does compile. Does the extern(C++) remove the special treatment of IUnknown and revert the class to the wrong calling convention?
> 
> 

The C++ and Windows calling convention are different. COM code will not be able to successfully call C++ functions. You can derive a C++ class from a COM interface, but you can't cast a COM interface to a C++ class.
March 21
On 3/21/2024 6:22 AM, Carl Sturtivant wrote:
> would produce a class with extern(Windows) calling and an extern(C++) COM compatible vtable.

There are two vtables in that example - one for the C++ class, and one for the COM interface. They are not interchangeable with a cast, because the layouts are different.

March 21
On 3/21/2024 7:30 AM, Carl Sturtivant wrote:
> On Thursday, 21 March 2024 at 03:38:18 UTC, Walter Bright wrote:
>> A com object is an interface, not a class. All com objects must derive from core.sys.windows.unknwn.IUknown, or from an interface that derives from IUnknown.
>>
>> IUnknown uses the extern (Windows) calling convention.
>>
>> For com objects, slot 0 in the vtbl[] is for QueryInterface(), not the classinfo.
> 
> It's nice that the D language itself makes this happen for `core.sys.windows.unknwn.IUnknown` specially so as to make writing a COM client using a class possible and having it pass on its extern(Windows) calling convention is nice too.
> 
> As you say, all COM objects must inherit from this --- otherwise they don't get the twin benefits of extern(Windows) calling and a COM compatible vtbl[] for their IUnknown interface.
> 
> Can we get those benefits out here without `core.sys.windows.unknwn.IUnknown` by definining a class extern(C++) to get a COM compatible vtbl[] and then qualifying each of its methods as extern(Windows) to get COM compatible calling, and writing our methods in the correct COM order, starting with QueryInterface, AddRef, Release, and relying on D to place the methods in the vtbl[] in that order?

No, because the vtbl[] layout is different.


> (Of course, this won't then bless the descendants of such an interface or class, the way the descendents of `core.sys.windows.unknwn.IUnknown` are blessed by a special dispensation in the D language definition, but still. It would be very nice if we could so bless an interface or class ourselves with `extern(COM)`, fixing the vtbl[], calling convention, and method ordering in the vtable rules, for self and any descendant.)
> 
> I ask if we can get these benefits ourselves as above because there's an external world difficulty using `core.sys.windows.unknwn.IUnknown` with the rest of Windows programming outside of just using `core.sys.windows.windows` and suchlike.
> 
> `core.sys.windows.unknwn.IUnknown` forces the definition of `core.sys.windows.basetyps.GUID` into any COM code written using it because the first parameter of `core.sys.windows.unknwn.IUnknown.QueryInterface` is defined to be of type `IID*` and `IID` is defined to be `const(GUID)`, all in `core.sys.windows.basetyps`.
> 
> Now if ImportC is used to bring in Windows COM related headers so as to use the wide collection COM machinery there, a binary-identical GUID type is defined in `guidef.h` and used throughout. Inheriting from `core.sys.windows.unknwn.IUnknown` to make COM objects of type that interface means that QueryInterface will have the wrong type for its first parameter to work with all of Windows COM machinery. Working around this is awkward.

You can invent your own COM-like system in D by using interfaces, but they won't work with Windows COM code.

March 21
On Thursday, 21 March 2024 at 16:06:32 UTC, Walter Bright wrote:
> The C++ and Windows calling convention are different. COM code will not be able to successfully call C++ functions. You can derive a C++ class from a COM interface, but you can't cast a COM interface to a C++ class.

OK, thanks for the clarification.
March 21
On Thursday, 21 March 2024 at 16:07:46 UTC, Walter Bright wrote:
> On 3/21/2024 6:22 AM, Carl Sturtivant wrote:
>> would produce a class with extern(Windows) calling and an extern(C++) COM compatible vtable.
>
> There are two vtables in that example - one for the C++ class, and one for the COM interface. They are not interchangeable with a cast, because the layouts are different.

Good to know! Thank you.