Scriptability (AppleScript) in a Mac Carbon applic

2020-08-01 06:25发布

问题:

I am trying to add AppleScripting support to my non-Cocoa based application.

I'm working the low level functions such as AEGetParamDesc, handling the form / want / seld parameters myself.

My vocabulary offers a class, let's call it "Image". It has a property "name".

I've gotten some Applescript code working, such as:

get Images
get name of every Image
get count Images
get every Image
get first Image
get Image 1

So, basically, access to both the objects and its properties works.

However, when I tried these similar access forms, they all fail:

get Images whose name = "foo"

and

repeat with img in Images
end repeat

In the first case, it appears I'll have to handle a test form.

In the second case, the count operator (cnte) does not request the class object directly but instead uses a cobj operator describing an index object.

This all makes me wonder how far this will go. Will I have to implement every possible syntax and operator of Applescript individually in my code? I'd assume that the "whose" operator would simply combine the requests for "every Image" and "name of Image x" the way I can indivually write them in Applescript, instead of using different AppleEvent formulas for each of them.

Same for whose <boolean-test>. Why doesn't AppleScript simply perform the equality test of name = "foo" itself, as it's a text comparison which should not have to involve my application code at all?

Is there something I'm missing? Can I forward these to AE functions I'm not yet aware of or do I have to handle every possible comparison and flow control command myself?

回答1:

Thomas Tempelmann (of Find Any File fame?), if you really can't rewrite the app in Cocoa, here is an article I wrote many years ago with some detail about coding (in C) for scriptability pre-Cocoa, including handling formWhose, which you asked about above.

http://www.mactech.com/articles/develop/issue_28/reuter.html

The article was published by Apple in its Develop magazine, and included source code I wrote for a sample application called "Sketch". This was years before Apple released its own sample project also named "Sketch." I still have the source code if it would help you.

Good luck!



回答2:

The Apple Event Object Model was designed for high-latency IPC on an OS (System 7) that could do 60 context switches per second max. Complex queries and procedures that can operate on multiple objects allow more work to be done using fewer cross-process messages. In addition, it's designed to be a relatively thick View-Controller abstraction with the emphasis on UI/UX, presenting an idealized view of the user's data as a relational graph, regardless of how the underlying data is actually stored or how complex or difficult to implement the VC code to map from one to the other may be. The AEOM has far more in common with relational databases than anything in the OOP world, while the closest analogy to Apple event IPC would be sending XQueries over XML-RPC. For background see:

http://www.cs.utexas.edu/~wcook/Drafts/2006/ashopl.pdf

Nowadays, of course, OS X can do thousands of process switches per second without breaking a sweat, so there's less necessity to implement a complex AE View-Controller just to get acceptable performance. TBH, I'd suggest you save your own time and implement the simplest RPC that works simply, reliably, and fast. Restrict handlers that mutate state to operating on a single object per message (because doing Set operations on Arrays is hell to get right), implement the simplest query forms needed to locate objects, and return by-ID specifiers that basically act as safe pointers to previously identified objects so users can rapidly manipulate them with further commands without the cost of repeatedly roundtripping complex full queries.

Oh, and unless you've a particular reason to be using C, I'd recommend using NSAppleEventDescriptor and NSAppleEventManager only. The C Apple Event Manager API is ancient, gnarly, and legacy since 10.6 so not recommended for new development. You might (repeat, might) even find a way to use of some of the Cocoa Scripting classes to do a bit of the heavy lifting w.r.t. object specifiers, although CS is pretty awful itself and heavily coupled to ApplicationKit so don't waste time messing with it unless it actually helps (CS has buried better projects before).

The other thing, of course, is that the entire Mac Automation ecosystem is in a thoroughly moribund state, and unlikely to get any better under the current management, so the cost-vs-benefit of developing a complete, sophisticated AEOM from scratch just isn't there any more. If "simple, fast, safe, and dumb" is "good enough" for whatever users you hope to attract, just do that.



回答3:

Apple's sample code for Sketch provides a good example of using the Cocoa Scripting API.

The sample code does not perform any special handling for "repeat" and "whose", yet it can run AppleScripts using these terms.

This suggests that Cocoa Scripting, i.e. mainly the classes and protocols in NSScriptObjectSpecifiers.h, takes care of the complex handling that the old Carbon API exposes.

Therefore, also based on @foo's answer, it appears to be smart to attampt to create proxy classes based on NSObject(NSScriptObjectSpecifiers), implementing the objectSpecifier method as well as get/set accessors for any properties, and then use the ability to reference those classes in the .sdef file. Even without Objective C, such classes can be created using the ObjC runtime functions such as objc_registerClassPair.