1

I am a C programmer, but I am new to Windows & COM programming.

For the following C# code:

//Change the font of the selected text in the running PowerPoint.
Application app = Marshal.GetActiveObject("Powerpoint.Application");
app.ActiveWindow.Selection.TextRange.Font = ...;

I know how to write the corresponding OLE code in C. To recap briefly:

  1. Call GetActiveObject to get the IUnknown interface of the application;
  2. Call IUnknown_QueryInterface to get the IDispatch of the application;
  3. Call IDispatch_GetIDsOfNames and IDispatch_Invoke with DISPATCH_PROPERTYGET to get the named property of the current IDispatch.
  4. Repeat Step 2 to Step 4, until we reach the second innermost property(that is TextRange in the above example).
  5. Call IDispatch_GetIDsOfNames and IDispatch_Invoke with DISPATCH_PROPERTYPUT to set the value of innermost property(that is Font in the above example).

here comes the question: What if a property is an "array"? For example, given the following C# code:

Application app = Marshal.GetActiveObject("Illustrator.Application");
foreach (object textFrame in app.ActiveDocument.TextFrames)
    ...;

The above C# code iterates every text frame from the TextFrames "array", I have no idea of the corresponding C code for "iterating" such a property in C. I have searched through the Internet and Microsoft development references, but failed to get any clue.

How do I get members from an "array" property using C?

張俊芝
  • 187
  • 1
  • 9
  • It is not an array. https://learn.microsoft.com/en-us/windows/win32/api/oaidl/nn-oaidl-ienumvariant – Hans Passant Dec 03 '20 at 12:56
  • Are you using Visual Studio? is C++ ok? Do you really want to enumerate or scanning each item by index is ok? – Simon Mourier Dec 03 '20 at 20:36
  • @SimonMourier I am using MSYS + Clang, I am a regular Linux programmer, and was temporarily working on Windows. The issue has been solved with help of xMRi – 張俊芝 Dec 04 '20 at 04:49
  • You can have a look at the type library using OleView tool from the Windows SDK. The type library for illustrator is located at C:\Program Files\Adobe\Adobe Illustrator 2021\Plug-ins\Extensions\ScriptingSupport.aip (adapt to your context). It will tell you what the `Document` type implements. You don't have to use _NewEnum, you can also use `long Count()` and `TextFrame* Item(VARIANT key)` methods which is usually a bit easier from C/C++ and allows direct access to an item using its index. – Simon Mourier Dec 04 '20 at 07:41
  • @SimonMourier Thank you for your information about the tool and the alternative solution. This tool you recommended may also help in future development. – 張俊芝 Dec 04 '20 at 15:03
  • @SimonMourier Excuse me, one more question: What if the Item's key is a string? For example, I saw C# code `application.TextFonts[""]`, I used OLE Viewer to read the ScriptingSupport.aip, and found that TextFonts has `Item([in] VARIANT ItemKey)`, then I called Invoke to try to get the TextFonts's item with DISPPARAMS assigned the following values: rgdispidNamedArgs = NULL, cNamedArgs = 0, cArgs = 1, rgvarg = , but the Invoke function failed with Code DISP_E_BADINDEX. What is the problem? Thank you. – 張俊芝 Dec 05 '20 at 08:41
  • If using a text as index works using scripts or .NET then it sure can work using C/C++. Devil hides in details. Post another question with your calling code. – Simon Mourier Dec 05 '20 at 09:02

1 Answers1

0

Textframe may not be an array. I suppose it is an enumerator. But check the returned type in the debugger.

Or you need to show us the type lib or idl file for the interface you are using.

In if it is an iterator the TextFrames object has a property _NewEnum. This has an interface IEnumVARIANT... usually OnNext is called until OnNext returns S_FALSE.

SO what is happening here is that ForEach asks calls the _NewEnum property of the TextFrames object. It may call OnReset or OnClose and than calls OnNext for each member...

If it is an array you get a VARIANT containing a SAFEARRAY.

xMRi
  • 14,982
  • 3
  • 26
  • 59
  • Thanks for your informative guide. I got a VARIANT whose vt is VT_DISPATCH, so It seems like an enumerator, as you suggested. But there's still one more step to solve the issue. I called IDispatch_GetIDsOfNames(textFramesVar.pdispVal, &IID_NULL, (LPOLESTR[1]){L"_NewEnum"}, 1, LOCALE_USER_DEFAULT, &dispId); GetIDsOfNames just failed(and using GetIDsOfNames to get the ID of _NewEnum sounds wrong to my instinct). Is getting the SDK, quering the interface and then calling Get__NewEnum the only way to get the _NewEnum property? Thank you. – 張俊芝 Dec 03 '20 at 14:29
  • DISPID_NEWENUM is a constant... and fixed for all enumerators. – xMRi Dec 03 '20 at 18:57
  • Reference: https://learn.microsoft.com/en-us/previous-versions/windows/desktop/automat/dispid-constants – Michael Gunter Dec 03 '20 at 18:58
  • @xMRi Great, it works! Thanks for the help. – 張俊芝 Dec 04 '20 at 04:44