Send keystrokes to frontmost app from sandboxed cocoa app
Asked Answered
S

1

13

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,

Sydney answered 15/4, 2013 at 14:14 Comment(2)
Could you provide enough information on which apps do this and when for me to recreate it? I've never seen it happen from a sandboxed app.Babul
Please check aText.app, just write aText in Google and check the very first link. It seems their site version is not sand boxed, but app store version has and working in sandbox environment without using any temporary exception. please let me know if i am wrong.ThanksSydney
F
5

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.

Fur answered 22/4, 2013 at 23:44 Comment(7)
Hi, thanks for your efforts. But i have something which i am not getting. As you explain about apple script which is great thanks, but the real question is about doing that without apple script or temporary exception.Sydney
I have checked the aText.app entitlements via codesign tool and didn't found any temporary exception, while when i add temporary exception in my test app, i can see that in the entitlements via codesign, it seems aText.app is not using temporary exception.Sydney
There might be something very simple, but it didn't hit me yet. I am still trying to find a elegant way to do that. And will post if found the solution, as it seems to be quite a popular demand. :) thanksSydney
Can you provide a website for the app you are seeing the behavior with? I am not familiar with aText.app.Fur
Do you have an example of a free app that does this? Also, what entitlements does the app have? Unless something unusual is going on, one of those entitlements is allowing it to do something outside of the sandbox restrictions - and sending events to other processes it certainly a normal restriction of the sandbox. So, understanding the entitlements of the atext.app might be the clue that is needed.Fur
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.application-identifier</key> <string>KHEMQ2FD9E.com.trankynam.aText</string> <key>com.apple.developer.ubiquity-container-identifiers</key> <array> <string>KHEMQ2FD9E.com.trankynam.aText</string> </array> <key>com.apple.security.app-sandbox</key> <true/> <key>com.apple.security.files.user-selected.read-write</key> <true/> </dict> </plist>Sydney
Did anyone actually get an app using this technique approved in the App Store? I've tried and my app was rejected because it's not allowed to talk to "System Events" via AppleEvents.Mockheroic

© 2022 - 2024 — McMap. All rights reserved.