July 13, 2016
On Tuesday, 12 July 2016 at 23:55:55 UTC, Adam Sansier wrote:

>
> Ok, Another hack:
>
> 		iInterface x;
> 		void** y = cast(void**)&x;
> 		*y = malloc(iInterface.sizeof);
> 		
> 		x.__vptr = cast(immutable(void*)*)(*ptr);
> 		x.func();
>
> works.
>
> x is the object of type iInterface. It has no object associated with it, basically create one using malloc and set it's vtable.
>
> this avoids the need to create the class.

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.
July 13, 2016
On Wednesday, 13 July 2016 at 02:25:35 UTC, Jesse Phillips wrote:
> On Tuesday, 12 July 2016 at 15:09:26 UTC, Adam Sansier wrote:
>> So, com throughs me a interface ptr and I need to map it to an interface. When I do, I get an access violation.
>>
>> I have an (com) ptr and an interface. How do I link them up so I can call the functions?
>>
>> I marked the interface extern(C++) so it's a C++ style interface.
>>  The first field of a COM object is a pointer to its vtable.  This is still true in extern(C++) D, right? The calling convention is thiscall.
>>
>> https://dlang.org/spec/cpp_interface.html
>
> I'm not the best person to answer your questions here, but if you're working with COM you do not want to declare them as extern(C++). D supported COM long before it did direct C++ interfacing.
>
> You can look at how Juno interfaces with Windows COM objects:
>
> https://github.com/JesseKPhillips/Juno-Windows-Class-Library/blob/master/source/juno/xml/msxml.d#L226
>
> You'll notice that it inherits from IDispatch instead of IUnknown, Juno defines that interface.
>
> https://github.com/JesseKPhillips/Juno-Windows-Class-Library/blob/master/source/juno/com/core.d#L2063
>
> Juno tries to make it easier to write and interface with COM, but I've only been keeping it compiling and haven't gotten to writing my own stuff (I've run into issues with manifest files an such). It would be awesome if you found it useful and could improve on the experience.
>
> https://github.com/JesseKPhillips/Juno-Windows-Class-Library/wiki


I think you would have to explain to me why it would be worth switching. At least in my case it is already working and seems to be much easier than a quick casual glance at juno. I don't need a full blown COM lib at this point though and maybe it only works for my use case(Where CoCreateInstance returns the interface ptr directly and no need to query).

I think extern(C++) is necessary because of the calling convention. I could be wrong. extern(Windows) definitely doesn't work. Why the methods I'm using might seem a bit hackish, they are working and is rather simple(a few lines of code), it might not be robust though.

Maybe you could write up a little more on the juno readme.markdown file to explain it's purpose and capabilities.

It seems like it provides server capabilities, which at don't need at the moment(maybe later), and seems quite large. My com code is basically about 10 lines total + the interface.

If you can convince me to try it out, I might... but doing com isn't my primary goal here and I seem to have finished up what I was trying to achieve(my use case is probably relatively simple though). Last thing I want to do is get bogged down in this stuff, which feels clumsy and not well documented(Both the D and C sides)





July 13, 2016
On Wednesday, 13 July 2016 at 02:34:14 UTC, Mike Parker wrote:
> On Tuesday, 12 July 2016 at 23:55:55 UTC, Adam Sansier wrote:
>
>>
>> Ok, Another hack:
>>
>> 		iInterface x;
>> 		void** y = cast(void**)&x;
>> 		*y = malloc(iInterface.sizeof);
>> 		
>> 		x.__vptr = cast(immutable(void*)*)(*ptr);
>> 		x.func();
>>
>> works.
>>
>> x is the object of type iInterface. It has no object associated with it, basically create one using malloc and set it's vtable.
>>
>> this avoids the need to create the class.
>
> 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.

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. "

July 13, 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.
July 13, 2016
On Wednesday, 13 July 2016 at 03:38:03 UTC, Mike Parker wrote:
> 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}?

Nope...

>
>>
>> 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.

You don't have to beleive me, but if I don't mark the methods extern(C++), then only 0 arg methods work.

In fact, Release does not work unless I mark it extern (C++).

So, while you may think it should work one way, and maybe it does for you in some case, it doesn't for me and has given me quite an amount of grief.

Regardless of what you think, I can prove that the code won't work when it is marked extern(Windows) and works when it is marked extern (C++)... so what you should be asking yourself is why it is doing that rather than assuming I'm making it up or doing something wrong.







July 13, 2016
On Wednesday, 13 July 2016 at 06:44:36 UTC, Adam Sansier wrote:

> Regardless of what you think, I can prove that the code won't work when it is marked extern(Windows) and works when it is marked extern (C++)... so what you should be asking yourself is why it is doing that rather than assuming I'm making it up or doing something wrong.

I don't have your code in front of me, nor do I have your system configuration, and can only go on what I know from my own experience. The example I posted works for me. In the past, I have successfully used DirectX bindings with D that were not marked extern(C++). So I *know* that it does work as intended, at least the times I've tried it. That's *my* experience. If it isn't working for you, then there must be a reason. I'm not here to challenge your story or to accuse you of making things up. I'm trying to help you get to the bottom of your problem, i.e. I *am* asking myself why you are having these issues.

So you say the sample code doesn't work for you. Fine. That's one of the reasons I posted it, so we can establish if it does or does not. It would help even more if you could post a minimal example of your own code that other people can attempt to compile, run and/or debug. Otherwise, the only thing anyone *can* do is make guesses and assumptions, and then we get nowhere.
July 13, 2016
On Wednesday, 13 July 2016 at 06:44:36 UTC, Adam Sansier wrote:
> On Wednesday, 13 July 2016 at 03:38:03 UTC, Mike Parker wrote:
>> 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}?
>
> Nope...
>
>>
>>>
>>> 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.
>
> You don't have to beleive me, but if I don't mark the methods extern(C++), then only 0 arg methods work.
>
> In fact, Release does not work unless I mark it extern (C++).
>
> So, while you may think it should work one way, and maybe it does for you in some case, it doesn't for me and has given me quite an amount of grief.
>
> Regardless of what you think, I can prove that the code won't work when it is marked extern(Windows) and works when it is marked extern (C++)... so what you should be asking yourself is why it is doing that rather than assuming I'm making it up or doing something wrong.

Ok, this is the thing.

In C++ I can do

		auto p = *((size_t**)*ptr) + 4;
		typedef size_t(__stdcall *fp)(char*);
		auto f = (fp)*p;
		res = f(n);

to call the 4th function that accepts a char. I can do that for all the functions > 2.

If I call 0 through 2 I get errors, that is from the IUnknown interface.

It seems the interface I'm using is built up of static functions... functions that don't accept a this. Hence they work and extern(C++) works for them, which I guess doesn't pass this.

Because of the hacked vtble stuff, the class used is not the original vtbl which is required for IUnknown.

Hence, they crash because they either get no this or the wrong this.

e.g.,
                // Release the interface
		auto p = *((size_t**)*ptr) + 2;
		typedef size_t(__stdcall *fp)(void*);
		auto f = (fp)*p;
		res = f(ptr);


I'm not entirely sure if all this is correct but the C++ code shows that I don't pass a "this" to the interface functions and it works(if it's passed in ECX and they simply don't use it or if they are "static" or whatever is going on)... not sure which. OTOH, I have to pass something to Release to get it not to crash.

So, any ideas?

void** ptr = null;		
auto res = CoCreateInstance(&CLS_ID, cast(IUnknown)null, CLSCTX_INPROC_SERVER, &CLS_ID, cast(void**)&ptr);

I could check to see if addref and release are working if I could get the number of references. Is this possible? A quick search doesn't bring up anything.


To sum up this confusion:

1. Are the working interface functions I'm calling "static"(no this)? Is that possible? extern(Windows) breaks the code and extern(C++) works. I can call them with a C++ function pointer without this and it works. Not sure if this is passed on the stack or ecx and none of the functions I've called use this so they don't crash.

2. IUnknown's methods are crashing. This seems to be because this is not passed or an invalid this is passed.  This is probably due to the way I am hacking the vtbl but it is the way it was done in the C++ code. I can fix this stuff up without a big problem though as long as I know what to pass for this and possibly check to make sure the reference count decreases.

Thanks..





July 13, 2016
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.
July 13, 2016
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);

See the example above.

IUnknown ptr = null;		
auto res = CoCreateInstance(&MyCLSID, null,
CLSCTX_INPROC_SERVER, &MyIID, cast(void**)&ptr);
if(SUCCEEDED(res))ptr.Release();

Are you sure CLSID matches IID in your case?
July 13, 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.