Photoshop programming

thedeemon via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Mon Feb 15 04:18:47 PST 2016


On Sunday, 14 February 2016 at 09:48:54 UTC, Patience wrote:
> Photoshop has the ability to be controlled by scripts and 
> programming languages. For example, C# can be used to access 
> photoshop by adding the appropriate reference and using 
> directives. I believe it is COM based but I am not totally sure.
>
> I've tried reading the docs but it's not making much sense.
>
> http://www.lunesu.com/uploads/ModernCOMProgramminginD.pdf
>
> links at the bottom are down.
>
> It says one has to create a wrapper, but then talks like it can 
> be done automatically. I assume that is what project does? But 
> then one has to create an idl, not sure what that is ;\
>
> Any info on how to do this stuff properly?

Unfortunately the project mentioned in that presentation seems to 
be unavailable.

In order to work with COM objects from D you need to have 
definitions of their interfaces translated to D. Initially they 
are often described in IDL files (Interface Definition Language) 
or TLB files (type library), and there are nice tools like 
tlb2idl.exe and idl2d.exe that can convert them to .d files. You 
can find them in the VisualD project tree. And if original 
definitions are in C++, in many cases Ctrl-C-Ctrl-V is all you 
need to convert to D. ;)

Then you may work with them directly or you might want a wrapper 
that will call Release() for you when appropriate. I'm using a 
wrapper like this:
https://gist.github.com/thedeemon/3c2989b76004fafe9aa0
Originally taken from VisualD source but then modified to my 
needs.

For example, I need to use IAMVfwCompressDialogs interface from 
Microsoft SDK. It's defined in axextend.idl file in the SDK and 
looks like this:

     [
         object,
         local,
         uuid(D8D715A3-6E5E-11D0-B3F0-00AA003761C5),
         pointer_default(unique)
     ]
     interface IAMVfwCompressDialogs : IUnknown
     {

         // Bring up a dialog for this codec
         HRESULT ShowDialog(
             [in]  int iDialog,   // VfwCompressDialogs enum
             [in]  HWND hwnd
         );

         // Calls ICGetState and gives you the result
         HRESULT GetState(
             [out, size_is(*pcbState), 
annotation("__out_bcount_part(*pcbState, *pcbState)")] LPVOID 
pState,
             [in, out, annotation("__inout")]  int *pcbState
         );

         // Calls ICSetState
         HRESULT SetState(
             [in, size_is(cbState), 
annotation("__in_bcount(cbState)")] LPVOID pState,
             [in]  int cbState
         );

         // Send a codec specific message
         HRESULT SendDriverMessage(
             [in]  int uMsg,
             [in]  long dw1,
             [in]  long dw2
         );
     }

I use idl2d.exe and get following D code (minus some comments):

const GUID IID_IAMVfwCompressDialogs = IAMVfwCompressDialogs.iid;

interface IAMVfwCompressDialogs : IUnknown
{
     static const GUID iid = { 0xD8D715A3,0x6E5E,0x11D0,[ 
0xB3,0xF0,0x00,0xAA,0x00,0x37,0x61,0xC5 ] };

     // Bring up a dialog for this codec
     HRESULT ShowDialog(
                        in  int iDialog,   // VfwCompressDialogs 
enum
                        in  HWND hwnd
                            );

     // Calls ICGetState and gives you the result
     HRESULT GetState(
                      LPVOID pState,
                      int *pcbState
                          );

     // Calls ICSetState
     HRESULT SetState(
                      in LPVOID pState,
                      in  int cbState
                          );

     // Send a codec specific message
     HRESULT SendDriverMessage(
                               in  int uMsg,
                               in  int dw1,
                               in  int dw2
                                   );
}

Then in my D code I just write

     enum { VfwCompressDialog_Config = 0x01, 
VfwCompressDialog_About =  0x02 }
     ...
     auto vfw = ComPtr!IAMVfwCompressDialogs(pg.vcodec);
     if (vfw) vfw.ShowDialog(VfwCompressDialog_Config, hwnd);

where pg.vcodec is my variable of type ComPtr!IBaseFilter I 
created earlier. Here the ComPtr wrapper uses internally 
QueryInterface() to get pointer to desired interface, and when my 
vfw variable goes out of scope, Release() will be called 
automatically. Moreover, if ShowDialog here returns a bad value a 
COMException will be generated automatically (this is my own 
addition to ComPtr behavior).

To create some COM object in the first place, knowing its CLSID, 
I do like this:

     auto CLSID_NullRenderer = 
Guid!("C1F400A4-3F08-11D3-9F0B-006008039E37"); //qedit.dll
     auto pNullRendr = ComPtr!IBaseFilter(CLSID_NullRenderer, 
"null renderer");

Here IBaseFilter is just some COM interface from Microsoft SDK I 
need.
ComPtr constructor understands what to do depending on whether 
it's given some GUID or a pointer to some object.

Moral of this story, if you have the COM interfaces defined in D, 
you can have wrappers to do resource management, interface 
querying and error checking automatically, but first you need to 
translate (often using automatic tools) the interfaces.


More information about the Digitalmars-d-learn mailing list