digitalmars.D.learn - COM Expertise needed: COM Callbacks
- Nierjerson (51/51) Apr 23 2017 Still trying to get the com automation code working. This is a
- MGW (2/3) Apr 24 2017 Please, use ZIP for archive.
- Nierjerson (2/5) Apr 24 2017 http://s000.tinyupload.com/index.php?file_id=67286353487198133918
- Atila Neves (8/12) Apr 25 2017 I tried looking at this because I just did some COM work even if
- Nierjerson (96/109) Apr 25 2017 There isn't a lot of code! All the code in Gen is irrelevant
- John Chapman (8/27) Apr 26 2017 When you use DISPATCH_PROPERTYPUT you need to set cNamedArgs and
- Nierjerson (4/13) Apr 26 2017 Did you try this? I tried and same issue. Are you sure the above
- John Chapman (8/24) Apr 27 2017 From the documentation
- Nierjerson (17/44) Apr 27 2017 Ok, thanks. I will try again, Maybe I did something wrong. It
- John Chapman (28/40) Apr 28 2017 QueryInterface is COM's version of opCast. It asks if you support
- Nierjerson (30/72) Apr 28 2017 Ok, I tried this and returned this but it didn't work. I didn't
- Nierjerson (5/25) Apr 28 2017 Your code works. I had something similar but I think I was
- Mike B Johnson (119/154) May 03 2017 I wrapped the COM interface that is returned by PS and the
- Mike B Johnson (371/371) May 05 2017 I've modified the code and it seems to call GetTypeInfo but the
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
On Monday, 24 April 2017 at 00:55:45 UTC, Nierjerson wrote:Still trying to get the com automation code working. This is aPlease, use ZIP for archive.
Apr 24 2017
On Monday, 24 April 2017 at 17:31:09 UTC, MGW wrote:On Monday, 24 April 2017 at 00:55:45 UTC, Nierjerson wrote:http://s000.tinyupload.com/index.php?file_id=67286353487198133918Still trying to get the com automation code working. This is aPlease, use ZIP for archive.
Apr 24 2017
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
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: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.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.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(¶mVars[0]); auto res = s.iDispatch.Invoke(cast(int)ID, &iidNULL, 0, DISPATCH_PROPERTYPUT, ¶ms, 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
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(¶mVars[0]); auto res = s.iDispatch.Invoke(cast(int)ID, &iidNULL, 0, DISPATCH_PROPERTYPUT, ¶ms, 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
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: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?[...]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
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: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.On Tuesday, 25 April 2017 at 18:39:56 UTC, Nierjerson wrote: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?[...]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 27 2017
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: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.On Wednesday, 26 April 2017 at 15:30:37 UTC, John Chapman wrote: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.On Tuesday, 25 April 2017 at 18:39:56 UTC, Nierjerson wrote: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?[...]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 27 2017
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
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: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.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?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
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: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)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; }
Apr 28 2017
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 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?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.
May 03 2017
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(¶mVars[0]); DISPID mydispid = DISPID_PROPERTYPUT; params.rgdispidNamedArgs = &mydispid; params.cNamedArgs = 1; writeln("-----------"); auto res = s.iDispatch.Invoke(cast(int)ID, &iidNULL, 0, DISPATCH_PROPERTYPUT, ¶ms, 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