Send keystrokes to frontmost app from sandboxed co

2019-03-25 00:22发布

问题:

I need to send keystrokes to front most app from my cooca app.

I already have working code for it by using CGEventCreateKeyboardEvent() and AXUIElementPostKeyboardEvent(), but it only works if app is not sandboxed.

I have searched google for the same, but didn't find any working solution.

I saw that a Text app and few others doing the same thing in sandboxed environment, so i am wondering, if someone help me to figure out, that how aText.app and others are able to send keystrokes in sandbox environment.

Thanks,

回答1:

This is actually possible. I have made an example app available here - SendKey at GitHub

I took the easy road and started with a simple AppleScript:

delay 5

tell application "System Events"
    repeat 10 times
        keystroke "#"
    end repeat
end tell

The 'delay' in the script simply gives me enough time to make a text editor the frontmost application. I would suggest starting with just running this script to see what it does.

Then, I created an Xcode project using the default Application template and wrote:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    NSString*       scriptPath  = [[NSBundle mainBundle] pathForResource:@"sendkey" ofType:@"scpt"];
    NSURL*          scriptURL   = [NSURL fileURLWithPath:scriptPath];
    NSDictionary*   errors;
    NSAppleScript*  script      = [[NSAppleScript alloc] initWithContentsOfURL:scriptURL error:&errors];

    NSLog( @"%@", errors );

    [script executeAndReturnError:&errors];

    NSLog( @"%@", errors );
}

I tested this without turning on sandboxing to verified it works and it did. Then I turned on Sandboxing and, of course, it broke. But, fortunately, there is a way around that. For now, Apple is providing a temporary entitlement called com.apple.security.temporary-exception.apple-events. And, you can request the exception be granted for 'com.apple.systemevents'. This is what my entitlements file looks like:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
   "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>com.apple.security.temporary-exception.apple-events</key>
    <array>
      <string>com.apple.systemevents</string>
    </array>
    <key>com.apple.security.app-sandbox</key>
    <true />
  </dict>
</plist>

Once I added this entitlement to my sandboxed app and signed it, it worked as expected again.

Now, if you want to send other keys, this question & answer will demonstrate how to build your script on the fly - Passing variables to an applescript.

Of course, once you have all of these working, you can probably turn to NSAppleEventDescriptor and related classes to build the event in code, but I haven't played with that technique.

Please note that Apple does suggest you do the following when using a temporary entitlement:

If you choose not to sandbox your app now or to use a temporary exception entitlement, use Apple’s bug reporting system to let Apple know about the issue you are encountering. Apple considers feature requests as it develops the OS X platform. Also, be sure use the Review Notes field in iTunes Connect to explain why the exception is needed.