C++ interface vs D and com

Mike Parker via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Tue Jul 12 20:38:03 PDT 2016


On Wednesday, 13 July 2016 at 02:49:54 UTC, Adam Sansier wrote:
> On Wednesday, 13 July 2016 at 02:34:14 UTC, Mike Parker wrote:

>> What happens when you declare an interface that extends from 
>> IUnknown (and not extern(C++)), then cast the pointer returned 
>> from the COM API? It should just work without needing to muck 
>> around with the vtable.
>
> That was what I tried first, It didn't work. I don't know what 
> the problem though. I either get an access violation or the 
> functions don't do anything.

Perhaps you forgot to call CoInitialize{Ex}?


>
> I think it's more complex because without extern(C++) the 
> vtable is in a different place than expected(it's offset by 1), 
> so simple casting does not work.
>
> "A COM interface differs from a regular interface in that there 
> is no object.Interface entry in vtbl[0]; the entries vtbl[0..$] 
> are all the virtual function pointers, in the order that they 
> were declared. This matches the COM object layout used by 
> Windows.
>
> A C++ interface differs from a regular interface in that it 
> matches the layout of a C++ class using single inheritance on 
> the target machine. "

You don't need extern(C++) for COM interfaces. There are several 
declared in the Windows bindings that each inherit from IUnknown 
and there's no extern(C++) in sight (they existed long before C++ 
support did). Here's a working example using one of them, 
IShellLinkW, declared in core.sys.windows.shlobj.

```
import core.sys.windows.windows,
        core.sys.windows.shlobj,
        core.sys.windows.com;

pragma(lib, "Ole32");

void main()
{
     IShellLinkW iface;
     auto shellLinkCLSID = CLSID_ShellLink;
     auto shellLinkIID = IID_IShellLinkW;

     CoInitialize(null);
     scope(exit)CoUninitialize();

     auto hr = CoCreateInstance(
         &shellLinkCLSID,
         null,
         CLSCTX_INPROC_SERVER,
         &shellLinkIID,
         cast(void**)&iface
     );
     if(SUCCEEDED(hr)) {
         import std.stdio : writeln;
         writeln("Got it!");
         iface.Release();
     }
     else throw new Exception("Failed to create IShellLink 
instance");
}
```

There's a minor annoyance here in that the IID constants are all 
declared in the Windows bindings as manifest constants, which is 
normally the smart thing to do with constants. However, they're 
intended to be used as lvalues with the COM API, so I had to save 
them off in local variables in order to take their addresses. You 
can do whatever you want with your own declarations, of course.


More information about the Digitalmars-d-learn mailing list