Any insight appreciated. Details:
>dmd --version
DMD64 D Compiler v2.107.0
Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved written by Walter Bright
References:
class ComObject
interface IUnknown
I only used class ComObject and implicitly the IUnknown interface it inherits from as a test of some COM code I was writing using comheaders.c
containing the following, compiled with ImportC.
#define WINVER 0x0A00
#define _WIN32_WINNT 0x0A00
#define _WIN32_DCOM
#include <wtypes.h>
#include <oleauto.h>
#include <oaidl.h>
The file main.d
compiled with it is as follows.
import std.stdio;
import comheaders;
static import com = core.sys.windows.com;
pragma(lib, "onecore"); //to fix linkage of two irrelevant symbols
void main() {
auto COMobject = new com.ComObject();
//auto COMobject = new ComObject();
IUnknown* ip = cast(IUnknown*)COMobject;
writeln(COMobject.count);
writeln(" ip vtable: ", ip.lpVtbl);
auto vtable = COMobject.__vptr;
writeln("COMobject vtable: ", vtable);
writeln("ip &AddRef: ", &ip.lpVtbl.AddRef);
writeln("ip offset: ", cast(void*)&ip.lpVtbl.AddRef - cast(void*)ip.lpVtbl);
auto ipaddref = cast(void*)ip.lpVtbl.AddRef;
writeln(" ip AddRef: ", ipaddref);
auto addref = cast(void*)(&COMobject.AddRef).funcptr;
writeln("COMobject AddRef: ", addref);
writeln("COMobject AddRef : ip AddRef offset: ", addref - ipaddref);
COMobject.AddRef();
writeln(COMobject.count);
ip.lpVtbl.AddRef(ip);
writeln(COMobject.count);
}
Here I make a ComObject
from the statically imported core.sys.windows.com
but avoid using anything else from the D windows libs. The object contains a reference count, that should be incremented with a call of AddRef. The output was as follows.
0
ip vtable: 7FF756091A30
COMobject vtable: 7FF756091A30
ip &AddRef: 7FF756091A38
ip offset: 8
ip AddRef: 7FF756027970
COMobject AddRef: 7FF756022EC0
COMobject AddRef : ip AddRef offset: -19120
1
1
This shows that the call of AddRef with the correct offset does nothing, and is a different function pointer to that of AddRef in the com.ComObject. So that object will apparently not work correctly with outside world code as claimed (see the above reference links). This was compiled with
dmd main.d comheaders.c vcintrinsics.lib -P/wd5105
where vcintrinsics.lib is a library I constructed to fix a problem with DMD not knowing of a series of MSVC intrinsics, i.e. to satify the linker as per here, and -P/wd5105 is to suppress a warning from MSVC when it is used by ImportC for as a C preprocessor.
I copied the source of the inconveniently named interface IUnknown
in unknwn.d
and of class ComObject
from com.d
, both in C:\D\dmd2\src\druntime\src\core\sys\windows\
into the bottom of main.d and experimented. I made a local ComObject with the commented out line above active. One change fixed the problem: using extern(C++) --- no other linking attribute worked. Here's the working code. I had to edit IUnknown
which was an interface but is now a struct in comheaders.c
, into IUnknown*
inside QueryInterface, and E_NOINTERFACE
into com.E_NOINTERFACE
and define the D interface with a name different to the struct IUnknown
so I made it _IUnknown_
but otherwise the source is unchanged apart from not using extern(Windows) and prefixing it all with extern(C++). Here is the rest of main.d
.
import core.atomic;
extern(C++):
interface _IUnknown_ {
HRESULT QueryInterface(IID* riid, void** pvObject);
ULONG AddRef();
ULONG Release();
}
class ComObject : _IUnknown_
{
HRESULT QueryInterface(const(IID)* riid, void** ppv)
{
if (*riid == IID_IUnknown)
{
*ppv = cast(void*)cast(IUnknown*)this;
AddRef();
return S_OK;
}
else
{ *ppv = null;
return com.E_NOINTERFACE;
}
}
ULONG AddRef()
{
return atomicOp!"+="(*cast(shared)&count, 1);
}
ULONG Release()
{
LONG lRef = atomicOp!"-="(*cast(shared)&count, 1);
if (lRef == 0)
{
// free object
// If we delete this object, then the postinvariant called upon
// return from Release() will fail.
// Just let the GC reap it.
//delete this;
return 0;
}
return cast(ULONG)lRef;
}
LONG count = 0; // object reference count
}
The output is now as follows.
0
ip vtable: 7FF76B9C0360
COMobject vtable: 7FF76B9C0360
ip &AddRef: 7FF76B9C0368
ip offset: 8
ip AddRef: 7FF76B951580
COMobject AddRef: 7FF76B951580
COMobject AddRef : ip AddRef offset: 0
1
2
showing that finding AddRef
in the ComObject
s Vtable produces the same function pointer as that through the COM Interface, and both work.
My working hypothesis: both ComObject
and IUnknown
brought in by importing core.sys.windows.com
are broken which is all of the support for COM in Phobos. Please confirm or deny.