21 Replies
      Latest reply on Apr 1, 2020 6:19 AM by hhas01
      alienspaces Level 1 Level 1 (20 points)

        Hi!

         

        I want to get access to the NSScriptClassDescription & NSScriptCommandDescription objects for a running application via the NSScriptSuiteRegistry object but I'm not having much luck.

         

        I was hoping that all running applications that have a sdef file would be listed when calling the var suiteNames: [String] function, but this doesn't return any suite names for running apps that have sdef files. It returns names like NSCoreSuite, NSTextSuite, ASKDocumentSuite.

         

        I then thought perhaps the suites aren't loaded until the app responds to an Apple Event so I fired off a quick NSScriptObject instance targeting the app I'm wanting to see the suite defs for, but that had no effect on suite names ruturned from NSScriptSuiteRegistry.

         

        Is it even possible to inspect the AppleScript definition for a 3rd party or Apple app with NSScriptSuiteRegistry? I really don't want to have to import an sdef into Core Data and build it myself, if I can simply query the system at run time.

        • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
          eskimo Apple Staff Apple Staff (13,895 points)

          I want to get access to the [terminology] for a running application via the NSScriptSuiteRegistry object but I'm not having much luck.

          Yeah, I don’t think that’ll work.  NSScriptSuiteRegistry is about the terminology exported by your app, not about the terminology of other apps.

          I believe the droid you’re looking for here is OSACopyScriptingDefinitionFromURL.  As far as I can tell this will issue the kGetAETE event to the app if required.

          Share and Enjoy

          Quinn “The Eskimo!”
          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
          let myEmail = "eskimo" + "1" + "@apple.com"

            • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
              alienspaces Level 1 Level 1 (20 points)

              Thanks for pointing out that Carbon API. :-)  I've not delved that deeply into AppleScript yet. Perhaps you can point me in the right direction as I start this project idea I have, or I may just be barking up the wrong trees.

               

              The idea is to get hold of the KVC definitions for the AS object graphs from a list of running applications and call them using KVC.

               

              While I can slurp up a sdef file and stick it into Core Data and run my command builder off those reference definitions, I was hoping to be able to get the AppleScript KVC keys at runtime via an Apple framework. Do you know if this is possible, and if so how? I was hoping to simply use the Scripting Bridge to query SBApplications using the cocoa keys in the sdef.

               

              > I believe the droid you’re looking for here is OSACopyScriptingDefinitionFromURLAs far as I can tell this will issue the kGetAETE event to the app if required.

               

              What is the consequence of that? Would that then ensure I'd be able to query the system for KVC keys somehow, or will it simply load the sdef XML into CFData?

                • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                  hhas01 Level 1 Level 1 (20 points)

                  “The idea is to get hold of the KVC definitions for the AS object graphs from a list of running applications and call them using KVC.”

                   

                  Apple event IPC = RPC + Queries, not OOP. It’s also notoriously high latency. Crawling the object graph will be murderously slow. Plus, application dictionaries are not IDLs; they are famously ambiguous/incomplete/incorrect, so there is a limit to how much you can rely on them.

                   

                  The more important question is: why do you want to do it? If you’re just trying to learn your way around scriptable applications, get Script Debugger which includes powerful dictionary and object graph browsers. If you’re thinking of writing your own, don’t bother: the whole AE/AS/OSA stack was put into maintenance mode and left to die quietly years ago. Barring miracles, its day is done.

                   

                  “I was hoping to simply use the Scripting Bridge to query SBApplications using the cocoa keys in the sdef.”

                   

                  There is no desktop automation task that cannot be made worse by trying to do it in Scripting Bridge. It’s junk. Stick to AppleScript. It’s the only [nominally] supported option that speaks Apple events right.

                   

                  If you like living dangerously then SwiftAutomation also speaks AEs right, but caveat emptor as I haven’t updated or tested it in a couple years and do not provide support.

                  • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                    eskimo Apple Staff Apple Staff (13,895 points)

                    I don’t really understand what you mean by “KVC definitions”.  I presume you’re using KVC as a shortcut for key-value-coding, but that’s a Cocoa concept that doesn’t really map well to Apple events.

                    Share and Enjoy

                    Quinn “The Eskimo!”
                    Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                    let myEmail = "eskimo" + "1" + "@apple.com"

                      • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                        alienspaces Level 1 Level 1 (20 points)

                        Hi Quinn,

                         

                        > "I presume you’re using KVC as a shortcut for key-value-coding"

                         

                        Yes! Sorry for not being particularly clear. What I mean by "KVC definitions" is the KVC-compliant keys in the sdef file. All I really want to do is use those keys to access the SBApplication attributes and elements just like any other KVC code would. I don't have a need for working directly with Apple Events. It's the Scripting Bridge that really interests me.

                         

                        So the question is, is it possible to query a system framework at runtime to get these KVC keys that are described in a running app's sdef file?

                          • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                            eskimo Apple Staff Apple Staff (13,895 points)

                            All I really want to do is use those keys to access the SBApplication attributes and elements just like any other KVC code would.

                            Yeah, that’s going to be a challenge.  ScriptingBridge tries to impose a Cocoa interpretation on an AppleScript world, which is tricky stuff.  To get this info from an AppleScript terminology ('aete') — that is, the thing you get back from OSACopyScriptingDefinitionFromURL — you would have to reimplement that logic.  It would be nice if ScriptingBridge exported that mapping but, AFAICT, it does not.

                            hhas01 and I disagree on many things, but in this case I concur with their conclusion: Skip ScriptingBridge and do this work in terms of Apple events.

                            Share and Enjoy

                            Quinn “The Eskimo!”
                            Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                            let myEmail = "eskimo" + "1" + "@apple.com"

                              • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                                hhas01 Level 1 Level 1 (20 points)

                                “ScriptingBridge tries to impose a Cocoa interpretation on an AppleScript world”

                                 

                                This is why SB’s ORM approach doesn’t (and can’t) work right. ORMs covering RDBMSes are notoriously twitchy; and those only have to cover half a dozen apps speaking broadly the same language (SQL). In AppleScript’s world, there are hundreds of apps with wildly different structure and commands, and little consistency in behavior.

                                 

                                The only information in an AETE/SDEF that AppleScript actually uses are the name/OSType mappings and the type of each (type/enum/property/command/parameter). Everything else is ignored—classes and inheritance are a fiction created for documentation purposes; even one-to-one and one-to-many relationships (containment) aren’t guaranteed to hold. (You’ll see this logic replicated in SwiftAutomation’s AETE/SDEF parsers, AS quirks and all.)

                                 

                                It would really help if OP would describe the problem they’re trying to solve. We can talk about “how” till the cows come home, but unless we understand the “why” we don’t know if our advice is appropriate or not.

                                • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                                  alienspaces Level 1 Level 1 (20 points)

                                  Thanks for the advice. I'll have to experiment a bit and see what is going to be a good fit for what I'm designing.

                                   

                                  If I went the Apple event route, would it be easier to figure out at runtime what events an app responds to or would I have to parse the sdef in the app's bundle? Parsing the sdef is not really an issue for me, I've already designed a Core Data model from the SDEF.dtd, but, if as you say, there is no other way to get this info for a running app, then I'll have to parse it. That way, I'm guessing I'd make use of the AE codes in the sdef and not the <cocoa> element KVC-compliant key mappings, and use those with the Carbon framework?

                                   

                                  My macOS app is obviously written in Swift and (Objective-C where necessary), so I just assumed Sripting Bridge would reduce the amount type coersion I've had to get involved in etc. Is not going to be quite difficult to handle type coersion and getting the AE data to play nicely with a Swift GUI?

                                   

                                  I haven't read the legacy document, Apple Events Programming Guide (2007) yet. Is it still fairly accurate?

                                   

                                  Is it not possible to use a support ticket to get some private feedback from you? I simply can't publically divulge some of the design details of my app. It's just frustrating because if I could tell you privately exactly what I'm trying to achieve, it may have a bearing on your opinion.

                                    • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                                      hhas01 Level 1 Level 1 (20 points)

                                      “That way, I'm guessing I'd make use of the AE codes in the sdef and not the <cocoa> element KVC-compliant key mappings, and use those with the Carbon framework?”

                                       

                                      I think you’re getting confused by the KVC data found in .sdef files. That data is not provided for clients’ use; it’s purely for use by the CocoaScripting framework upon which Cocoa-based apps implement their AE support. See `man 5 sdef` if you’ve not already done so.

                                       

                                      As to navigating an app’s object graph, run-time introspection is poor/non-existent. CS-based apps have a standard `properties` property, but this only tells you about attributes and one-to-one relationships; it doesn’t tell you what one-to-many relationships (elements) exist. Nor is it a standard feature in older non-CS based apps (e.g. most MS, Adobe apps). You have to resort to reading the app’s aete/sdef; however, even that is troublesome, e.g. SDEF does not distinguish between attributes of type `text` (really, typeUnicodeText) and one-to-one relationships of type `text` (really cText), so you’ve no idea if a given property is one or other except to try querying elements (e.g. `every character of PROPERTY`) and see if the event succeeds or fails. Human users, of course, learn an app’s object model and recognize which is which, e.g. `name of front document of app "TextEdit"` vs `text of front document of app "TextEdit"`; an automated crawler will not, so will have to trial-and-error extensively.

                                       

                                      And remember, AppleScript does not use any of that “type” information itself, so there are zero guarantees that information is accurate. e.g. Properties that can return, say, a string or `missing value` constant may declare their type as `text`, so even if you do a `get` there is no guarantee the result is a String. If you try to enforce that type (as SB does), you won’t get the right result. And that’s even before we consider implementation bugs in CS and/or apps, e.g. try `tell app "TextEdit" to get path of front document` and see what happens when the front document is newly created and hasn’t been saved yet. This is why SwiftAutomation leaves casting entirely to the client code, e.g. `PROPERTY.get() as String?`; it expects the code’s author knows that property returns either String or `missing value`, regardless of what the app’s dictionary may claim, so puts them in control. Works great, and takes advantage of the Apple Event Manager’s ability to coerce between various types so if you say `[String]` and the returned data is 1.0 then you’ll still get `["1.0"]`, but it’s not something you can automate as SDEF data’s not reliable enough.

                                       

                                      Another example: `tell application "Finder" to get entire contents of desktop`, where the dictionary defines the `entire contents` property as type `specifier` (typeObjectSpecifier). Run that command and it returns a list of specifiers. Yet you can also write `tell application "Finder" to get name of every file of entire contents of desktop`, so it’s clear the property’s type is not a list. As with `text of front document of app "TextEdit"`, or `text of every document of app "TextEdit"`, what you get it partly dependent on the query you ask, and partly dependent on what the app anticipates is the most helpful result for you. (Unfortunately, while AE IPC has the facility to do content negotiation—see keyAERequestedType—it is poorly specced, underpowered, and rarely supported by applications.)

                                       

                                      ..

                                       

                                      Again, if you’re trying to extract all data from each running app, you are going to be crushed at how unspeakably slow it all is, even if you do apply each `get` operation to all elements at once. I just did a job for a customer, automating a task in Adobe Illustrator, which I prototyped in Python3+appscript because AppleScript is just a PITA. That script only sent a few thousand AEs; took half an hour to run!!! I dunno exactly what it was tickling wrong (a couple minutes is more typical performance), but even on a good day AEOM queries are hella expensive and any form of IPC is inevitably magnitudes slower than in-process messaging. (The production version, written for Illustrator’s own in-process JS engine: 10secs.)

                                       

                                      That’s why, in practice, we only query/manipulate the remote data we need. Pulling mass data over the wire is totally impractical performance-wise; plus there’s no transactional guarantees so even while you’re doing it the remote state may be simultaneously changing.

                                       

                                      Apple event IPC may have its virtues—e.g. I’ve long argued its query-driven model could be a great match for Siri’s query-builder (especially if the AEOM spec was tightened up)—but when you get down to details it is painfully rough to deal with.

                                       

                                      There’s two decades’ hard experience encoded in SwiftAutomation, and even then it’s not perfect, but it’s maybe worth a skim of the code just to get some appreciation of how and why it works the way it does. Because every other approach (gensuitemodule, JavaScriptOSA, aeve, RubyOSA, and of course ScriptingBridge) falls apart when real-world *AppleScriptable* apps stomp all over their assumptions.

                                       

                                      Oh, and for general insight into Apple event IPC and how and why it works the way it does (and why it was left so awkward and unpolished), read this paper by one of its original designers:

                                       

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

                                       

                                      You’ll learn more from that than you ever will from Apple’s own public developer docs, which are riddled with omissions and lies, it pains me to say.

                                       

                                      Not trying to deter you, mind (must be a pretty good idea to make you so determined to pursue it!); just letting you know what a world of hurt you are in for!

                                        • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                                          alienspaces Level 1 Level 1 (20 points)

                                          "I think you’re getting confused by the KVC data found in .sdef files. That data is not provided for clients’ use; it’s purely for use by the CocoaScripting framework upon which Cocoa-based apps implement their AE support."

                                           

                                          I realize this 100%. I'm really not confused about sdef files or the data therein. I've read the Cocoa Scripting Guide and the Scripting Bridge Programming Guide for Cocoa. So I know why sdefs exist and what the data therein describes. I think the mistake I've made is in assuming that every app has to provide cocoa keys in their sdef, but they don't. So just because an app has a scripting defintion doesn't mean you can access it via cocoa unless use use the sdp tool and link the generated header in. Which is not going to help me.

                                           

                                          If you want to send Apple events to a running app that has Apple Script support, can I do this using the AE code information in the sdef file and creating my own events? I haven't read the Apple Event Programming Guide yet. Just wondering how far I'd get with this approach.

                                            • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                                              eskimo Apple Staff Apple Staff (13,895 points)

                                              If you want to send Apple events to a running app that has Apple Script support, can I do this using the AE code information in the sdef file and creating my own events?

                                              Yes.  This is exactly what Script Editor does when you run a script.

                                              Share and Enjoy

                                              Quinn “The Eskimo!”
                                              Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                              let myEmail = "eskimo" + "1" + "@apple.com"

                                                • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                                                  alienspaces Level 1 Level 1 (20 points)

                                                  Thanks very much for your help. I know I'm on the correct track now.

                                                  • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                                                    hhas01 Level 1 Level 1 (20 points)

                                                    “Yes. This is exactly what Script Editor does when you run a script.”

                                                     

                                                    Not exactly. SE is just a host for OSA language components. The language itself is responsible for building and sending AEs. In AppleScript’s case, the parser uses lookup tables built from an app’s AETE resource to convert property/element/command/etc names in a script’s source code to the corresponding OSTypes (four-char codes) which are stored in the generated bytecode. On executing that bytecode, the interpreter calls the C AEM APIs to assemble typeObjectSpecifier and typeAppleEvent records and send them to the app process.

                                                     

                                                    Since OSA is built on the Carbon ComponentManager (which is an old and gnarly in-process plugin mechanism), and SE (also being old and gnarly) loads OSA components directly instead of into separate subprocesses as per modern recommendations, then yeah, outgoing AEs and their replies will be sent from and to the SE process itself, which is why Apple grant SE special exemption to standard sandboxing rules. But that’s a separate pain point/discussion, so let’s not get into that here.

                                                      • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                                                        alienspaces Level 1 Level 1 (20 points)

                                                        This subprocess issue is one I was thinking about recently. I see there are security issues regarding IPC and just haven't had the time to look into it yet. An app has to request temporary entitlements or specify exactly which apps it wan't to communicate with. But I guess those entitlements would extend to subprocesses. If anything it would prevent the main app process from crashing and allow you to save face albeit only slightly if an AE IPC process crashed.

                                                          • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                                                            eskimo Apple Staff Apple Staff (13,895 points)

                                                            But I guess those entitlements would extend to subprocesses.

                                                            That is a complicated question, and one I’d recommend that you avoid trying to deal with.  Fortunately that’s not a problem because…

                                                            If anything it would prevent the main app process from crashing and allow you to save face albeit only slightly if an AE IPC process crashed.

                                                            This is not necessary.  If the process you target via Apple events crashes, your Apple event operation will fail with an error.  It won’t trigger a crash in your process.

                                                            Share and Enjoy

                                                            Quinn “The Eskimo!”
                                                            Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                                            let myEmail = "eskimo" + "1" + "@apple.com"

                                                              • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                                                                alienspaces Level 1 Level 1 (20 points)

                                                                Thanks for clearing that up for me. Once I learnt the AEM data structures and had a read through a lot of Inside Macintosh: Interapp Communication, it seems quite easy to follow.

                                                                 

                                                                Any idea where the Text Suite's script definition files are located on macOS? I've found ScriptingAddtions, DatabaseEvents etc, but can't seem to find the Text Suite.

                                                                  • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                                                                    hhas01 Level 1 Level 1 (20 points)

                                                                    I don’t think there is one. There’s a default Standard Suite definition at /System/Library/ScriptingDefinitions/CocoaStandard.sdef, which CS-based apps can XInclude in their own SDEFs (because nothing makes XML parsing more fun than having to deal with XIncludes too; this is why SA ended up using Cocoa’s bulky XML DOM API instead of faster, simpler CF SAX APIs), but AFAIK CocoaScripting’s default Text Suite is always included via cut-n-paste.

                                                                     

                                                                    Don’t expect too much rigor or polish from CocoaScripting. IIRC it was originally created by a NeXT engineer working blind from internal AppleScript documentation, and later patched up by the Mac Automation team into something that usually mostly works, but it has various legacy bugs and flaws in addition to the inevitable challenge of trying to be a robust, efficient, usable framework for a painfully underspecced AEOM. Fair attempt, but far from complete success.

                                                                     

                                                                    BTW, the standard Text Suite implementation is famously buggy, e.g. this will disappear your text before your eyes, so don’t run it on anything important:

                                                                     

                                                                    tell front document of application "TextEdit"

                                                                        move last word of paragraph 1 to end of paragraph 2

                                                                    end tell

                                                                     

                                                                    (It’s also, as I recall, fabulously slow; CS’s one-size-fits-all query-mapping abstraction doesn’t do well representing lots of single characters; quelle surprise. It would’ve made more sense to treat `character` elements as *character ranges*, i.e. substrings, which is what users invariably want anyway.)

                                                          • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                                                            hhas01 Level 1 Level 1 (20 points)

                                                            Be very wary of the SB documentation—it is a fiction, and very misleading. (There’s a reason SB and JXA never became popular, even though Mac Automation should be catnip for geeks.)

                                                             

                                                            As to CS documentation, it’s worth reading around. Third-party tutorials can offer insights that the Apple docs don’t. e.g. Here’s one by Matt Neuburg (old, but still worth reading as CS itself hasn’t changed much):

                                                             

                                                            www.apeth.net/matt/scriptability/scriptabilityTutorial.html

                                                             

                                                            One good thing about Apple’s CS docs if memory serves: it’s the once place where they do talk about one-to-one and one-to-many relationships, which is the key to understanding how an Apple Event Object Model really works.

                                                             

                                                            Also bear in mind that CS’s behaviors (which are themselves not without bugs and quirks) only apply to apps built on CS, and don’t generalize to apps built on the older, lower-level Apple Event Manager APIs, which are much more variable in their behaviors.

                                                             

                                                            ..

                                                             

                                                            Yes, you can construct and send AEs yourself using NSAppleEventDescriptor. It’s low-level and a bit of a chore, but it can be done if that’s what you need. It’s what the “stable” version of SwiftAutomation on my BitBucket uses.†

                                                             

                                                            As for finding SwiftAutomation useful, nice to hear it. Alas, I couldn’t get Apple to take it (the offer stands), which is why I don’t see much point in pursuing it further. As far as studying other code, the only other AE bridge of worth is Frontier’s, but I don’t think you’ll glean much from that as it’s ancient, ancient C; besides I’ve already been through that and every other AE bridge myself and distilled all that knowledge into appscript, which in turn is refined into SA.

                                                             

                                                            You can message me via my SF profile (sourceforge.net/u/hhas), though as you can tell I’m pretty cheesed off at the whole mess and make no promises as to how helpful I can be.

                                                             

                                                            --

                                                             

                                                            († There’s also an experimental fork of SA on my GitHub which assembles AE data directly, being intended as a modern cross-platform replacement to AEM itself, but ignore that. I got most of the way down to the Mach layer, but it’s a lot of effort to black-box reverse-engineer that last step when Apple themselves aren’t interested.)

                                                              • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                                                                alienspaces Level 1 Level 1 (20 points)

                                                                >One good thing about Apple’s CS docs if memory serves: it’s the once place where they do talk about one-to-one and one-to-many relationships, which is the key to understanding how an Apple Event Object Model really works.

                                                                 

                                                                I'm actually glad I read the higher-level Cocoa docs first. It's actually very easy to understand the design.

                                                                 

                                                                > As far as studying other code, the only other AE bridge of worth is Frontier’s, but I don’t think you’ll glean much from that as it’s ancient, ancient C

                                                                 

                                                                I was reading an article on AE from 1992 and all the example code was written in Pascal :-O

                                                                 

                                                                Thanks for the SourceForge link. I'll only message you if I get really stuck. Plus I've still got to read the AE Programming Guide this weekend and go through your project, so you probably won't hear from me for a while.

                                                            • Re: Querying Class & Command Descs from NSScriptSuiteRegistry
                                                              alienspaces Level 1 Level 1 (20 points)

                                                              Also, thanks for sharing your work on SwiftAutomation. For me it's invaluable as I haven't found any other code yet. I've had a cursory glance over your documentation and some of the code. I see what you mean by 20 years of hard experience going into the code. The example that interests me the most is the creation of NSAppleEventDescriptions and their collaboration with AEM. It looks like I'll have to fire off events based off the sdef data. Do you have an email or website submission form I can get you on if I have any questions? :-)