www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - COM Expertise needed: COM Callbacks

reply Nierjerson <Nierjerson somewhere.com> writes:
Still trying to get the com automation code working. This is a 
general issue with COM programming as I do not have the 
experience to solve the problem.

There are two files at:

http://s000.tinyupload.com/index.php?file_id=33201858563271816000

Gen.d is the automatically generated COM wrapper in D for 
photoshop.

main.d is the test code.

You'll need windows and photoshop to make use of the code.

The idea is simple. Set the RGBColor foreground color using one 
of the COM methods in cSolidColor. We can set the color using 
Red, Green, or Blue and doubles and it works because one can pass 
a simple type without any marshaling.

But if one wants to set all three using the RGBColor inteface, it 
requires more work; and this is where I'm stuck at.

What I have done is create a simple router that brings all the 
work in to main.d.

The first method is `void RGB(icRGBColor, cSolidColor)` and it 
simply attempts to invoke PS's SolidColor's RGB method to have it 
set the colors.

When we call this method, it passes our interface that we want 
photoshop to use as to get at the colors and set them.

The second method(s) is basically in icRGBColor. This class is 
suppose to represent the callback interface. Photoshop is suppose 
to call these methods to get the colors and such.


The idea is that we pass our icRGBColor instance to invoke which 
informs PS that we want to set the color using the 
interface(rather than say a double). PS then calls the 
appropriate methods on the interface to do what it needs to 
do(e.g., Red() to get the red value to set).

At least I believe that is how it goes.

When we call the invoke function eventually the QueryInterface is 
called, which is good because this means that PS(or COM) must 
have called it and this is probably what it is suppose to do.

the problem is, that the code crashes and I can't get it past 
that obstacle. What I'd like to do is get to the point where we 
can get PS to set the colors using that interface(icRGBColor). 
Right now I just have dummy returns and have it print to the 
console when the method is called, which is good enough for 
getting the kinks worked out.

Anyone that basically has more of a clue than I do mind giving it 
a look over and see if they can figure out how to get it to work. 
It's relatively simple. Only main.d really needs to be messed 
with and that's just about 50 lines of code(and only a few are 
actually relevant(icRGBColor.QueryInterface, RGB() and a few of 
the lines at the end of main).

Once this is figured out I should have a near complete automation 
of COM in D. This allows one to simply run the tool on the .idl 
file and it will generate a D file with all the machinery to use 
the COM back end.



Thanks.
Apr 23 2017
next sibling parent reply MGW <mgw yandex.ru> writes:
On Monday, 24 April 2017 at 00:55:45 UTC, Nierjerson wrote:
 Still trying to get the com automation code working. This is a
Please, use ZIP for archive.
Apr 24 2017
parent Nierjerson <Nierjerson somewhere.com> writes:
On Monday, 24 April 2017 at 17:31:09 UTC, MGW wrote:
 On Monday, 24 April 2017 at 00:55:45 UTC, Nierjerson wrote:
 Still trying to get the com automation code working. This is a
Please, use ZIP for archive.
http://s000.tinyupload.com/index.php?file_id=67286353487198133918
Apr 24 2017
prev sibling parent reply Atila Neves <atila.neves gmail.com> writes:
On Monday, 24 April 2017 at 00:55:45 UTC, Nierjerson wrote:
 Still trying to get the com automation code working. This is a 
 general issue with COM programming as I do not have the 
 experience to solve the problem.

 [...]
I tried looking at this because I just did some COM work even if most of it in C++. There's a _lot_ of code and I was instantly lost, even with your explanations. I'd forget about Photoshop for now, just write a simple COM client. That'd also ease in getting other people to help because having Photoshop is a high barrier for entry. Can you get this to work in C++?
Apr 25 2017
parent reply Nierjerson <Nierjerson somewhere.com> writes:
On Tuesday, 25 April 2017 at 10:03:30 UTC, Atila Neves wrote:
 On Monday, 24 April 2017 at 00:55:45 UTC, Nierjerson wrote:
 Still trying to get the com automation code working. This is a 
 general issue with COM programming as I do not have the 
 experience to solve the problem.

 [...]
I tried looking at this because I just did some COM work even if most of it in C++. There's a _lot_ of code and I was instantly lost, even with your explanations.
There isn't a lot of code! All the code in Gen is irrelevant except for a few functions... even then, you don't ever really need to mess with it except to see a few of the definitions. 99.9% of the code is irrelevant, I could have removed most of it and it would function the same. The reason why I left it in is for completeness and because many of the different classes reference other classes so having one sorta requires having all the rest(even though they are never used, it is to avoid missing symbols, etc, which can be fixed but would require some editing and would prevent the code from working). Only three classes are used is used in Gen.d: cApplication, cSolidColor, and cRGBColor. One only really needs to look at main.d. I've put all the code necessary in that file. One only needs to look at Gen.d to get the general idea and at that, only those specific classes.
 I'd forget about Photoshop for now, just write a simple COM 
 client. That'd also ease in getting other people to help 
 because having Photoshop is a high barrier for entry.
Photoshop is the main app I'm trying to get to work so this is appropriate for me and it is also the only real way I have to test COM.
 Can you get this to work in C++?
The code generation probably could be tailored to work in C++ without too much issue. I'm not going to do it though! Too much work as it is and I first need to get this to work. I don't see any real difference between D and C++(if I'm going to use C++ for anything I'm going to use D instead). I've already had to rewrite the engine because D's CTFE could not handle the load. So this has turned out to be quite a bit of work. --- The main issue has nothing to do with PS though(I imagine it is a general COM programming task). The server(PS in this case) gives me an interface that has functions like RGB(cRGBColor rgb); (Actually a _RGBColor com interface which I wrap with a cRGBColor interface to provide the ability to handle the marshaling and such behind the scenes), So I pass it a cRGBcolor interface, which actually can't be used because photoshop has no clue about that type. Instead, I construct a dummy interface that inherits from IDispatch which I'll pass to server instead. I'll use it as a wrapper to hook up everything I need. The problem is, when I do this, the app crashes. I do not know why. What I do know is that it seems to be calling QueryInterface and so it seems like it might actually be getting to the server and the server is calling the QueryInterface method. If that is the case, then the passing of the interface is working but something else is failing. I'm just not sure though as I don't know enough about COM to determine what exactly is going on(after all, it's all happening on the server "behind closed doors"). .... auto rgb2 = new icRGBColor(); rgb2.iDispatch = rgb2; //auto rgb = cast(icRGBColor)&rgb2; //dd.RGB(cast(cRGBColor)(cast(void*)rgb)); //dd.RGB(cast(icRGBColor)(cast(void*)rgb)); cSolidColor.RGBSet = &RGB; auto rgb = new icRGBColor(); //auto rgb = cast(icRGBColor)(cast(void*)dd.RGB()); dd.RGB(rgb); // <--- This is where the work begins! Tries to get photoshop to set the RGB color through the rgb object return 0; } // This is the invoker.Only useful for debugging(brought out from Gen.d here for convenience // The invoke line is what is important. When it is called, photoshop will be given the icRGBColor object and will do things with it(if it works, that is... seems to and seems to call QueryInterface but doesn't get any further). void RGB(icRGBColor ic, cSolidColor s) { import main; EXCEPINFO exception; uint argErr = 0; auto iidNULL = IID_NULL; auto RT = new SafeVariantPtr(); VARIANT[1] paramVars; DISPPARAMS params = {rgvarg: paramVars.ptr, cArgs: 1, cNamedArgs: 0}; auto ID = s.COMMethodIDs[`RGB`]; paramVars[0].punkVal = ic; paramVars[0].vt = VARENUM.VT_DISPATCH; scope(exit) VariantClear(&paramVars[0]); auto res = s.iDispatch.Invoke(cast(int)ID, &iidNULL, 0, DISPATCH_PROPERTYPUT, &params, cast(VARIANT*)RT, &exception, &argErr); assert(res == S_OK, `Could not invoke COM Function cSolidColor.RGB. Error `~to!string(res, 16)); } ... The icRGBColor interface that follows is a simple callback interface. All the methods simply print their function name when called, this helps to know when they are called. When one runs the program they should see a few calls to QueryInterface. Either photoshop is calling this method on purpose or by accident.(calling something else but the vtable is wrong) Hope that helps some. I only need some advice, guidance on how things are suppose to work so I don't wait a lot of time going down a blind alley.
Apr 25 2017
parent reply John Chapman <johnch_atms hotmail.com> writes:
On Tuesday, 25 April 2017 at 18:39:56 UTC, Nierjerson wrote:
 void RGB(icRGBColor ic, cSolidColor s)
 {
 	import main;
 	EXCEPINFO exception;
 	uint argErr = 0;
 	auto iidNULL = IID_NULL;
 	auto RT = new SafeVariantPtr();
 	VARIANT[1] paramVars;
 	DISPPARAMS params = {rgvarg: paramVars.ptr, cArgs: 1, 
 cNamedArgs: 0};
 	auto ID = s.COMMethodIDs[`RGB`];
 	paramVars[0].punkVal = ic; paramVars[0].vt = 
 VARENUM.VT_DISPATCH; scope(exit) VariantClear(&paramVars[0]);
 	auto res = s.iDispatch.Invoke(cast(int)ID, &iidNULL, 0, 
 DISPATCH_PROPERTYPUT, &params, cast(VARIANT*)RT, &exception, 
 &argErr);
 	assert(res == S_OK, `Could not invoke COM Function 
 cSolidColor.RGB. Error `~to!string(res, 16));

 }
When you use DISPATCH_PROPERTYPUT you need to set cNamedArgs and rgdispidNamedArgs like so: int dispidNamed = DISPID_PROPERTYPUT; params.cNamedArgs = 1; params.rgdispidNamedArgs = &dispidNamed; You should also call AddRef on any COM objects you add to your paramVars array.
Apr 26 2017
parent reply Nierjerson <Nierjerson somewhere.com> writes:
On Wednesday, 26 April 2017 at 15:30:37 UTC, John Chapman wrote:
 On Tuesday, 25 April 2017 at 18:39:56 UTC, Nierjerson wrote:
 [...]
When you use DISPATCH_PROPERTYPUT you need to set cNamedArgs and rgdispidNamedArgs like so: int dispidNamed = DISPID_PROPERTYPUT; params.cNamedArgs = 1; params.rgdispidNamedArgs = &dispidNamed; You should also call AddRef on any COM objects you add to your paramVars array.
Did you try this? I tried and same issue. Are you sure the above is required? I'm not using any "named" args so not sure why it should matter?
Apr 26 2017
parent reply John Chapman <johnch_atms hotmail.com> writes:
On Wednesday, 26 April 2017 at 23:04:53 UTC, Nierjerson wrote:
 On Wednesday, 26 April 2017 at 15:30:37 UTC, John Chapman wrote:
 On Tuesday, 25 April 2017 at 18:39:56 UTC, Nierjerson wrote:
 [...]
When you use DISPATCH_PROPERTYPUT you need to set cNamedArgs and rgdispidNamedArgs like so: int dispidNamed = DISPID_PROPERTYPUT; params.cNamedArgs = 1; params.rgdispidNamedArgs = &dispidNamed; You should also call AddRef on any COM objects you add to your paramVars array.
Did you try this? I tried and same issue. Are you sure the above is required? I'm not using any "named" args so not sure why it should matter?
From the documentation (https://msdn.microsoft.com/en-us/library/windows/desktop/ms221479(v=vs.85).aspx): "When you use IDispatch::Invoke() with DISPATCH_PROPERTYPUT or DISPATCH_PROPERTYPUTREF, you have to specially initialize the cNamedArgs and rgdispidNamedArgs elements of your DISPPARAMS structure" Thought it might help.
Apr 27 2017
parent reply Nierjerson <Nierjerson somewhere.com> writes:
On Thursday, 27 April 2017 at 07:51:26 UTC, John Chapman wrote:
 On Wednesday, 26 April 2017 at 23:04:53 UTC, Nierjerson wrote:
 On Wednesday, 26 April 2017 at 15:30:37 UTC, John Chapman 
 wrote:
 On Tuesday, 25 April 2017 at 18:39:56 UTC, Nierjerson wrote:
 [...]
When you use DISPATCH_PROPERTYPUT you need to set cNamedArgs and rgdispidNamedArgs like so: int dispidNamed = DISPID_PROPERTYPUT; params.cNamedArgs = 1; params.rgdispidNamedArgs = &dispidNamed; You should also call AddRef on any COM objects you add to your paramVars array.
Did you try this? I tried and same issue. Are you sure the above is required? I'm not using any "named" args so not sure why it should matter?
From the documentation (https://msdn.microsoft.com/en-us/library/windows/desktop/ms221479(v=vs.85).aspx): "When you use IDispatch::Invoke() with DISPATCH_PROPERTYPUT or DISPATCH_PROPERTYPUTREF, you have to specially initialize the cNamedArgs and rgdispidNamedArgs elements of your DISPPARAMS structure" Thought it might help.
Ok, thanks. I will try again, Maybe I did something wrong. It seems though that it isn't necessary, at least in my specific test case since the same behavior was seen(although it might have been a future bug). I think the main issue though, is that I really don't know what is going on when I invoke the PS function. It seems to call the server method that takes the interface and then the server does it's "magic"(which is calling my QueryInterface) but how the implemented QueryInterface is suppose to respond is beyond me... I've tried some based stuff but nothing seem to work. The good news is that it is doing something(calling QueryInterface) which means that the server is at work. Any more ideas? I think the issue currently is is the QueryInterface(it is simply not doing what it is suppose to). I'll probably have to look at some other implementations to see what is going on.
Apr 27 2017
parent reply John Chapman <johnch_atms hotmail.com> writes:
On Thursday, 27 April 2017 at 20:20:23 UTC, Nierjerson wrote:
 I think the main issue though, is that I really don't know what 
 is going on when I invoke the PS function. It seems to call the 
 server method that takes the interface and then the server does 
 it's "magic"(which is calling my QueryInterface) but how the 
 implemented QueryInterface is suppose to respond is beyond 
 me... I've tried some based stuff but nothing seem to work. The 
 good news is that it is doing something(calling QueryInterface) 
 which means that the server is at work.

 Any more ideas?  I think the issue currently is is the 
 QueryInterface(it is simply not doing what it is suppose to). 
 I'll probably have to look at some other implementations to see 
 what is going on.
QueryInterface is COM's version of opCast. It asks if you support the interface represented by an IID (riid). If you don't, then you return E_NOINTERFACE. If you do, then you point the result (pvObject) to yourself and return S_OK. Here's a basic implementation: extern(Windows) HRESULT QueryInterface(IID* riid, void** pvObject) { if (pvObject is null) return E_POINTER; *pvObject = null; if (*riid == IID_IUnknown) *pvObject = cast(void*)cast(IUnknown)this; else if (*riid == IID_IDispatch) *pvObject = cast(void*)cast(IDispatch)this; // and so on for all interfaces we support if (*pvObject is null) return E_NOINTERFACE; (cast(IUnknown)this).AddRef(); return S_OK; } AddRef/Release perform the COM object's reference counting, so you should implement them too. However, I don't understand why your icRBCColor class both implements and encapsulates IDispatch - the generated version cRGBColor from Gen.d just encapsulates it. Why do you need to instantiate an instance of icRGBColor? Can't you just use the rgb1 object you got from the dd.RGB() getter, assign the colour values to its Red, Blue, Green properties as needed, then call the dd.RGB(rgb1) setter? Does that not work?
Apr 28 2017
next sibling parent Nierjerson <Nierjerson somewhere.com> writes:
On Friday, 28 April 2017 at 09:25:31 UTC, John Chapman wrote:
 On Thursday, 27 April 2017 at 20:20:23 UTC, Nierjerson wrote:
 I think the main issue though, is that I really don't know 
 what is going on when I invoke the PS function. It seems to 
 call the server method that takes the interface and then the 
 server does it's "magic"(which is calling my QueryInterface) 
 but how the implemented QueryInterface is suppose to respond 
 is beyond me... I've tried some based stuff but nothing seem 
 to work. The good news is that it is doing something(calling 
 QueryInterface) which means that the server is at work.

 Any more ideas?  I think the issue currently is is the 
 QueryInterface(it is simply not doing what it is suppose to). 
 I'll probably have to look at some other implementations to 
 see what is going on.
QueryInterface is COM's version of opCast. It asks if you support the interface represented by an IID (riid). If you don't, then you return E_NOINTERFACE. If you do, then you point the result (pvObject) to yourself and return S_OK. Here's a basic implementation: extern(Windows) HRESULT QueryInterface(IID* riid, void** pvObject) { if (pvObject is null) return E_POINTER; *pvObject = null; if (*riid == IID_IUnknown) *pvObject = cast(void*)cast(IUnknown)this; else if (*riid == IID_IDispatch) *pvObject = cast(void*)cast(IDispatch)this; // and so on for all interfaces we support if (*pvObject is null) return E_NOINTERFACE; (cast(IUnknown)this).AddRef(); return S_OK; }
Ok, I tried this and returned this but it didn't work. I didn't clal AddRef though, but maybe I was in the right direction but had some other issue.
 AddRef/Release perform the COM object's reference counting, so 
 you should implement them too.

 However, I don't understand why your icRBCColor class both 
 implements and encapsulates IDispatch - the generated version 
 cRGBColor from Gen.d just encapsulates it. Why do you need to 
 instantiate an instance of icRGBColor? Can't you just use the 
 rgb1 object you got from the dd.RGB() getter, assign the colour 
 values to its Red, Blue, Green properties as needed, then call 
 the dd.RGB(rgb1) setter? Does that not work?
cRGBColor and any class in Gen.d are not COM interfaces and can't be used. All they do is wrap the dynamic method invocation work that is required to interact with PS. It may work that I could use them to set the values and then reset the instance but I feel that seems to be a waste. There is no need to really set the object if one is using the property setters. Although my method of creating a "direct" COM interface wrapper(one that inherits from IDispatch and simply delegates the work to the original com) seems to do basically the same, I plan on converting those COM Properties in to D properties and maintain state on both ends that are always in sync(possibly a bit slow, but should be easy to implement and allow one to work with COM in a more D like fashion. I could, for example, just have those "c" classes inherit from IDispatch directly and then pass on the IDispatch COM calls to the underlying COM interface(which is stored in the c class). That would be a sort of combination of the two methods above and might be better as it avoids actually having to get any of the callback COM stuff working(the stuff I'm having a problem with now). I just wasn't sure if this method would work and didn't want to mess with the gen code too much before I had a better understanding of what is going on. Regardless, getting the above code to work is still important as it will explain to me how it should work so that I understand for future purposes if this method does not go that route. Thanks, I'll try to get it to work a again and report back.
Apr 28 2017
prev sibling next sibling parent Nierjerson <Nierjerson somewhere.com> writes:
On Friday, 28 April 2017 at 09:25:31 UTC, John Chapman wrote:
 On Thursday, 27 April 2017 at 20:20:23 UTC, Nierjerson wrote:

 QueryInterface is COM's version of opCast. It asks if you 
 support the interface represented by an IID (riid). If you 
 don't, then you return E_NOINTERFACE. If you do, then you point 
 the result (pvObject) to yourself and return S_OK. Here's a 
 basic implementation:

 extern(Windows)
 HRESULT QueryInterface(IID* riid, void** pvObject) {
   if (pvObject is null) return E_POINTER;
   *pvObject = null;

   if (*riid == IID_IUnknown) *pvObject = 
 cast(void*)cast(IUnknown)this;
   else if (*riid == IID_IDispatch) *pvObject = 
 cast(void*)cast(IDispatch)this;
   // and so on for all interfaces we support

   if (*pvObject is null) return E_NOINTERFACE;
   (cast(IUnknown)this).AddRef();
   return S_OK;
 }
Your code works. I had something similar but I think I was returning a null pointer on IID_Unknown or something similar. I now get a different error but it is a COM error rather than access violation. (error 80020009)
Apr 28 2017
prev sibling parent reply Mike B Johnson <Mikey Ikes.com> writes:
On Friday, 28 April 2017 at 09:25:31 UTC, John Chapman wrote:
 On Thursday, 27 April 2017 at 20:20:23 UTC, Nierjerson wrote:
 I think the main issue though, is that I really don't know 
 what is going on when I invoke the PS function. It seems to 
 call the server method that takes the interface and then the 
 server does it's "magic"(which is calling my QueryInterface) 
 but how the implemented QueryInterface is suppose to respond 
 is beyond me... I've tried some based stuff but nothing seem 
 to work. The good news is that it is doing something(calling 
 QueryInterface) which means that the server is at work.

 Any more ideas?  I think the issue currently is is the 
 QueryInterface(it is simply not doing what it is suppose to). 
 I'll probably have to look at some other implementations to 
 see what is going on.
QueryInterface is COM's version of opCast. It asks if you support the interface represented by an IID (riid). If you don't, then you return E_NOINTERFACE. If you do, then you point the result (pvObject) to yourself and return S_OK. Here's a basic implementation: extern(Windows) HRESULT QueryInterface(IID* riid, void** pvObject) { if (pvObject is null) return E_POINTER; *pvObject = null; if (*riid == IID_IUnknown) *pvObject = cast(void*)cast(IUnknown)this; else if (*riid == IID_IDispatch) *pvObject = cast(void*)cast(IDispatch)this; // and so on for all interfaces we support if (*pvObject is null) return E_NOINTERFACE; (cast(IUnknown)this).AddRef(); return S_OK; } AddRef/Release perform the COM object's reference counting, so you should implement them too.
I wrapped the COM interface that is returned by PS and the queryInterface works and everything passes(as it should) but when I use your query interface it results in the COM error specified. Seems that the QueryInterface code is not 100% correct or something else is going on. What I notices is that when PS's QueryInterface routine is used, it is called only 3 times(queries 3 interfaces like IUnknown, etc) while when I use your code about 10 interfaces are queried(including those 3 from PS). PS's RGB QueryInterface QueryInterface Called: 00000003-0000-0000-C00-000000000046(GUID), 19F178(Object) QueryInterface Called: ECC8691B-C1DB-4DC0-855E-65F6C551AF49(GUID), 19F124(Object) QueryInterface Called: 00000003-0000-0000-C00-000000000046(GUID), 19EFE8(Object) main.icRGBColor.Release Yours QueryInterface Called: 00000003-0000-0000-C00-000000000046(GUID), 19F178(Object) Not Supported QueryInterface Called: ECC8691B-C1DB-4DC0-855E-65F6C551AF49(GUID), 19F124(Object) Not Supported QueryInterface Called: 00000003-0000-0000-C00-000000000046(GUID), 19EFE8(Object) Not Supported QueryInterface Called: 0000001B-0000-0000-C00-000000000046(GUID), 19F00C(Object) Not Supported QueryInterface Called: 00000000-0000-0000-C00-000000000046(GUID), 19F040(Object) IUnknown Supported main.icRGBColor.AddRef QueryInterface Called: 00000018-0000-0000-C00-000000000046(GUID), 19EF1C(Object) Not Supported QueryInterface Called: 334D391F-0E79-3B15-C9FF-EAC65DD07C42(GUID), 19EEF4(Object) Not Supported QueryInterface Called: 00000040-0000-0000-C00-000000000046(GUID), 19EF30(Object) Not Supported QueryInterface Called: 334D391F-0E79-3B15-C9FF-EAC65DD07C42(GUID), 19EEF4(Object) Not Supported QueryInterface Called: 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90(GUID), 19EF34(Object) Not Supported QueryInterface Called: 334D391F-0E79-3B15-C9FF-EAC65DD07C42(GUID), 19EEF4(Object) Not Supported QueryInterface Called: 77DD1250-139C-2BC3-BD95-900ACED61BE5(GUID), 19EF2C(Object) Not Supported QueryInterface Called: 334D391F-0E79-3B15-C9FF-EAC65DD07C42(GUID), 19EEF4(Object) Not Supported QueryInterface Called: BFD60505-5A1F-4E41-88BA-A6FB07202DA9(GUID), 19EF28(Object) Not Supported QueryInterface Called: 334D391F-0E79-3B15-C9FF-EAC65DD07C42(GUID), 19EEF4(Object) Not Supported QueryInterface Called: 03FB5C57-D534-45F5-A1F4-D39556983875(GUID), 19EF24(Object) Not Supported QueryInterface Called: 334D391F-0E79-3B15-C9FF-EAC65DD07C42(GUID), 19EEF4(Object) Not Supported QueryInterface Called: 2C258AE7-50DC-49FF-9D1D-2ECB9A52CDD7(GUID), 19EF20(Object) Not Supported QueryInterface Called: 00000019-0000-0000-C00-000000000046(GUID), 2B5A3C0(Object) Not Supported QueryInterface Called: 4C1E39E1-E3E3-4296-AA86-EC938D896E92(GUID), 19EF18(Object) Not Supported main.icRGBColor.Release QueryInterface Called: 00020400-0000-0000-C00-000000000046(GUID), 19ED8C(Object) IDispatch Supported main.icRGBColor.AddRef QueryInterface Called: F37B4894-3ED2-48AF-AD38-BB1B27E93869(GUID), 2D3EAAC(Object) Not Supported QueryInterface Called: 00000003-0000-0000-C00-000000000046(GUID), 19EFE8(Object) I don't know if things are passed off to another query method or what. The query interface I use is // IUnknown int QueryInterface(const(GUID)* riid, void** pvObject) { write("QueryInterface Called: ", Guid2Str(*riid), "(GUID), ", pvObject, "(Object)"); //version(wrap) { writeln(); return rgb.iDispatch.QueryInterface(riid, pvObject); } // Always set out parameter to NULL, validating it first. if (pvObject is null) return E_POINTER; *pvObject = null; if (*riid == IID_IUnknown) { *pvObject = cast(void*)cast(IUnknown)this; writeln("\tIUnknown Supported"); } else if (*riid == IID_IDispatch) { *pvObject = cast(void*)cast(IDispatch)this; writeln("\tIDispatch Supported"); } // and so on for all interfaces we support if (*pvObject is null) { writeln("\tNot Supported"); return E_NOINTERFACE; } //(cast(IUnknown)this).AddRef(); return S_OK; } Any ideas where to proceed?
May 03 2017
parent Mike B Johnson <Mikey Ikes.com> writes:
I've modified the code and it seems to call GetTypeInfo but the 
values passed seem frivolous. The modified code I'm using is 
below. Maybe we can get this to work? I'm interested in a 
photoshop interop too. Seems like it should be rather trivial to 
get to work but things don't add up ;/






module main;
import std.stdio;

import core.sys.windows.winnt;

import Gen;

string Guid2Str(GUID guid)
{
	import std.conv, std.string, std.algorithm;
	auto bytes = cast(byte[16])guid;
	auto a = to!string(*(cast(DWORD*)(bytes[0..4])), 
16).rightJustify(8, '0');
	auto b = to!string(*(cast(WORD*)(bytes[4..6])), 
16).rightJustify(4, '0');
	auto c = to!string(*(cast(WORD*)(bytes[6..8])), 
16).rightJustify(4, '0');
	auto d = to!string(*(cast(BYTE*)(bytes[8..9])), 
16).rightJustify(1, '0');
	auto e = to!string(*(cast(BYTE*)(bytes[9..10])), 
16).rightJustify(1, '0');
	auto q = bytes[10..16];
	reverse(q);
	auto f = to!string(*(cast(long*)(q)) & 0xFFFFFFFFFFFF, 
16).rightJustify(12, '0');

	auto ret = a~"-"~b~"-"~c~"-"~d~e~"-"~f;
	return ret;
}


// Creates a guid from a guid hex string. e.g., 
"B3C35001-B625-48D7-9D3B-C9D66D9CF5F1" -> {0xC09F153E, 0xDFF7, 
0x4EFF, [0xA5, 0x70, 0xAF, 0x82, 0xC1, 0xA5, 0xA2, 0xA8]}
GUID GuidN(string str)
{
	// 3   2   1        5E5C5D5C5B5A4L4H
	// 4dc0c1dbecc8691b 49af51c5f6655e85
	//DEFINE_GUID(IID_INoMarshal,0xecc8691b,0xc1db,0x4dc0,0x85,0x5e,0x65,0xf6,0xc5,0x51,0xaf,0x49);
	//MIDL_INTERFACE("ecc8691b-c1db-4dc0-855e-65f6c551af49")

	import std.string;
	GUID guid;
	auto bytes = cast(byte*)(cast(void*)&guid);
	if (str == "") return IID.init;
	auto parts = split(str, "-"); auto p3 = parts[3].rightJustify(4, 
'0'); auto p4 = parts[4].rightJustify(12, '0');

	*(cast(long*)(bytes[0..8])) = to!long(parts[2].rightJustify(4, 
'0')~parts[1].rightJustify(4, '0')~parts[0].rightJustify(8, '0'), 
16);	
	*(cast(long*)(bytes[8..16])) = 
to!long(p4[10..$]~p4[8..10]~p4[6..8]~p4[4..6]~p4[2..4]~p4[0..2
~p3[2..$]~p3[0..2], 16);

	return guid;
}

int main(string[] argv)
{
	

	// Issue QueryInterface seems to have the issue(not the same 
behavior as what is returned by the PS COM Interface)
	
	auto x = new cApplication();				
	auto x1 = x.Path();
	auto x2 = x.Name();	
	auto x3 = x.SystemInformation();
	auto x4 = x.Version();
	auto e = x.RecentFiles();
	auto g = x.DisplayDialogs();
	auto x5 = x.Documents();
	auto x6 = x5.Count();	


	auto dd = x.ForegroundColor();
	auto __red = dd.RGB().Red();
	auto __green = dd.RGB().Green();
	auto __blue = dd.RGB().Blue();

	GUID clsID = Guid!("4549DC9D-8A15-46F0-A0ED-7DB9C02FCB18");
	GUID iID = Guid!("45F1195F-3554-4B3F-A00A-E1D189C0DC3E");
	auto rgb1 = dd.RGB();
	auto clsids = Guid2Str(rgb1.clsID);
	auto rgb2 = new icRGBColor();
	rgb2.iDispatch = rgb2;
	//auto rgb = cast(icRGBColor)&rgb2;
	//dd.RGB(cast(cRGBColor)(cast(void*)rgb));
	//dd.RGB(cast(icRGBColor)(cast(void*)rgb));


	cSolidColor.RGBSet = &RGB;
	auto rgb = new icRGBColor();
	//auto rgb = cast(icRGBColor)(cast(void*)dd.RGB());
	dd.RGB(rgb, rgb1);
	//dd.RGB2(rgb1);

     return 0;
}


void RGB(icRGBColor ic, cRGBColor c, cSolidColor s)
{
	GUID clsIDa = Guid!("AEADF007-9EE5-41D7-8CB1-AB5F353D1151");
	GUID iIDa = Guid!("D2D1665E-C1B9-4CA0-8AC9-529F6A3D9002");
	ic.iDispatch = s.iDispatch;
	ic.rgb = c;

	import main;
	EXCEPINFO exception;
	uint argErr = 0;
	auto iidNULL = IID_NULL;
	auto RT = new VARIANT();
	VARIANT[1] paramVars;
	DISPPARAMS params = {rgvarg: paramVars.ptr, cArgs: 1, 
cNamedArgs: 0};
	auto ID = s.COMMethodIDs[`RGB`];
	paramVars[0].punkVal = ic; paramVars[0].vt = 
VARENUM.VT_DISPATCH; scope(exit) VariantClear(&paramVars[0]);
	DISPID mydispid = DISPID_PROPERTYPUT;
	params.rgdispidNamedArgs = &mydispid;
	params.cNamedArgs = 1;
	writeln("-----------");
	auto res = s.iDispatch.Invoke(cast(int)ID, &iidNULL, 0, 
DISPATCH_PROPERTYPUT, &params, cast(VARIANT*)RT, &exception, 
&argErr);
	assert(res == S_OK, `Could not invoke COM Function 
cSolidColor.RGB. Error `~to!string(res, 16));

}






//version = wrap;


import std.conv, core.sys.windows.windows, core.sys.windows.com, 
core.sys.windows.wtypes, core.sys.windows.basetyps, 
core.sys.windows.unknwn, core.sys.windows.oaidl;

public class icRGBColor : IDispatch
{


	import std.conv, core.sys.windows.windows, core.sys.windows.com, 
core.sys.windows.wtypes, core.sys.windows.basetyps, 
core.sys.windows.unknwn, core.sys.windows.oaidl;
	public IDispatch iDispatch = null;
	int RefCount = 0;
	cRGBColor rgb;

version(all)
{
	void printFunc(string S = __FUNCTION__)()
	{		
		writeln(S);	
	}

	double Red()
	{ 	
		
		printFunc();
		version(wrap)
			return rgb.Red();
		return 0;
	}

	void Red(double rhs)
	{
		printFunc();
		version(wrap)
			rgb.Red(rhs);
	}

	double Green()
	{

		printFunc();
		version(wrap)
			return rgb.Green();
		return 0;
	}

	void Green(double rhs)
	{


		printFunc();
		version(wrap)
			rgb.Green(rhs);
	}

	double Blue()
	{


		printFunc();
		version(wrap)
			return rgb.Blue();
		return 0;
	}

	void Blue(double rhs)
	{


		printFunc();
		version(wrap)
			rgb.Blue();
	}

	BSTR HexValue()
	{


		printFunc();
		version(wrap)
			return rgb.HexValue();
		return cast(wchar*)(""w.ptr);
	}

	void HexValue(BSTR rhs)
	{


		printFunc();
		version(wrap)
			rgb.HexValue(rhs);
	}

	cApplication Application()
	{

		printFunc();
		return null;
	}
}


/*
	QueryInterface Called: 
00000003-0000-0000-C00-000000000046(GUID), 19F178(Object), 0(Ret)
	QueryInterface Called: 
ECC8691B-C1DB-4DC0-855E-65F6C551AF49(GUID), 19F124(Object), 
80004002(Ret)
	QueryInterface Called: 
00000003-0000-0000-C00-000000000046(GUID), 19EFE8(Object), 0(Ret)

	QueryInterface Called: 
00000003-0000-0000-C00-000000000046(GUID), 19F178(Object)
	main.icRGBColor.GetTypeInfo
*/		

	// IUnknown
	int QueryInterface(const(GUID)* riid, void** pvObject)
	{
		write("QueryInterface Called: ", Guid2Str(*riid), "(GUID), ", 
pvObject, "(Object)");
		version(wrap)
		{			
			auto res = rgb.iDispatch.QueryInterface(riid, pvObject);
			writeln(", ", to!string(res, 16), "(Ret)");
			return res;
		}

		auto guids = [["00000003-0000-0000-C00-000000000046", "0"], 
["ECC8691B-C1DB-4DC0-855E-65F6C551AF49", "0x80004002"]];
		
		foreach(g; guids)
		{
			if (Guid2Str(*riid) == g[0])
			{
				void **pv;
				auto guid = GuidN(g[0]);

				auto x = rgb.iDispatch.QueryInterface(&guid, pv);
				auto res3 = to!int(g[1], 16);
				

				// Always set out parameter to NULL, validating it first.
				if (pvObject is null)
					return E_POINTER;
				*pvObject = null;

				*pvObject = cast(void*)cast(IUnknown)this;

				writeln();
				return res3;

			}
		}

		/*
		auto ress = iDispatch.QueryInterface(riid, pvObject);
		writeln("(", ress, ")");
		return ress;
		*/

		// Always set out parameter to NULL, validating it first.
		if (pvObject is null) return E_POINTER;
		*pvObject = null;

		if (*riid == IID_IUnknown)
		{
			*pvObject = cast(void*)cast(IUnknown)this;
			writeln("\tIUnknown Supported");
		}
		else if (*riid == IID_IDispatch)
		{
			*pvObject = cast(void*)cast(IDispatch)this;
			writeln("\tIDispatch Supported");
		}

	
		// and so on for all interfaces we support
	
		
		if (*pvObject is null)
		{
			writeln("\tNot Supported");
			return E_NOINTERFACE;
		}

		//(cast(IUnknown)this).AddRef();
		return S_OK;

	}


version(all)
{
	uint AddRef()
	{
		
		printFunc();
		version(wrap)
			return rgb.iDispatch.AddRef();
		return ++RefCount;
	}

	uint Release()
	{


		printFunc();
		version(wrap)
			return rgb.iDispatch.Release();
		return --RefCount;
	}



	// IDispatch
	int GetTypeInfoCount(uint* x)
	{

		printFunc();
		auto res = rgb.iDispatch.GetTypeInfoCount(x);
		writeln(res);
		version(wrap)
			return rgb.iDispatch.GetTypeInfoCount(x);
		return res;					
		return 0;
	}
}


	LPTYPEINFO m_ptinfoEnglish;
	LPTYPEINFO m_ptinfoGerman;
	static const auto LCID_ENGLISH = MAKELCID(MAKELANGID(0x09, 
0x01), SORT_DEFAULT);
	static const auto LCID_GERMAN = MAKELCID(MAKELANGID(0x07, 0x01), 
SORT_DEFAULT);

	int GetTypeInfo(uint iTInfo, LCID lcid, LPTYPEINFO* ppTInfo)
	{
		import std.c.stdlib;

		auto xxx = LCID_ENGLISH;
		LPTYPEINFO ptinfo;

		//if (ppTInfo == NULL)
			//return E_INVALIDARG;

//		*ppTInfo = NULL;

//		if(iTInfo != 0)
			//return DISP_E_BADINDEX;

		if(lcid == LOCALE_SYSTEM_DEFAULT || lcid == 0)
			lcid = GetSystemDefaultLCID();

		if(lcid == LOCALE_USER_DEFAULT)
			lcid = GetUserDefaultLCID();

		switch(lcid)
		{
			case LCID_GERMAN:
				ptinfo = m_ptinfoGerman;
				break;

			case LCID_ENGLISH:
				ptinfo = m_ptinfoEnglish;
				break;

			default:
				return DISP_E_UNKNOWNLCID;
		}

		ptinfo.AddRef();
		*ppTInfo = ptinfo;
		return NOERROR;

		//m_ptinfo.AddRef();      // AddRef and return pointer to cached
		// typeinfo for this object.
		//*ppTInfo = m_ptinfo;

		return NOERROR;

		ITypeInfo t;
		auto p = malloc(ITypeInfo.sizeof);
		ppTInfo = &t;
		printFunc();
		//iTInfo  = 0; lcid = 0;
		auto res = rgb.iDispatch.GetTypeInfo(iTInfo, lcid, 
cast(ITypeInfo*)p);
		ppTInfo = cast(ITypeInfo*)p;
		writeln(res);
		version(wrap)
			return rgb.iDispatch.GetTypeInfo(a,b,c);
		return res;
		return 0;
	}

version(all)
{
	int GetIDsOfNames(const(GUID)* a, wchar** b, uint c, uint d, 
int* e)
	{


		printFunc();
		version(wrap)
			return rgb.iDispatch.GetIDsOfNames(a,b,c,d,e);
		return 0;
	}

	int Invoke(int a, const(GUID)* b, uint c, ushort d, DISPPARAMS* 
e, VARIANT* f, EXCEPINFO* g, uint* h)
	{


		printFunc();
		version(wrap)
			return rgb.iDispatch.Invoke(a,b,c,d,e,f,g,h);
		return 0;
	}







	// cRGBColor
	public this()
	{
		printFunc();
	}

}
}
May 05 2017