C++ interface vs D and com
Adam Sansier via Digitalmars-d-learn
digitalmars-d-learn at puremagic.com
Wed Jul 13 09:48:53 PDT 2016
On Wednesday, 13 July 2016 at 08:34:55 UTC, John wrote:
> On Wednesday, 13 July 2016 at 07:31:57 UTC, Adam Sansier wrote:
>> void** ptr = null;
>> auto res = CoCreateInstance(&CLS_ID, cast(IUnknown)null,
>> CLSCTX_INPROC_SERVER, &CLS_ID, cast(void**)&ptr);
>
> How are you casting your "ptr" variable (which BTW should be
> just void* or usually IUnknown) to your interface? A common
> mistake if you're used to C++ is using cast(ISomeInterface*)ptr
> - which would cause access violations - instead of
> cast(ISomeInterface)ptr.
No, I figured that stuff out about the first 5 mins I started
this 3 day journey.
This is the problem, and I believe the solution:
The COM interface I'm using does not use this in the code and
either is "static" no this passed or simply ignores it. I know in
some cases when I would try certain things my arguments to them
would not be correct(pass a char* to get data and get crap back
or crash randomly).
IUnknown requires one to pass the interface as a this.
The mismatch between the two created most of the havoc as I
wrongfully assumed they were all the same and, as typical with
computers, the behavior was not consistent and sometimes would
work in some ways and other times wouldn't.
Regardless, to solve the problem, I believe(No crashes, don't
know if the references are truly handled properly, I have to do
this:
extern (C++) class cASIO
{
extern (C++)
{
int QueryInterface(void*, const(GUID)* riid, void** pvObject)
{
return 0;
}
uint AddRef(void*)
{
return 0;
}
uint Release(void*)
{
return 0;
}
}
extern(C++)
{
void func1(char *name) { }
void func2(int) { }
...
}
}
extern(C++) doesn't pass this or passes it in ECX(Not sure which
yet, I think it doesn't pass this from what I recall about the
disassembly).
I then simply manually pass the interface ptr created by
CoCreateInstance to Release.
Of course, if I could cast the interface ptr directly to a type
with the interface and it all work, then D should handle that
stuff behind the scenes. I tried that initially and it might have
worked or half worked or something. I will go back and try again
knowing what I know now and try to keep everything correct.
So, for those that run into these COM problems in the future:
1. Make sure the linkage is 100% correct per function. IUnknown
uses extern(Windows) but must be passed the correct this or it
will not work well. This can be emulated by marking them
extern(C++) and passing the expected this as first parameter.
The actual interface may not have the same linkage as IUnknown.
If the 0 parameter functions are working but non-zero parameter
functions are not behaving correctly then is a linkage issue.
They should be marked extern(C++) as this gives more control over
what is passed. Passing the correct this as the first parameter
will either work or not, if not, either the function expects it
in ECX or it is some type of static like function. This can be
difficult to know if the function itself doesn't even use this
internally.
One can debug these things and see the vtable in memory and all
the function addresses and be sure the addresses are linking up,
but one can't see the parameter passing as easily. If your sure
the function addresses are correct and your not calling the wrong
addresses, then the problem is a linkage issue.
2. The interface ptr returned by COM should be able to be
mappable to a *COM* interface pointer in D. A normal interface
differs by a COM interface in D the vtable is offset by a ptr. In
this case, your calls may work or not depending on the design but
something will eventually not work causing grief. We have no idea
how to mark an interface as COM in D... someone once said through
the grapevine that it does this if it inherits IUnknown. I
believe that marking it extern(C++) works.
3. There are a lot of pitfalls here because of the way the
different modifiers work. Some combinations may work when they
really don't... Trial and error is a real pain. Making sure the
vtable is correctly being used and then knowing the linkage of
the interface functions should get one at least 50% there. This
assumes different functions in the difference don't use different
linkage... which may create even more havoc. There's a lot of
misinformation on the net. Just because someone got some code to
work in their specific case doesn't mean it is the correct way to
do it for your case.
More information about the Digitalmars-d-learn
mailing list