June 17, 2016
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, &params, 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.
March 11, 2017
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?!?
March 11, 2017
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, &params, 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.

1 2 3 4
Next ›   Last »