4 Replies
      Latest reply on Nov 19, 2019 2:36 AM by hhas01
      Vannes Level 1 Level 1 (0 points)

        Hi there,

        My macOS application is able to control Powerpoint through AppleScript. The first step of controlling is to see if Microsoft Office installed or not. Here is the worked AppleScript:

        set appID to "com.microsoft.Powerpoint"
        -- check if the application exists
        set doesExist to false
        try
          tell application "Finder" to get application file id appID
          set doesExist to true
        end try
        
        return doesExist

        If the Office is installed, the script returns "true" and "false" vice versa. The script is working under Script Editor and Xcode 8.3.3.

        However I upgraded my Xcode to 10.3, the script always return "false". This stops me to do notarization. How my object-C call AppleScript API is as below:

        // AppleScript macro
        + (NSString *)pptIsInstalledOrNot
        {
            return @"\n\
            set appID to \"com.microsoft.Powerpoint\" \n\
            -- check if the application exists \n\
            set doesExist to false \n\
            try \n\
                tell application \"Finder\" to get application file id appID \n\
                set doesExist to true \n\
            end try \n\
            \n\
            return doesExist \n\
            ";
        }
        
        // AppleScript execution 
        + (NSAppleEventDescriptor *)runProcess:(NSString *)script
                              errorDescription:(NSString **)errorDescription
        {
            NSDictionary *errorInfo = [[NSDictionary alloc] init];
            NSAppleScript *appleScript = [[NSAppleScript alloc] initWithSource:script];
            NSAppleEventDescriptor *eventResult = [appleScript executeAndReturnError:&errorInfo];
            
            // Check errorInfo
            if (!eventResult) {
                // Set error message
                if ([errorInfo valueForKey:NSAppleScriptErrorMessage]) {
                    id message = [errorInfo valueForKey:NSAppleScriptErrorMessage];
                    if (errorDescription) {
                        *errorDescription = (NSString *)message;
                    }
                }
            }
            else {
                // Set output to the AppleScript's output
                
            }
            
            return eventResult;
        }
        
        // main process 
        
        Boolean isInstalled = false;
            {
                NSString *script = [self pptIsInstalledOrNot];
                NSString *errorMessage = nil;
                NSAppleEventDescriptor *descriptor = [self runProcess:script errorDescription:&errorMessage];
                isInstalled = [descriptor booleanValue];
            }
        
        

        I ever tried other ways, still failed. How do I modify my program on XCode 10.3? Appreciate for any ideas and

        thanks your time.

        • Re: Can not control Powerpoint through AppleScript built by Xcode above 10.3
          eskimo Apple Staff Apple Staff (12,425 points)

          Is there a specific reason you’re doing this via AppleScript?  If you’re starting with native code, you could just as easily use NSWorkspace.  For example, on my machine this code:

          NSLog(@"%@", [NSWorkspace.sharedWorkspace URLForApplicationWithBundleIdentifier:@"com.microsoft.Powerpoint"]);
          NSLog(@"%@", [NSWorkspace.sharedWorkspace URLForApplicationWithBundleIdentifier:@"com.apple.iWork.Keynote"]);

          prints this:

          … (null)
          … file:///Applications/Keynote.app/

          indicating that I have Keynote but no PowerPoint.

          Share and Enjoy

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

            • Re: Can not control Powerpoint through AppleScript built by Xcode above 10.3
              Vannes Level 1 Level 1 (0 points)

              Thank you Quinn for such efficiently response, to be honest we are developing an application to control PowerPoint/Keynote, we found that the AppleScript is the only way controlling PowerPoint and we developed many AppleScript macros to control it.

               

              I will replace current method by the mothod you are suggesting me, but some other function we've implemented might still not work(I am afraid of it). For instance, Play slide/Stop play slide, draw, blackout screen...,etc.

               

              Anyway I will take your suggestion first then go forward and update my status, thanks!

                • Re: Can not control Powerpoint through AppleScript built by Xcode above 10.3
                  eskimo Apple Staff Apple Staff (12,425 points)

                  we are developing an application to control PowerPoint/Keynote, we found that the AppleScript is the only way controlling PowerPoint and we developed many AppleScript macros to control it.

                  Fair enough.  It’s perfectly feasible to run AppleScripts to control scriptable apps.  There are, however, some hoops you need to jump through.

                  Note  As you mentioned notarisation, I’m assuming that you plan to deploy outside of the Mac App Store.  If that’s incorrect, and you’re targeting the Mac App Store, things get trickier.  Let me know if that’s the case.

                  On modern systems (10.14 and later) there are two things that you need to do in order to get a non-sandboxed app to script other apps:

                  1. Add an NSAppleEventsUsageDescription string to your Info.plist.

                  2. If you have the hardened runtime enabled — which you’ll need if you want to notarise your app — you must add the Apple Events entitlement (com.apple.security.automation.apple-events).

                  With both of those, the following code activates Keynote and starts the current presentation:

                  static NSString * scriptText = @
                      "tell application \"Keynote\"\n"
                      "    activate\n"
                      "    start document 1\n"
                      "end tell"
                      ;
                  NSAppleScript * script = [[NSAppleScript alloc] initWithSource:scriptText];
                  assert(script != nil);
                  NSDictionary * error = nil;
                  NSAppleEventDescriptor * result = [script executeAndReturnError:&error];
                  if (result == nil) {
                      // … look at `error` …
                      NSLog(@"error: %@", error);
                  } else {
                      // … look at `result` …
                      NSLog(@"result: %@", result);
                  }

                  I tested this on 10.14.6 but I believe it’ll also work on 10.15 just fine.

                  Share and Enjoy

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

                  • Re: Can not control Powerpoint through AppleScript built by Xcode above 10.3
                    hhas01 Level 1 Level 1 (20 points)

                    we found that the AppleScript is the only way controlling PowerPoint and we developed many AppleScript macros to control it

                     

                    Yep, AppleScript is the only [nominally] supported solution that works right; especially with older Carbon-based apps like PowerPoint. Stick to it.

                     

                    However, for any non-trivial automation work I strongly recommend you avoid NSAppleScript and instead call into AppleScript via the AppleScript-ObjC bridge:

                     

                    appscript.sourceforge.net/asoc.html

                     

                    ASOC makes your AS scripts and handlers appear as native Cocoa objects and methods to your ObjC code, automatically converting argument and result values between standard AS and Foundation types, making it trivially easy to call AS handlers from your ObjC methods and vice-versa.

                     

                    It’s not perfect—C primitives (bool, int, double, etc) must be manually boxed as NSNumber, you’ll need to trap any AS errors within your AS handlers, and AS object specifiers don’t round-trip through ObjC very well—but it’s still leagues better than writing slow, complicated, error-prone NSAppleScript code, especially if using Late Night’s Script Debugger as your external code editor (SD includes a bunch of features for ASOC development).

                     

                    If you need any help using ASOC, best place to ask is on the Late Night forums:

                     

                    forum.latenightsw.com