February 14, 2016
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?



February 15, 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.