Re: Accessing COM Objects
On Friday, 17 June 2016 at 08:09:42 UTC, John wrote: On Wednesday, 15 June 2016 at 21:06:01 UTC, Joerg Joergonson wrote: My thinking is that CoCreateinstance is suppose to give us a pointer to the interface so we can use it, if all this stuff is crashing does that mean the interface is invalid or not being assigned properly or is there far more to it than this? The problem is Photoshop hasn't provided an interface with methods that can be called directly. They don't exist on the interface, hence them being commented out. It's a mechanism known as late binding (everything is done at runtime rather than compile time). You need to ask the interface for the method's ID, marshal the parameters into a specific format, and then "invoke" the method using that ID. And you're not going to like it. Here's an example just to call the "Load" method: // Initialize the Photoshop class instance IDispatch psApp; auto iid = IID__Application; auto clsid = CLSID_Application; assert(SUCCEEDED(CoCreateInstance(&clsid, null, CLSCTX_ALL, &iid, cast(void**)&psApp))); scope(exit) psApp.Release(); // Get the ID of the Load method auto methodName = "Load"w.ptr; auto dispId = DISPID_UNKNOWN; iid = IID_NULL; assert(SUCCEEDED(psApp.GetIDsOfNames(&iid, &methodName, 1, 0, &dispId))); // Put the parameters into the expected format VARIANT fileName = { vt: VARENUM.VT_BSTR, bstrVal: SysAllocString("ps.psd"w.ptr) }; scope(exit) VariantClear(&fileName); DISPPARAMS params = { rgvarg: &fileName, cArgs: 1 }; // Finally call the method assert(SUCCEEDED(psApp.Invoke(dispId, &iid, 0, DISPATCH_METHOD, ¶ms, null, null, null))); tlb2d only outputs the late-bound methods as a hint to the user so they know the names of the methods and the expected parameters (well, it saves looking them up in OleView). Had Photoshop supplied a compile-time binding, you could have just called psApp.Load(fileName) like you tried. It's possible to wrap that ugly mess above in less verbose code using native D types, and the Juno COM library mentioned earlier enabled that, but the code is quite ancient (and is part of and depends on a larger library). I've been slowly working on a more modern library. You'd be able to just write this: auto psApp = makeReference!"Photoshop.Application"(); psApp.Load("ps.psd"); But I don't know when it'll be ready. So, I was playing around with this method and was able to get things to work. Have you been able to automate this properly? Seems like if we have the interface and methods, we can create an implementation that automates the above marshaling and stuff automatically using reflection? e.g., give interface _Application : IDispatch { ... /*[id(0x4C64536C)]*/ void Load(BSTR Document); ... /*[id(0x71756974)]*/ void Quit(); ... } it shouldn't be too hard to generate a class like Generated code: class PSAppication : _Application { ... void Load(BSTR Document) { // The invoking and marshaling code automatically generated // } ... void Quit() { // The invoking and marshaling code automatically generated // } ... } ? I assume this is what you said you were working on, more or less? Would be awesome if you already had this up and running! If not, I guess I'll try to implement something like it ;/ If you haven't worked on this, I have a few questions for ya: 1. Do we have to cocreateinit every time or can we just do it once? Seems like it could be a major performance issue if we have to call it each time? 2. Marshaling the paramters seems like it could be tricky as we would have to know each case? Scanning the photoshop idl file suggests there are many different parameter and return types(strings, ints, VARIANT_BOOL, com interfaces, enum, etc). A few are easy to handle and you showed how to handle strings, but some of the others I wouldn't know how to do. 3. Does the juno code handle this well enough to copy and paste most of the labor? 4. Any pitfalls to worry about? Thanks.
Re: Accessing COM Objects
On Friday, 17 June 2016 at 08:09:42 UTC, John wrote: On Wednesday, 15 June 2016 at 21:06:01 UTC, Joerg Joergonson wrote: [...] The problem is Photoshop hasn't provided an interface with methods that can be called directly. They don't exist on the interface, hence them being commented out. It's a mechanism known as late binding (everything is done at runtime rather than compile time). You need to ask the interface for the method's ID, marshal the parameters into a specific format, and then "invoke" the method using that ID. [...] Any news on this? I'd like to do some photoshop programming in D too but it seems like a mess?!?
Re: Accessing COM Objects
On Wednesday, 15 June 2016 at 21:06:01 UTC, Joerg Joergonson wrote: My thinking is that CoCreateinstance is suppose to give us a pointer to the interface so we can use it, if all this stuff is crashing does that mean the interface is invalid or not being assigned properly or is there far more to it than this? The problem is Photoshop hasn't provided an interface with methods that can be called directly. They don't exist on the interface, hence them being commented out. It's a mechanism known as late binding (everything is done at runtime rather than compile time). You need to ask the interface for the method's ID, marshal the parameters into a specific format, and then "invoke" the method using that ID. And you're not going to like it. Here's an example just to call the "Load" method: // Initialize the Photoshop class instance IDispatch psApp; auto iid = IID__Application; auto clsid = CLSID_Application; assert(SUCCEEDED(CoCreateInstance(&clsid, null, CLSCTX_ALL, &iid, cast(void**)&psApp))); scope(exit) psApp.Release(); // Get the ID of the Load method auto methodName = "Load"w.ptr; auto dispId = DISPID_UNKNOWN; iid = IID_NULL; assert(SUCCEEDED(psApp.GetIDsOfNames(&iid, &methodName, 1, 0, &dispId))); // Put the parameters into the expected format VARIANT fileName = { vt: VARENUM.VT_BSTR, bstrVal: SysAllocString("ps.psd"w.ptr) }; scope(exit) VariantClear(&fileName); DISPPARAMS params = { rgvarg: &fileName, cArgs: 1 }; // Finally call the method assert(SUCCEEDED(psApp.Invoke(dispId, &iid, 0, DISPATCH_METHOD, ¶ms, null, null, null))); tlb2d only outputs the late-bound methods as a hint to the user so they know the names of the methods and the expected parameters (well, it saves looking them up in OleView). Had Photoshop supplied a compile-time binding, you could have just called psApp.Load(fileName) like you tried. It's possible to wrap that ugly mess above in less verbose code using native D types, and the Juno COM library mentioned earlier enabled that, but the code is quite ancient (and is part of and depends on a larger library). I've been slowly working on a more modern library. You'd be able to just write this: auto psApp = makeReference!"Photoshop.Application"(); psApp.Load("ps.psd"); But I don't know when it'll be ready.
Re: Accessing COM Objects
On Wednesday, 15 June 2016 at 21:06:01 UTC, Joerg Joergonson wrote: Ok, I've tried things like uncommenting Document Open(BSTR Document, VARIANT As, VARIANT AsSmartObject); void Load(BSTR Document); /*[id(0x70537673)]*/ BSTR get_ScriptingVersion(); /*[id(0x70464D4D)]*/ double get_FreeMemory(); /*[id(0x76657273)]*/ BSTR get_Version(); and everything crashes with bad reference. ... My thinking is that CoCreateinstance is suppose to give us a pointer to the interface so we can use it, if all this stuff is crashing does that mean the interface is invalid or not being assigned properly or is there far more to it than this? First of all, you can't just comment/uncomment parts of COM interface descriptions. Each COM interface has some specific layout of its functions, and if you list them in wrong order or skip some of them the virtual methods table gets completely screwed up, so you think you call one method but end up calling another, because you intended to call method #12 and instead called method #4. COM interface definitions in D must match their definitions in IDL exactly. One omission of a method, one mistake in its type, and you're fucked.
Re: Accessing COM Objects
On Wednesday, 15 June 2016 at 19:21:51 UTC, John wrote: On Wednesday, 15 June 2016 at 18:32:28 UTC, Joerg Joergonson wrote: import core.sys.windows.com, core.sys.windows.oaidl; Thanks. Should these not be added to the generated file? The problem is that other type libraries will probably require other headers to be imported, and there's no way to work out which, so I've left that up to the user for now. Also, could you add to it the following: const static GUID iid = Guid!("5DE90358-4D0B-4FA1-BA3E-C91BBA863F32"); inside the interface (Replace the string with the correct guid)? This allows it to work with ComPtr which looks for the iid inside the interface, shouldn't hurt anything. I could add that as an option. In any case, I haven't got ComPtr to work so... GUID Guid(string str)() { static assert(str.length==36, "Guid string must be 36 chars long"); enum GUIDstring = "GUID(0x" ~ str[0..8] ~ ", 0x" ~ str[9..13] ~ ", 0x" ~ str[14..18] ~ ", [0x" ~ str[19..21] ~ ", 0x" ~ str[21..23] ~ ", 0x" ~ str[24..26] ~ ", 0x" ~ str[26..28] ~ ", 0x" ~ str[28..30] ~ ", 0x" ~ str[30..32] ~ ", 0x" ~ str[32..34] ~ ", 0x" ~ str[34..36] ~ "])"; return mixin(GUIDstring); } also tried CoCreateInstance and getting error 80040154 Not sure if it works. Changed the GUID to another one found in the registry(not the one at the top of the generated file) and it works. Both load photoshop Oops. The one at the top of the file is the type library's ID, not the class ID. I should just omit it if it causes confusion. int main(string[] argv) { //auto ps = ComPtr!_Application(CLSID_PS).require; //const auto CLSID_PS = Guid!("6DECC242-87EF-11cf-86B4-44455354"); // PS 90.1 fails because of interface issue const auto CLSID_PS = Guid!("c09f153e-dff7-4eff-a570-af82c1a5a2a8"); // PS 90.0 works. auto hr = CoInitialize(null); auto iid = IID__Application; _Application* pUnk; hr = CoCreateInstance(&CLSID_PS, null, CLSCTX_ALL, &iid, cast(void**)&pUnk); if (FAILED(hr)) throw new Exception("ASDF"); } The photoshop.d file http://www.filedropper.com/photoshop_1 So, I guess it works but how to access the methods? The photoshop file looks to have them listed but they are all commented out. They're commented out because Photoshop seems to have only provided a late-binding interface and you have to call them by name through IDispatch.Invoke. It's possible to wrap all that in normal D methods, and I'm working on it, but it won't be ready for a while. Ok, I've tried things like uncommenting Document Open(BSTR Document, VARIANT As, VARIANT AsSmartObject); void Load(BSTR Document); /*[id(0x70537673)]*/ BSTR get_ScriptingVersion(); /*[id(0x70464D4D)]*/ double get_FreeMemory(); /*[id(0x76657273)]*/ BSTR get_Version(); and everything crashes with bad reference. If I try ComPtr, same thing const auto CLSID_PS = Guid!("c09f153e-dff7-4eff-a570-af82c1a5a2a8"); // PS 90.0 works. auto hr = CoInitialize(null); auto iid = IID__Application; auto ps = cast(_Application)(ComPtr!_Application(CLSID_PS).require); _Application pUnk; hr = CoCreateInstance(&CLSID_PS, null, CLSCTX_ALL, &iid, cast(void**)&pUnk); if (FAILED(hr)) throw new Exception("ASDF"); auto ptr = cast(wchar*)alloca(wchar.sizeof * 1000); auto fn = `ps.psd`; for(auto i = 0; i < fn.length; i++) { ptr[i] = fn[i]; } writeln(ps.get_FreeMemory()); pUnk.Load(ptr); My thinking is that CoCreateinstance is suppose to give us a pointer to the interface so we can use it, if all this stuff is crashing does that mean the interface is invalid or not being assigned properly or is there far more to it than this? (
Re: Accessing COM Objects
On Wednesday, 15 June 2016 at 18:35:42 UTC, Joerg Joergonson wrote: On Wednesday, 15 June 2016 at 06:09:33 UTC, thedeemon wrote: On Monday, 13 June 2016 at 17:38:41 UTC, Incognito wrote: [...] There are ready tools idl2d: https://github.com/dlang/visuald/tree/master/c2d [...] I can't seem to get ComPtr to work. auto ps = ComPtr!_Application(CLSID_PS).require; Where CLSID_PS is the Guid from the registry that seems to work with CoCreate. _Application was generated from tbl2d. See my other post for a more(not much) complete description of the issues files. Ensure you are calling CoInitialize before anything else.
Re: Accessing COM Objects
On Wednesday, 15 June 2016 at 18:32:28 UTC, Joerg Joergonson wrote: import core.sys.windows.com, core.sys.windows.oaidl; Thanks. Should these not be added to the generated file? The problem is that other type libraries will probably require other headers to be imported, and there's no way to work out which, so I've left that up to the user for now. Also, could you add to it the following: const static GUID iid = Guid!("5DE90358-4D0B-4FA1-BA3E-C91BBA863F32"); inside the interface (Replace the string with the correct guid)? This allows it to work with ComPtr which looks for the iid inside the interface, shouldn't hurt anything. I could add that as an option. In any case, I haven't got ComPtr to work so... GUID Guid(string str)() { static assert(str.length==36, "Guid string must be 36 chars long"); enum GUIDstring = "GUID(0x" ~ str[0..8] ~ ", 0x" ~ str[9..13] ~ ", 0x" ~ str[14..18] ~ ", [0x" ~ str[19..21] ~ ", 0x" ~ str[21..23] ~ ", 0x" ~ str[24..26] ~ ", 0x" ~ str[26..28] ~ ", 0x" ~ str[28..30] ~ ", 0x" ~ str[30..32] ~ ", 0x" ~ str[32..34] ~ ", 0x" ~ str[34..36] ~ "])"; return mixin(GUIDstring); } also tried CoCreateInstance and getting error 80040154 Not sure if it works. Changed the GUID to another one found in the registry(not the one at the top of the generated file) and it works. Both load photoshop Oops. The one at the top of the file is the type library's ID, not the class ID. I should just omit it if it causes confusion. int main(string[] argv) { //auto ps = ComPtr!_Application(CLSID_PS).require; //const auto CLSID_PS = Guid!("6DECC242-87EF-11cf-86B4-44455354"); // PS 90.1 fails because of interface issue const auto CLSID_PS = Guid!("c09f153e-dff7-4eff-a570-af82c1a5a2a8"); // PS 90.0 works. auto hr = CoInitialize(null); auto iid = IID__Application; _Application* pUnk; hr = CoCreateInstance(&CLSID_PS, null, CLSCTX_ALL, &iid, cast(void**)&pUnk); if (FAILED(hr)) throw new Exception("ASDF"); } The photoshop.d file http://www.filedropper.com/photoshop_1 So, I guess it works but how to access the methods? The photoshop file looks to have them listed but they are all commented out. They're commented out because Photoshop seems to have only provided a late-binding interface and you have to call them by name through IDispatch.Invoke. It's possible to wrap all that in normal D methods, and I'm working on it, but it won't be ready for a while.
Re: Accessing COM Objects
On Wednesday, 15 June 2016 at 06:09:33 UTC, thedeemon wrote: On Monday, 13 June 2016 at 17:38:41 UTC, Incognito wrote: [...] There are ready tools idl2d: https://github.com/dlang/visuald/tree/master/c2d [...] I can't seem to get ComPtr to work. auto ps = ComPtr!_Application(CLSID_PS).require; Where CLSID_PS is the Guid from the registry that seems to work with CoCreate. _Application was generated from tbl2d. See my other post for a more(not much) complete description of the issues files.
Re: Accessing COM Objects
On Wednesday, 15 June 2016 at 17:20:31 UTC, John wrote: On Wednesday, 15 June 2016 at 16:45:39 UTC, Joerg Joergonson wrote: Thanks. When I ran it I got a d file! when I tried to use that d file I get undefined IID and IDispatch. I imagine these interfaces come from somewhere, probably built in? Any ideas? Add the following after the module name: import core.sys.windows.com, core.sys.windows.oaidl; Thanks. Should these not be added to the generated file? Also, could you add to it the following: const static GUID iid = Guid!("5DE90358-4D0B-4FA1-BA3E-C91BBA863F32"); inside the interface (Replace the string with the correct guid)? This allows it to work with ComPtr which looks for the iid inside the interface, shouldn't hurt anything. In any case, I haven't got ComPtr to work so... GUID Guid(string str)() { static assert(str.length==36, "Guid string must be 36 chars long"); enum GUIDstring = "GUID(0x" ~ str[0..8] ~ ", 0x" ~ str[9..13] ~ ", 0x" ~ str[14..18] ~ ", [0x" ~ str[19..21] ~ ", 0x" ~ str[21..23] ~ ", 0x" ~ str[24..26] ~ ", 0x" ~ str[26..28] ~ ", 0x" ~ str[28..30] ~ ", 0x" ~ str[30..32] ~ ", 0x" ~ str[32..34] ~ ", 0x" ~ str[34..36] ~ "])"; return mixin(GUIDstring); } also tried CoCreateInstance and getting error 80040154 Not sure if it works. Changed the GUID to another one found in the registry(not the one at the top of the generated file) and it works. Both load photoshop int main(string[] argv) { //auto ps = ComPtr!_Application(CLSID_PS).require; //const auto CLSID_PS = Guid!("6DECC242-87EF-11cf-86B4-44455354"); // PS 90.1 fails because of interface issue const auto CLSID_PS = Guid!("c09f153e-dff7-4eff-a570-af82c1a5a2a8"); // PS 90.0 works. auto hr = CoInitialize(null); auto iid = IID__Application; _Application* pUnk; hr = CoCreateInstance(&CLSID_PS, null, CLSCTX_ALL, &iid, cast(void**)&pUnk); if (FAILED(hr)) throw new Exception("ASDF"); } The photoshop.d file http://www.filedropper.com/photoshop_1 So, I guess it works but how to access the methods? The photoshop file looks to have them listed but they are all commented out. I suppose this is what ComPtr and other methods are used to help create the interface but none seem to work.
Re: Accessing COM Objects
On Wednesday, 15 June 2016 at 16:45:39 UTC, Joerg Joergonson wrote: Thanks. When I ran it I got a d file! when I tried to use that d file I get undefined IID and IDispatch. I imagine these interfaces come from somewhere, probably built in? Any ideas? Add the following after the module name: import core.sys.windows.com, core.sys.windows.oaidl;
Re: Accessing COM Objects
On Wednesday, 15 June 2016 at 15:12:06 UTC, thedeemon wrote: On Wednesday, 15 June 2016 at 07:01:30 UTC, Joerg Joergonson wrote: It seems idl2d from VD is not easily compilable? I don't remember problems with that, anyway here's the binary I used: http://stuff.thedeemon.com/idl2d.exe It crashes when I use it ;/ core.exception.UnicodeException@src\rt\util\utf.d(290): invalid UTF-8 sequence tbl2d did work and gave me a d file but I need to figure out what IID and IDispatch are.
Re: Accessing COM Objects
On Wednesday, 15 June 2016 at 08:24:41 UTC, John wrote: On Wednesday, 15 June 2016 at 08:21:06 UTC, John wrote: OK, adding the return type to the signature should fix that. So: private static Parameter getParameters(MethodImpl method) Sorry, I meant the getParameter methods should return be: private static Parameter[] getParameters(MethodImpl method) and private static Parameter[] getParameters(MethodImpl method, out Parameter returnParameter, bool getReturnParameter) Thanks. When I ran it I got a d file! when I tried to use that d file I get undefined IID and IDispatch. I imagine these interfaces come from somewhere, probably built in? Any ideas?
Re: Accessing COM Objects
On Wednesday, 15 June 2016 at 16:03:04 UTC, Jesse Phillips wrote: On Monday, 13 June 2016 at 01:22:33 UTC, Incognito wrote: [...] There is also: https://github.com/JesseKPhillips/Juno-Windows-Class-Library It kind of provides similar highlevel options as the "Modern COM Programming in D." But I don't use it and have only be somewhat keeping it alive (I had some hiccups in supporting 64bit), so I haven't been working to improve the simplicity of interfacing to COM objects. It also includes definitions for accessing Windows COM objects which aren't needed when interfacing with your own or other COM objects. I'd like to have two libraries, Juno Library and Juno Windows Class Library. I'll check it it out...
Re: Accessing COM Objects
On Monday, 13 June 2016 at 01:22:33 UTC, Incognito wrote: I've been reading over D's com and can't find anything useful. It seems there are different ways: http://www.lunesu.com/uploads/ModernCOMProgramminginD.pdf which is of no help and requires an idl file, which I don't have. Then theres this http://wiki.dlang.org/COM_Programming There is also: https://github.com/JesseKPhillips/Juno-Windows-Class-Library It kind of provides similar highlevel options as the "Modern COM Programming in D." But I don't use it and have only be somewhat keeping it alive (I had some hiccups in supporting 64bit), so I haven't been working to improve the simplicity of interfacing to COM objects. It also includes definitions for accessing Windows COM objects which aren't needed when interfacing with your own or other COM objects. I'd like to have two libraries, Juno Library and Juno Windows Class Library.
Re: Accessing COM Objects
On Wednesday, 15 June 2016 at 07:01:30 UTC, Joerg Joergonson wrote: It seems idl2d from VD is not easily compilable? I don't remember problems with that, anyway here's the binary I used: http://stuff.thedeemon.com/idl2d.exe
Re: Accessing COM Objects
On Wednesday, 15 June 2016 at 08:21:06 UTC, John wrote: OK, adding the return type to the signature should fix that. So: private static Parameter getParameters(MethodImpl method) Sorry, I meant the getParameter methods should return be: private static Parameter[] getParameters(MethodImpl method) and private static Parameter[] getParameters(MethodImpl method, out Parameter returnParameter, bool getReturnParameter)
Re: Accessing COM Objects
On Wednesday, 15 June 2016 at 06:56:59 UTC, Joerg Joergonson wrote: When I try to compile your code I get the following errors: main.d(953): Error: function core.sys.windows.objbase.CoTaskMemAlloc (uint) is not callable using argument types (immutable(ulong)) main.d(970): Error: can only slice tuple types, not _error_ main.d(974): Error: can only slice tuple types, not _error_ coTaskMemAlloc is defined with ULONG in the objbase.d file... so I have no idea what's going on there. immutable bufferSize = (funcDesc.cParams + 1) * (wchar*).sizeof; auto names = cast(wchar**)CoTaskMemAlloc(bufferSize); Looks like bufferSize just needs to be cast to uint. Didn't get that error in DMD. The other two I also don't know: params ~= new Parameter(method, (name[0 .. SysStringLen(name)]).toUTF8(), If I run it in ldc I get the error Error: forward reference to inferred return type of function call 'getParameters' private static getParameters(MethodImpl method) { Parameter dummy; return getParameters(method, dummy, false); } It does compile in DMD though. OK, adding the return type to the signature should fix that. So: private static Parameter getParameters(MethodImpl method) When running I get the error Error loading type library/DLL. The IDL file is in the same directory Did you try to pass it an IDL file? No wonder it didn't work - you pass in the type library instead, which is a binary file such as a DLL, EXE or TLB file. You can get the file's path from OleView by highlighting the library on the left (eg Photoshop) and on the right it will show a tree with the path beside "win32".
Re: Accessing COM Objects
On Wednesday, 15 June 2016 at 06:09:33 UTC, thedeemon wrote: On Monday, 13 June 2016 at 17:38:41 UTC, Incognito wrote: Cool. Oleview gives me the idl files. How to convert the idl files to d or possibly c? There are ready tools idl2d: https://github.com/dlang/visuald/tree/master/c2d and tlb2idl: https://github.com/dlang/visuald/tree/master/tools I've used this idl2d and it works pretty well (although not perfect, sometimes manual editing still required). Example of real-world DirectShow interfaces translated: https://gist.github.com/thedeemon/46748f91afdbcf339f55da9b355a6b56 Would I just use them in place of IUnknown once I have the interface? If you have the interface defined AND you know its IID, you can request it from CoCreateInstance and then use as ordinary D object. You might want to look at this wrapper that takes most of COM machinery: https://gist.github.com/thedeemon/3c2989b76004fafe9aa0 Then you just write almost as in C#, something like auto pGraph = ComPtr!IGraphBuilder(CLSID_FilterGraph, "pGraph").require; ComPtr!ICaptureGraphBuilder2 pBuilder = ComPtr!ICaptureGraphBuilder2(CLSID_CaptureGraphBuilder2).require; pBuilder.SetFiltergraph(pGraph); ... auto CLSID_NullRenderer = Guid!("C1F400A4-3F08-11D3-9F0B-006008039E37"); //qedit.dll auto pNullRendr = ComPtr!IBaseFilter(CLSID_NullRenderer, "nulrend"); pGraph.AddFilter(pNullRendr, "Null Renderer"w.ptr); ... auto imf = ComPtr!IMediaFilter(pGraph); imf.SetSyncSource(null); All the CreateInstance, QueryInterface, AddRef/Release etc. is taken care of. And even HRESULT return codes are automatically checked. Thanks, if I can get the idl converted I'll test it out. It seems idl2d from VD is not easily compilable?
Re: Accessing COM Objects P3
[in] long index, [out] long* value); [id(0x60020017)] HRESULT PutClass([in] long value); [id(0x60020018)] HRESULT GetGlobalClass( [in] long index, [out] long* value); [id(0x60020019)] HRESULT PutGlobalClass([in] long value); [id(0x6002001a)] HRESULT GetPath( [in] long index, [out] BSTR* pathString); [id(0x6002001b)] HRESULT PutPath([in] BSTR pathString); [id(0x6002001c)] HRESULT GetDataLength( [in] long index, [out] long* value); [id(0x6002001d)] HRESULT GetData( [in] long index, [out] BSTR* value); [id(0x6002001e)] HRESULT PutData( [in] long length, [in] BSTR value); }; [ odl, uuid(7CA9DE40-9EB3-11D1-B033-00C04FD7EC47), helpstring("Container class for actions system parameters."), dual, oleautomation ] interface IActionDescriptor : IDispatch { [id(0x6002)] HRESULT GetType( [in] long key, [out] long* type); [id(0x60020001)] HRESULT GetKey( [in] long index, [out] long* key); [id(0x60020002)] HRESULT HasKey( [in] long key, [out] long* HasKey); [id(0x60020003)] HRESULT GetCount([out] long* count); [id(0x60020004)] HRESULT IsEqual( [in] IActionDescriptor* otherDesc, [out] long* IsEqual); [id(0x60020005)] HRESULT Erase([in] long key); [id(0x60020006)] HRESULT Clear(); [id(0x60020007)] HRESULT GetInteger( [in] long key, [out] long* retval); [id(0x60020008)] HRESULT PutInteger( [in] long key, [in] long value); [id(0x60020009)] HRESULT GetDouble( [in] long key, [out] double* retval); [id(0x6002000a)] HRESULT PutDouble( [in] long key, [in] double value); [id(0x6002000b)] HRESULT GetUnitDouble( [in] long key, [out] long* unitID, [out] double* retval); [id(0x6002000c)] HRESULT PutUnitDouble( [in] long key, [in] long unitID, [in] double value); [id(0x6002000d)] HRESULT GetString( [in] long key, [out] BSTR* retval); [id(0x6002000e)] HRESULT PutString( [in] long key, [in] BSTR value); [id(0x6002000f)] HRESULT GetBoolean( [in] long key, [out] long* retval); [id(0x60020010)] HRESULT PutBoolean( [in] long key, [in] long value); [id(0x60020011)] HRESULT GetList( [in] long key, [out] IActionList** list); [id(0x60020012)] HRESULT PutList( [in] long key, [in] IActionList* list); [id(0x60020013)] HRESULT GetObject( [in] long key, [out] long* classID, [out] IActionDescriptor** retval); [id(0x60020014)] HRESULT PutObject( [in] long key, [in] long classID, [in] IActionDescriptor* value); [id(0x60020015)] HRESULT GetGlobalObject( [in] long key, [out] long* classID, [out] IActionDescriptor** retval); [id(0x60020016)] HRESULT PutGlobalObject( [in] long key, [in] long classID, [in] IActionDescriptor* value); [id(0x60020017)] HRESULT GetEnumerated( [in] long key, [out] long* enumType, [out] long* value); [id(0x60020018)] HRESULT PutEnumerated( [in] long key, [in] long enumType, [in] long value); [id(0x60020019)] HRESULT GetReference( [in] long key, [out] IActionReference** reference); [id
Re: Accessing COM Objects
On Monday, 13 June 2016 at 17:38:41 UTC, Incognito wrote: Cool. Oleview gives me the idl files. How to convert the idl files to d or possibly c? There are ready tools idl2d: https://github.com/dlang/visuald/tree/master/c2d and tlb2idl: https://github.com/dlang/visuald/tree/master/tools I've used this idl2d and it works pretty well (although not perfect, sometimes manual editing still required). Example of real-world DirectShow interfaces translated: https://gist.github.com/thedeemon/46748f91afdbcf339f55da9b355a6b56 Would I just use them in place of IUnknown once I have the interface? If you have the interface defined AND you know its IID, you can request it from CoCreateInstance and then use as ordinary D object. You might want to look at this wrapper that takes most of COM machinery: https://gist.github.com/thedeemon/3c2989b76004fafe9aa0 Then you just write almost as in C#, something like auto pGraph = ComPtr!IGraphBuilder(CLSID_FilterGraph, "pGraph").require; ComPtr!ICaptureGraphBuilder2 pBuilder = ComPtr!ICaptureGraphBuilder2(CLSID_CaptureGraphBuilder2).require; pBuilder.SetFiltergraph(pGraph); ... auto CLSID_NullRenderer = Guid!("C1F400A4-3F08-11D3-9F0B-006008039E37"); //qedit.dll auto pNullRendr = ComPtr!IBaseFilter(CLSID_NullRenderer, "nulrend"); pGraph.AddFilter(pNullRendr, "Null Renderer"w.ptr); ... auto imf = ComPtr!IMediaFilter(pGraph); imf.SetSyncSource(null); All the CreateInstance, QueryInterface, AddRef/Release etc. is taken care of. And even HRESULT return codes are automatically checked.
Re: Accessing COM Objects
On Monday, 13 June 2016 at 19:26:08 UTC, Incognito wrote: On Monday, 13 June 2016 at 19:11:59 UTC, John wrote: On Monday, 13 June 2016 at 17:38:41 UTC, Incognito wrote: Cool. Oleview gives me the idl files. How to convert the idl files to d or possibly c? Would I just use them in place of IUnknown once I have the interface? In OleView you can save the IDL file, then run another tool, midl.exe, on the IDL file. That will generate the C headers. But you'd then have to translate that to D by hand. I have written a tool that takes a COM type library and automatically converts it to a D source file. I could finish it off over the next few days and put the source on GitHub if you're interested. That would be great! With it I might be able to put a nice wrapper around Photoshop for use with D. Others mind find it useful too as other programs still use COM. It's up: https://github.com/jsatellite/tlb2d/tree/master/src/tlb2d There's probably a few bugs, and type libraries have a tendency to re-define existing types so you'll have to comment those out. Also it doesn't generate dependencies, but it does list them at the top of the generated module so you can run the tool on those type libraries manually.
Re: Accessing COM Objects
Visual D has a tool to convert IDL files to D.
Re: Accessing COM Objects
On Monday, 13 June 2016 at 19:11:59 UTC, John wrote: On Monday, 13 June 2016 at 17:38:41 UTC, Incognito wrote: Cool. Oleview gives me the idl files. How to convert the idl files to d or possibly c? Would I just use them in place of IUnknown once I have the interface? In OleView you can save the IDL file, then run another tool, midl.exe, on the IDL file. That will generate the C headers. But you'd then have to translate that to D by hand. I have written a tool that takes a COM type library and automatically converts it to a D source file. I could finish it off over the next few days and put the source on GitHub if you're interested. That would be great! With it I might be able to put a nice wrapper around Photoshop for use with D. Others mind find it useful too as other programs still use COM.
Re: Accessing COM Objects
On Monday, 13 June 2016 at 17:38:41 UTC, Incognito wrote: Cool. Oleview gives me the idl files. How to convert the idl files to d or possibly c? Would I just use them in place of IUnknown once I have the interface? In OleView you can save the IDL file, then run another tool, midl.exe, on the IDL file. That will generate the C headers. But you'd then have to translate that to D by hand. I have written a tool that takes a COM type library and automatically converts it to a D source file. I could finish it off over the next few days and put the source on GitHub if you're interested.
Re: Accessing COM Objects
On Monday, 13 June 2016 at 07:40:09 UTC, John wrote: On Monday, 13 June 2016 at 01:22:33 UTC, Incognito wrote: I've been reading over D's com and can't find anything useful. It seems there are different ways: http://www.lunesu.com/uploads/ModernCOMProgramminginD.pdf which is of no help and requires an idl file, which I don't have. Then theres this http://wiki.dlang.org/COM_Programming which is also of no help: import std.stdio; import std.stdio, core.sys.windows.com, core.sys.windows.windows, std.exception, std.meta, std.traits; import std.utf, core.stdc.stdlib, core.sys.windows.objidl, core.sys.windows.ole2; pragma(lib, "ole32.lib"); GUID Guid(string str)() { static assert(str.length==36, "Guid string must be 36 chars long"); enum GUIDstring = "GUID(0x" ~ str[0..8] ~ ", 0x" ~ str[9..13] ~ ", 0x" ~ str[14..18] ~ ", [0x" ~ str[19..21] ~ ", 0x" ~ str[21..23] ~ ", 0x" ~ str[24..26] ~ ", 0x" ~ str[26..28] ~ ", 0x" ~ str[28..30] ~ ", 0x" ~ str[30..32] ~ ", 0x" ~ str[32..34] ~ ", 0x" ~ str[34..36] ~ "])"; return mixin(GUIDstring); } int main(string[] argv) { // Adobe Photoshop App 9.0 CLSID {c09f153e-dff7-4eff-a570-af82c1a5a2a8} // Adobe Photoshop App 9.1 CLSID {6DECC242-87EF-11cf-86B4-44455354} auto CLSID_DOMDocument60 = Guid!("6DECC242-87EF-11cf-86B4-44455354"); auto iid = IID_IUnknown; void* pUnk; auto hr = CoCreateInstance(&CLSID_DOMDocument60, null, CLSCTX_ALL, &iid, &pUnk); if (FAILED(hr)) throw new Exception("Error!"); writeln("Hello D-World!"); return 0; } Maybe my CLSID's are wrong. Got them from the registry. The exception triggers each time. Even if it worked, I wouldn't know how to use it. I can do this stuff in C# by simply dragging and dropping a dll into the references and it works fine but is a bit slow. I was hoping I could speed things up using D but it seems like COM isn't really supported, despite what several references say. COM is supported in D. The difference is that C# hides all the plumbing behind nice classes. You're going to need Photoshop's COM interface to do anything with it. If you don't have a C header that you can translate into D, you could use a tool included in the Windows SDK called OleView. It will peek inside COM libraries and give you the interface definitions. As to why CoCreateInstance isn't working, make sure you're calling CoInitialize or CoInitializeEx beforehand. If it's still failing, use std.windows.syserror.sysErrorString(hr) to see if it gives you a reason. Otherwise, CLSCTX_ALL might be the culprit - try using CLSCTX_SERVER instead. Cool. Oleview gives me the idl files. How to convert the idl files to d or possibly c? Would I just use them in place of IUnknown once I have the interface?
Re: Accessing COM Objects
On Monday, 13 June 2016 at 01:22:33 UTC, Incognito wrote: I've been reading over D's com and can't find anything useful. It seems there are different ways: http://www.lunesu.com/uploads/ModernCOMProgramminginD.pdf which is of no help and requires an idl file, which I don't have. Then theres this http://wiki.dlang.org/COM_Programming which is also of no help: import std.stdio; import std.stdio, core.sys.windows.com, core.sys.windows.windows, std.exception, std.meta, std.traits; import std.utf, core.stdc.stdlib, core.sys.windows.objidl, core.sys.windows.ole2; pragma(lib, "ole32.lib"); GUID Guid(string str)() { static assert(str.length==36, "Guid string must be 36 chars long"); enum GUIDstring = "GUID(0x" ~ str[0..8] ~ ", 0x" ~ str[9..13] ~ ", 0x" ~ str[14..18] ~ ", [0x" ~ str[19..21] ~ ", 0x" ~ str[21..23] ~ ", 0x" ~ str[24..26] ~ ", 0x" ~ str[26..28] ~ ", 0x" ~ str[28..30] ~ ", 0x" ~ str[30..32] ~ ", 0x" ~ str[32..34] ~ ", 0x" ~ str[34..36] ~ "])"; return mixin(GUIDstring); } int main(string[] argv) { // Adobe Photoshop App 9.0 CLSID {c09f153e-dff7-4eff-a570-af82c1a5a2a8} // Adobe Photoshop App 9.1 CLSID {6DECC242-87EF-11cf-86B4-44455354} auto CLSID_DOMDocument60 = Guid!("6DECC242-87EF-11cf-86B4-44455354"); auto iid = IID_IUnknown; void* pUnk; auto hr = CoCreateInstance(&CLSID_DOMDocument60, null, CLSCTX_ALL, &iid, &pUnk); if (FAILED(hr)) throw new Exception("Error!"); writeln("Hello D-World!"); return 0; } Maybe my CLSID's are wrong. Got them from the registry. The exception triggers each time. Even if it worked, I wouldn't know how to use it. I can do this stuff in C# by simply dragging and dropping a dll into the references and it works fine but is a bit slow. I was hoping I could speed things up using D but it seems like COM isn't really supported, despite what several references say. COM is supported in D. The difference is that C# hides all the plumbing behind nice classes. You're going to need Photoshop's COM interface to do anything with it. If you don't have a C header that you can translate into D, you could use a tool included in the Windows SDK called OleView. It will peek inside COM libraries and give you the interface definitions. As to why CoCreateInstance isn't working, make sure you're calling CoInitialize or CoInitializeEx beforehand. If it's still failing, use std.windows.syserror.sysErrorString(hr) to see if it gives you a reason. Otherwise, CLSCTX_ALL might be the culprit - try using CLSCTX_SERVER instead.
Re: Accessing COM Objects
On Monday, 13 June 2016 at 04:52:49 UTC, Mike Parker wrote: On Monday, 13 June 2016 at 02:08:22 UTC, Incognito wrote: What interface are you talking about? How can I cast to something I don't have? I do not have a photoshop COM interface. Are you saying that if CoCreateInstance worked that I can then use the iid or pUnk to access the COM? Do I get the members by trial and error? pUnk.width? Even if CoCreateInstance passed, what do I do next? You have to define the interface yourself, extending from IUnknown, implementing whatever functions are available on the COM interface you want. Here's an example for the ID3D11Device interface: https://github.com/auroragraphics/directx/blob/master/d3d11.d#L1332 Sorry. Hit send too soon. Once you've got your interface defined, you should be able to do this: MyCOMType mct; auto hr = CoCreateInstance(&CLSID_DOMDocument60, null, CLSCTX_ALL, &iid, &cast(void*)mct); It's been a long time since I worked directly with COM, so there are probably details I'm missing, but this is the general idea.
Re: Accessing COM Objects
On Monday, 13 June 2016 at 02:08:22 UTC, Incognito wrote: What interface are you talking about? How can I cast to something I don't have? I do not have a photoshop COM interface. Are you saying that if CoCreateInstance worked that I can then use the iid or pUnk to access the COM? Do I get the members by trial and error? pUnk.width? Even if CoCreateInstance passed, what do I do next? You have to define the interface yourself, extending from IUnknown, implementing whatever functions are available on the COM interface you want. Here's an example for the ID3D11Device interface: https://github.com/auroragraphics/directx/blob/master/d3d11.d#L1332
Re: Accessing COM Objects
On Monday, 13 June 2016 at 01:52:12 UTC, Mike Parker wrote: On Monday, 13 June 2016 at 01:22:33 UTC, Incognito wrote: I can do this stuff in C# by simply dragging and dropping a dll into the references and it works fine but is a bit slow. I was hoping I could speed things up using D but it seems like COM isn't really supported, despite what several references say. Com *is* supported in D. I think it's better to work with interfaces rather than classes like the Wiki example: interface MyCOMType : IUknown {} Then when you get a pointer from CoCreateInstance or whatever, you cast it to the interface type and away you go. In your code, your problem is that CoCreateInstance is failing, not that D doesn't support COM. It will help you to find out what the actual value of 'hr' is. Possible values are listed at [1]. [1] https://msdn.microsoft.com/en-us/library/windows/desktop/ms686615(v=vs.85).aspx What interface are you talking about? How can I cast to something I don't have? I do not have a photoshop COM interface. Are you saying that if CoCreateInstance worked that I can then use the iid or pUnk to access the COM? Do I get the members by trial and error? pUnk.width? Even if CoCreateInstance passed, what do I do next?
Re: Accessing COM Objects
On Monday, 13 June 2016 at 01:22:33 UTC, Incognito wrote: I can do this stuff in C# by simply dragging and dropping a dll into the references and it works fine but is a bit slow. I was hoping I could speed things up using D but it seems like COM isn't really supported, despite what several references say. Com *is* supported in D. I think it's better to work with interfaces rather than classes like the Wiki example: interface MyCOMType : IUknown {} Then when you get a pointer from CoCreateInstance or whatever, you cast it to the interface type and away you go. In your code, your problem is that CoCreateInstance is failing, not that D doesn't support COM. It will help you to find out what the actual value of 'hr' is. Possible values are listed at [1]. [1] https://msdn.microsoft.com/en-us/library/windows/desktop/ms686615(v=vs.85).aspx
Accessing COM Objects
I've been reading over D's com and can't find anything useful. It seems there are different ways: http://www.lunesu.com/uploads/ModernCOMProgramminginD.pdf which is of no help and requires an idl file, which I don't have. Then theres this http://wiki.dlang.org/COM_Programming which is also of no help: import std.stdio; import std.stdio, core.sys.windows.com, core.sys.windows.windows, std.exception, std.meta, std.traits; import std.utf, core.stdc.stdlib, core.sys.windows.objidl, core.sys.windows.ole2; pragma(lib, "ole32.lib"); GUID Guid(string str)() { static assert(str.length==36, "Guid string must be 36 chars long"); enum GUIDstring = "GUID(0x" ~ str[0..8] ~ ", 0x" ~ str[9..13] ~ ", 0x" ~ str[14..18] ~ ", [0x" ~ str[19..21] ~ ", 0x" ~ str[21..23] ~ ", 0x" ~ str[24..26] ~ ", 0x" ~ str[26..28] ~ ", 0x" ~ str[28..30] ~ ", 0x" ~ str[30..32] ~ ", 0x" ~ str[32..34] ~ ", 0x" ~ str[34..36] ~ "])"; return mixin(GUIDstring); } int main(string[] argv) { // Adobe Photoshop App 9.0 CLSID {c09f153e-dff7-4eff-a570-af82c1a5a2a8} // Adobe Photoshop App 9.1 CLSID {6DECC242-87EF-11cf-86B4-44455354} auto CLSID_DOMDocument60 = Guid!("6DECC242-87EF-11cf-86B4-44455354"); auto iid = IID_IUnknown; void* pUnk; auto hr = CoCreateInstance(&CLSID_DOMDocument60, null, CLSCTX_ALL, &iid, &pUnk); if (FAILED(hr)) throw new Exception("Error!"); writeln("Hello D-World!"); return 0; } Maybe my CLSID's are wrong. Got them from the registry. The exception triggers each time. Even if it worked, I wouldn't know how to use it. I can do this stuff in C# by simply dragging and dropping a dll into the references and it works fine but is a bit slow. I was hoping I could speed things up using D but it seems like COM isn't really supported, despite what several references say.