www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - C++ interface vs D and com

reply Adam Sansier <Adam.Sansier gmail.com> writes:
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
Jul 12 2016
next sibling parent reply Lodovico Giaretta <lodovico giaretart.net> writes:
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 an expert in this field, but did you read this[1]? [1] https://dlang.org/spec/interface.html#com-interfaces
Jul 12 2016
parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
On Tuesday, 12 July 2016 at 15:12:21 UTC, Lodovico Giaretta 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 an expert in this field, but did you read this[1]? [1] https://dlang.org/spec/interface.html#com-interfaces
Yes, of course...
Jul 12 2016
parent reply Lodovico Giaretta <lodovico giaretart.net> writes:
On Tuesday, 12 July 2016 at 21:21:04 UTC, Adam Sansier wrote:
 On Tuesday, 12 July 2016 at 15:12:21 UTC, Lodovico Giaretta 
 wrote:
 I'm not an expert in this field, but did you read this[1]?

 [1] https://dlang.org/spec/interface.html#com-interfaces
Yes, of course...
Well, I asked because you say you marked your interfaces as extern(C++), which is *not* what the spec says you should do. Of course, because you didn't give us much infos on what's not working, we have to guess and point you to some generic resources. Did you read the wiki entry[1], and had a look to some source code[2]? It may be useful to understand if it's a simple mistake on your side or some more deep issue. [1] https://wiki.dlang.org/COM_Programming [2] https://github.com/dlang/druntime/blob/master/src/core/sys/windows/com.d
Jul 12 2016
parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
On Tuesday, 12 July 2016 at 22:01:41 UTC, Lodovico Giaretta wrote:
 On Tuesday, 12 July 2016 at 21:21:04 UTC, Adam Sansier wrote:
 On Tuesday, 12 July 2016 at 15:12:21 UTC, Lodovico Giaretta 
 wrote:
 I'm not an expert in this field, but did you read this[1]?

 [1] https://dlang.org/spec/interface.html#com-interfaces
Yes, of course...
Well, I asked because you say you marked your interfaces as extern(C++), which is *not* what the spec says you should do. Of course, because you didn't give us much infos on what's not working, we have to guess and point you to some generic resources. Did you read the wiki entry[1], and had a look to some source code[2]? It may be useful to understand if it's a simple mistake on your side or some more deep issue. [1] https://wiki.dlang.org/COM_Programming [2] https://github.com/dlang/druntime/blob/master/src/core/sys/windows/com.d
I've spent the last 2 days working on it looking up as much as I can. It's not as if there is any sensible information on it. Trust me... If you haven't done much with it, there is no way you can imagine how convoluted it is... you can pretend, but that doesn't do it justice. Ok, I got it to work but had to completely hack it. This is not the way to go about it though! There must be a better way! 1. Change the interface in to a class, create dummy functions for all members. This is because we must be able to instantiate the interface so we can modify it's vtable ptr. With just the interface, we'll never be able to create the object(maybe malloc will do?) Abstract class won't work either for similar reasons. The layout must look exactly like the interface(in order and include IUnknown methods). 2. change __vtbl ptr: auto interfaceClass = new InterfaceClass(); interfaceClass.__vptr = cast(immutable(void*)*)(*ptr); where ptr is what is returned from CoCreateInstance(if it is a direct object). Essentially we are creating a template with InterfaceClass then swapping it's vtable with the correct one. We technically don't need to recreate the class exactly but it's required for intellisense and compiler semantics. We could use opDispatch, etc. The vtable modification isn't bad, though. That's just a one liner. So, the problem now, is how to take the interface, which is simple, no implementation, and either create the implementation or create a sort of simple empty proxy that can be used to instantiate the interface? Basically the problem now is, that using this method one has to create a class and add stubs for all the methods along with all the inherited methods. This is tedious and they serve no purpose.
Jul 12 2016
parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
 So, the problem now, is how to take the interface, which is 
 simple, no implementation, and either create the implementation 
 or create a sort of simple empty proxy that can be used to 
 instantiate the interface?
I mean automatically of course. I believe D already has some library solution for this but haven't looked them up yet.
Jul 12 2016
parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
On Tuesday, 12 July 2016 at 22:55:05 UTC, Adam Sansier wrote:
 So, the problem now, is how to take the interface, which is 
 simple, no implementation, and either create the 
 implementation or create a sort of simple empty proxy that can 
 be used to instantiate the interface?
I mean automatically of course. I believe D already has some library solution for this but haven't looked them up yet.
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.
Jul 12 2016
parent reply Mike Parker <aldacron gmail.com> writes:
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.
Jul 12 2016
parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
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. "
Jul 12 2016
parent reply Mike Parker <aldacron gmail.com> writes:
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.
Jul 12 2016
parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
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.
Jul 12 2016
next sibling parent Mike Parker <aldacron gmail.com> writes:
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.
Jul 13 2016
prev sibling parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
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..
Jul 13 2016
next sibling parent reply John <johnch_atms hotmail.com> writes:
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.
Jul 13 2016
parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
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.
Jul 13 2016
parent reply Kagamin <spam here.lot> writes:
On Wednesday, 13 July 2016 at 16:48:53 UTC, Adam Sansier wrote:
 There's a lot of misinformation on the net.
Nope, it's just you. COM support in D and in general works fine for everyone else.
Jul 13 2016
parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
On Wednesday, 13 July 2016 at 19:22:44 UTC, Kagamin wrote:
 On Wednesday, 13 July 2016 at 16:48:53 UTC, Adam Sansier wrote:
 There's a lot of misinformation on the net.
Nope, it's just you. COM support in D and in general works fine for everyone else.
For anyone else having similar problems please ignore. COM doesn't work perfectly regardless of what some nobody says and I'm not the only one having problems. There are others and a search shows this. http://forum.dlang.org/thread/iovu2e$2d3d$1 digitalmars.com I notice you are the last one to say "COM uses stdcall convention. Everything else is not COM." Spreading your lies and disinformation. People like you are a source of problems, not a solution. Please learn that you don't know everything and your generalization about how everything works is simply wrong. http://www.digitalmars.com/d/archives/digitalmars/D/learn/thiscall_calling_convention_4943.html Regardless of why the problems exist, or if they are subtle or only in certain scenarios, they exist and some imbecile claiming they don't just makes more trouble for those trying to solve their problems. If you don't like my attitude, stop acting like the world is perfect and when someone has a problem that it just might be legitimate. If you don't actually feel like helping, simply don't reply, it's that easy.
Jul 13 2016
parent reply John <johnch_atms hotmail.com> writes:
On Wednesday, 13 July 2016 at 20:28:40 UTC, Adam Sansier wrote:
 On Wednesday, 13 July 2016 at 19:22:44 UTC, Kagamin wrote:
 On Wednesday, 13 July 2016 at 16:48:53 UTC, Adam Sansier wrote:
 There's a lot of misinformation on the net.
Nope, it's just you. COM support in D and in general works fine for everyone else.
For anyone else having similar problems please ignore. COM doesn't work perfectly regardless of what some nobody says and I'm not the only one having problems. There are others and a search shows this. http://forum.dlang.org/thread/iovu2e$2d3d$1 digitalmars.com I notice you are the last one to say "COM uses stdcall convention. Everything else is not COM." Spreading your lies and disinformation. People like you are a source of problems, not a solution. Please learn that you don't know everything and your generalization about how everything works is simply wrong. http://www.digitalmars.com/d/archives/digitalmars/D/learn/thiscall_calling_convention_4943.html Regardless of why the problems exist, or if they are subtle or only in certain scenarios, they exist and some imbecile claiming they don't just makes more trouble for those trying to solve their problems. If you don't like my attitude, stop acting like the world is perfect and when someone has a problem that it just might be legitimate. If you don't actually feel like helping, simply don't reply, it's that easy.
The problem is that ASIO deviates from the standard way of implementing COM. Other languages like Delphi expect COM to use stdcall too, so the problem is not unique to D. Putting "extern(C++):" before the interface method declarations should allow it to work... interface IASIO : IUnknown { extern(C++): HRESULT SomeMethod(); }
Jul 13 2016
parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
On Wednesday, 13 July 2016 at 20:47:21 UTC, John wrote:
 On Wednesday, 13 July 2016 at 20:28:40 UTC, Adam Sansier wrote:
 On Wednesday, 13 July 2016 at 19:22:44 UTC, Kagamin wrote:
 On Wednesday, 13 July 2016 at 16:48:53 UTC, Adam Sansier 
 wrote:
 There's a lot of misinformation on the net.
Nope, it's just you. COM support in D and in general works fine for everyone else.
For anyone else having similar problems please ignore. COM doesn't work perfectly regardless of what some nobody says and I'm not the only one having problems. There are others and a search shows this. http://forum.dlang.org/thread/iovu2e$2d3d$1 digitalmars.com I notice you are the last one to say "COM uses stdcall convention. Everything else is not COM." Spreading your lies and disinformation. People like you are a source of problems, not a solution. Please learn that you don't know everything and your generalization about how everything works is simply wrong. http://www.digitalmars.com/d/archives/digitalmars/D/learn/thiscall_calling_convention_4943.html Regardless of why the problems exist, or if they are subtle or only in certain scenarios, they exist and some imbecile claiming they don't just makes more trouble for those trying to solve their problems. If you don't like my attitude, stop acting like the world is perfect and when someone has a problem that it just might be legitimate. If you don't actually feel like helping, simply don't reply, it's that easy.
The problem is that ASIO deviates from the standard way of implementing COM. Other languages like Delphi expect COM to use stdcall too, so the problem is not unique to D. Putting "extern(C++):" before the interface method declarations should allow it to work... interface IASIO : IUnknown { extern(C++): HRESULT SomeMethod(); }
NO! If you do that then it's not COM! Put extern(Windows)! It's the only way it can work! Yes! your right, If you were only around to tell me that in the first place! ;) Now we know. Again, as I said before, the problem is informational. Maybe because come works 99% of the time doesn't help us in the 1% if some think 99%=100%(that new math shit that's causing so many problems). I've created a big enough stink about it that anyone in the future having similar issues might be able to resolve them much quicker. Thanks.
Jul 13 2016
next sibling parent Mike Parker <aldacron gmail.com> writes:
On Wednesday, 13 July 2016 at 21:27:29 UTC, Adam Sansier wrote:

 Yes! your right, If you were only around to tell me that in the 
 first place! ;) Now we know. Again, as I said before, the 
 problem is informational. Maybe because come works 99% of the 
 time doesn't help us in the 1% if some think 99%=100%(that new 
 math shit that's causing so many problems). I've created a big 
 enough stink about it that anyone in the future having similar 
 issues might be able to resolve them much quicker.
This demonstrates the importance of showing example code. Everyone who tried to help you in this thread was going on the assumption that you were using a library that does not deviate from standard practice. Personally, I had no idea that there were "COM" libraries that do that. When I did ask for example code, you still didn't post anything relevant, i.e. something that shows your interface declarations or how you were obtaining the COM pointer. Generally, you want to give as much info as possible with these sorts of problems, including compiler, command line options, error messages, and anything you can think of that is relevant. That you were using ASIO should have been in your very first post. Please keep that in mind the next time you have an issue. It may save you a lot of grief.
Jul 13 2016
prev sibling parent Kagamin <spam here.lot> writes:
On Wednesday, 13 July 2016 at 21:27:29 UTC, Adam Sansier wrote:
 Yes! your right, If you were only around to tell me that in the 
 first place! ;) Now we know. Again, as I said before, the 
 problem is informational.
We know it only after you posted links to threads with relevant info. You knew the problem is informational yet decided to conceal the relevant information and you knew it will result in a problem, so you consciously created that problem, it's not the world to blame for that. People in the treads you linked don't do such things, they easily see the details, provide relevant info, even full declaration of the interface, you don't do that, it was your decision to create that problem, it's not the world to blame for that. COM is documented, the world suggests to read docs before you get to anything or when you're in a trouble, you don't do that, and you know it leads to problems, and it's your decision to ask for those problems, that's your attitude - to create problems you suffer from, nobody else does that.
 I've created a big enough stink about it that anyone in the 
 future having similar issues might be able to resolve them much 
 quicker.
You only created lots of whining providing zero information about what's going on, that zero information won't help anyone.
Jul 14 2016
prev sibling parent Kagamin <spam here.lot> writes:
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?
Jul 13 2016
prev sibling next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 12 July 2016 at 15:09:26 UTC, Adam Sansier wrote:
 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.
If it is a COM interface, you should make it a COM interface by inheriting from IUnknown. http://dlang.org/spec/interface.html#com-interfaces
Jul 12 2016
parent Adam Sansier <Adam.Sansier gmail.com> writes:
On Tuesday, 12 July 2016 at 15:13:02 UTC, Adam D. Ruppe wrote:
 On Tuesday, 12 July 2016 at 15:09:26 UTC, Adam Sansier wrote:
 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.
If it is a COM interface, you should make it a COM interface by inheriting from IUnknown. http://dlang.org/spec/interface.html#com-interfaces
Obviously... doesn't help. There is some weirdness with the vtable. In the C++ version it is a simple list of void*'s starting at offset 0. In the D version I haven't figured out what is going on yet. Might just be a simple mistake on my part but something seems aloof. I will continue to work on it, it is difficult without being able to really see what is going on through proper debugging support though and not knowing how D does things differently than C++ under the hood.
Jul 12 2016
prev sibling parent reply Jesse Phillips <Jesse.K.Phillips+D gmail.com> writes:
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
Jul 12 2016
parent reply Adam Sansier <Adam.Sansier gmail.com> writes:
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)
Jul 12 2016
parent reply Jesse Phillips <Jesse.K.Phillips+D gmail.com> writes:
On Wednesday, 13 July 2016 at 02:41:22 UTC, Adam Sansier wrote:
 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)
Juno is definitely of more value if you're trying to access standard Windows COM objects, that way you don't have to write the interface definitions yourself. You'd mentioned that you got it work with a hack and there should be a better way, Juno might be that better way. It looks to me like your extern(C++) is needed because you're trying to shove the pointer from coCreateInstance into a D class. With COM you don't work with concrete classes, only interfaces. Code below is not tested and only from limited knowledge. IMYCASIO cASIO : IUnknown { void func1(char *name); void func2(int); } //... IMYCASIO obj; CoCreateInstance(..., cast(void**) &obj); obj.func1("me"); The only other thing I can think is, if you are obtaining an object that defines static methods, those aren't supported by COM. COM only works on virtual functions and you're probably just hacking memory address space to get at those functions.
Jul 13 2016
parent Adam Sansier <Adam.Sansier gmail.com> writes:
On Wednesday, 13 July 2016 at 20:02:50 UTC, Jesse Phillips wrote:
 On Wednesday, 13 July 2016 at 02:41:22 UTC, Adam Sansier wrote:
 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)
Juno is definitely of more value if you're trying to access standard Windows COM objects, that way you don't have to write the interface definitions yourself. You'd mentioned that you got it work with a hack and there should be a better way, Juno might be that better way. It looks to me like your extern(C++) is needed because you're trying to shove the pointer from coCreateInstance into a D class. With COM you don't work with concrete classes, only interfaces. Code below is not tested and only from limited knowledge. IMYCASIO cASIO : IUnknown { void func1(char *name); void func2(int); } //... IMYCASIO obj; CoCreateInstance(..., cast(void**) &obj); obj.func1("me"); The only other thing I can think is, if you are obtaining an object that defines static methods, those aren't supported by COM. COM only works on virtual functions and you're probably just hacking memory address space to get at those functions.
But juno wouldn't work with this then? Regardless if the "COM" interface I'm using is truly com as defined by some yet unknown standard, it is what I have to work with. I was able to simply the code. I required marking only those functions of the interface as extern(C++), everything else is normal and simple. I was able to simply cast the ptr to the interface and it worked. The thing I tried in the first 5 mins that didn't, probably because I used extern(Windows) like some fools say *HAVE& to be used which wasted 2 days of my life(ok, only a few hours each day but still ;) chasing that rabbit, actually did work. So, the point of this thread is simply to state for those in the future that such discrepancies do exist and are hard to figure out because there is little info on them. This already thwarted one person: http://forum.dlang.org/thread/iovu2e$2d3d$1 digitalmars.com So this is a 10 year problem: 2006-2011-2016. Funny how it happens in 5 year increments. Was Kagamin in on it? Does he have some vested interest in people not getting this stuff to work? Luckily I don't listen to fools, and luckily extern(C++) was implemented since then and we now have a solution.
Jul 13 2016