2013-07-17 63 views
34

我想在10.9上以编程方式启用对辅助设备的访问。在10.8和降低我用下面的AppleScript启用辅助设备的访问:以编程方式启用辅助设备访问10.9

tell application "System Events" 
if UI elements enabled is false then 
    set UI elements enabled to true 
end if 
end tell 

10.9,苹果已经移动辅助功能选项系统偏好设置➞安全&隐私➞隐私➞辅助功能。与以前版本的OS X(对所有应用程序使用通用复选框)不同,10.9中的新功能允许用户单独选择哪些应用程序可以控制系统执行其各种脚本功能。

The new system preferences regarding accessibility

苹果尚未提供任何API给开发者以编程方式启用辅助功能的应用程序。因此,当应用程序使用可访问性API时,Mac OS 10.9将提示最终用户权限对话框以启用辅助功能。此外,用户必须在启用辅助功能后重新启动应用程序。

Default prompt dialog put up by 10.9 OS for Xcode

我们能否在使用AppleScript或任何其他API 10.9启用辅助设备的访问编程?任何帮助解决这个问题将不胜感激。

+5

没有,有没有办法规避需要访问这个屏幕。它是操作系统的基本保护之一。任何可以避免这种情况的方式几乎肯定会被修补。 – ChrisCM

+0

@ChrisCM提示用户为应用程序启用辅助功能并重新启动应用程序是不可接受的解决方案。 – Vinpai

+1

我相信这是非常有意的行为,无法绕开。当你有权访问辅助功能时,你可以从文本框中复制文本,随机点击一些东西,一般来说做一些非常简单的事情。虽然这些东西在某些应用程序中非常有用,但它们不希望在用户不知情的情况下发生。但是,这显然是他们的沙盒努力使副作用更加“安全”的一个副作用。 –

回答

34

这不回答你的问题,但它是很好的了解一个新的API调用,出现在10.9和可以显示授权屏幕或绕过它:

NSDictionary *options = @{(id)kAXTrustedCheckOptionPrompt: @YES}; 
BOOL accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options); 

传递YES将强制授权屏幕出现,传递NO将默默跳过它。返回值与AXAPIEnabled()返回的值相同,在10.9中已弃用。为了确保该功能可以在您的系统上,只是把它比作NULL

if (AXIsProcessTrustedWithOptions != NULL) { 
    // 10.9 and later 
} else { 
    // 10.8 and older 
} 

你需要添加ApplicationServices.framework到您的项目,进口您的m或.h文件中:

#import <ApplicationServices/ApplicationServices.h> 

这是相当可惜的是授权屏幕不会让用户直接授权的应用程序,它只是打开系统首选项的右侧部分。其中,顺便说一下,你可以直接做不通过无用的系统对话会:

tell application "System Preferences" 
    set securityPane to pane id "com.apple.preference.security" 
    tell securityPane to reveal anchor "Privacy_Accessibility" 
    activate 
end tell 

或使用Objective C:

NSString *urlString = @"x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"; 
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]]; 

这可以用第一代码片段进行配对,以测试是否accessibilityEnabled通过传递到@NOkAXTrustedCheckOptionPrompt同时防止系统弹出出现,而不是打开辅助工具首直接窗格:

NSDictionary *options = @{(id)kAXTrustedCheckOptionPrompt: @NO}; 
BOOL accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options); 
if (!accessibilityEnabled) { 
    NSString *urlString = @"x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"; 
    [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]]; 
} 
+0

我认为“AXIsProcessTrustedWithOptions”仅在10.9中引入?因此,通过上面的代码,你不需要至少将目标定位到10.9以便编译它?或者让它在一个条件代码块中以10.9为目标? –

+0

啊......我现在看到 - 弱链接;-) http://stackoverflow.com/questions/17193066/cocoa-check-if-function-exists/17205070#17205070 –

+0

@BradParks你可以发布最终的代码你用于弱链接?任何人都可以发布正确的方式来获得这与ARC编译?注意:为了编译,您需要将'ApplicationServices.framework'添加到您的项目中,并向您的代码添加一个'#import '行。 –

8

您可以直接编辑TCC.db文件。我必须这样做才能在没有用户交互的情况下进行Divvy安装。只需将com.mizage.divvy替换为您的程序即可。

sudo sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db "INSERT INTO access VALUES('kTCCServiceAccessibility','com.mizage.divvy',0,1,1,NULL);" 

要删除的条目:

sudo sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db "delete from access where client='com.mizage.divvy';" 
+1

+1,但这对于通过Mac App Store分发的沙盒应用程序无效,对吧? (无论如何,这很有用,我只是想弄明白。)另外,你从哪里了解到'TCC.db'? – zoul

+0

是否需要重启? – jamespick

+0

nope,不需要重新启动 –

0

的sqlite3的 “黑客” 是伟大的。

我不得不使用权限“1,1,1”(无论这意味着什么)来使这项工作。

请注意,权限组合,而不是客户端(即程序名称)是唯一的数据库密钥。

0

谢谢大家。

我发出以下的登录窗口触发,以确保控制只给予我们希望每个会话的项目:

# Enable Service Accessibility for Textpander and others 
# Clear the acess table. 
sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db "DELETE FROM access" 

# Enter the access we wish to have. 
sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db "INSERT INTO access VALUES ('kTCCServiceAccessibility','com.apple.systempreferences',0,1,1,NULL)" 
sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db "INSERT INTO access VALUES ('kTCCServiceAccessibility','de.petermaurer.textpanderdaemon',0,1,1,NULL)" 
9

虽然@ user2865860的回答效果很好,不过,我觉得我会发布整个代码样本在10.9上完美工作以节省其他人一些时间。你需要获得root权限,所以它会提示用户输入密码。

char *command= "/usr/bin/sqlite3"; 
char *args[] = {"/Library/Application Support/com.apple.TCC/TCC.db", "INSERT or REPLACE INTO access VALUES('kTCCServiceAccessibility','com.yourapp',0,1,0,NULL);", nil}; 
AuthorizationRef authRef; 
OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authRef); 
if (status == errAuthorizationSuccess) { 
    status = AuthorizationExecuteWithPrivileges(authRef, command, kAuthorizationFlagDefaults, args, NULL); 
    AuthorizationFree(authRef, kAuthorizationFlagDestroyRights); 
    if(status != 0){ 
     //handle errors... 
    } 
} 
+2

为了帮助澄清这一点,_CREATE TABLE access(服务TEXT NOT NULL,客户端TEXT NOT NULL,客户端类型INTEGER NOT NULL,允许INTEGER NOT NULL,prompt_count INTEGER NOT NULL,csreq BLOB,CONSTRAINT key PRIMARY KEY(service,client,client_type)); _第一个0表示“client_type”,如果引用二进制文件,则为1,如果引用包名称,则为0。下一个是启用的,对我们来说应该是1。最后是prompt_count,不知道:) –

+2

截至塞拉利昂,这不再起作用。数据库文件现在即使对于root也是只读的。您可以关闭操作系统的“系统完整性保护”(https://support.apple.com/en-us/HT204899),然后您可以再次将该文件设为可写,但这并不是完全推荐的,并且永远不会如果您试图让用户在Mac App中执行此操作,它就会通过App Review。 –

1

感谢这个shell脚本样本@NightFlight,这真的很有帮助。我在Python应用程序中使用了AppleScript,如下所示:

set sh to "touch /private/var/db/.AccessibilityAPIEnabled && sqlite3 \\"/Library/Application Support/com.apple.TCC/TCC.db\\" \\"INSERT or REPLACE INTO access VALUES('kTCCServiceAccessibility','com.godevnode',0,1,0,NULL);\\"" 
do shell script sh with administrator privileges 

它在Python代码中作为字符串很好地工作。

编辑(2014年11月7日):

如果你想尝试这种在AppleScript的编辑器,使用略有不同的字符逃脱如下:10.9前

set sh to "touch /private/var/db/.AccessibilityAPIEnabled && sqlite3 \"/Library/Application Support/com.apple.TCC/TCC.db\" \"INSERT or REPLACE INTO access VALUES('kTCCServiceAccessibility','com.godevnode',0,1,0,NULL);\"" 
do shell script sh with administrator privileges 

对于Mac OS X,它是甚至更简单:

accessibility_api_file = "/private/var/db/.AccessibilityAPIEnabled" 

def __enable_accessibility_api(): 
    try: 
     script = 'do shell script "touch %s" with administrator ' \ 
       'privileges' % accessibility_api_file 
     result = applescript.AppleScript(script).run() 
     log.debug("Tried to enable accessibility api, result=" + result) 
     return True 
    except applescript.ScriptError as err: 
     log.error(str(err)) 
    return False 

只需触摸一个文件。上面的Python代码中提到的AppleScript也可以用于其他语言。

+0

我复制并将其粘贴到AppleScript编辑器中,但它说'一个未知的令牌不能追踪此标识符',并且它强调'com.apple'中的com.'。你可以请分享AppleScript编辑器代码。这是否与10.9之前兼容? – Noitidart

+1

它看起来像字符逃生问题。在AppleScript编辑器中尝试以下操作:将sh设置为“touch /private/var/db/.AccessibilityAPIEnabled && sqlite3 \”/ Library/Application Support/com.apple.TCC/TCC.db \“\”INSERT或REPLACE INTO访问VALUES 'kTCCServiceAccessibility','com.godevnode',0,1,0,NULL); \“” 以管理员权限执行shell脚本sh –

+0

非常感谢的人,我会测试并报告回来。我会打开applescript并编辑。我本周和下周真的很忙,所以我可能需要几个星期。 – Noitidart

2

我用这个自己挣扎着有点研究后,我发现:

  1. 黑客sqlite的DB在使用授权服务的主要缺点。首先,这将弹出一个对话框,告诉用户应用程序想要安装实用程序帮助程序(即使它仅使用SMJobSubmit的启动提交一次)。其次,它不适用于沙盒应用程序,因此不适用于应用程序商店。

  2. @Max Al Faeakh使用AuthorizationExecuteWithPrivileges已弃用。您需要使用以上SMJobSubmit的launchd。无论如何,这仍然需要授权。它还需要一个像这样的辅助应用程序one

我想最好的方法是使用两种:

NSDictionary *options = @{(id)kAXTrustedCheckOptionPrompt: @YES}; 
BOOL accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options); 

NSDictionary *options = @{(id)kAXTrustedCheckOptionPrompt: @NO}; 
BOOL accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options); 

和开放的偏好设置面板手动例如使用脚本桥框架:

SBSystemPreferencesApplication *prefs = [SBApplication applicationWithBundleIdentifier:@"com.apple.systempreferences"]; 
[prefs activate]; 

SBSystemPreferencesPane *pane = [[prefs panes] find:^BOOL(SBSystemPreferencesPane *elem) { 
    return [[elem id] isEqualToString:@"com.apple.preference.security"]; 
}]; 
SBSystemPreferencesAnchor *anchor = [[pane anchors] find:^BOOL(SBSystemPreferencesAnchor *elem) { 
    return [[elem name] isEqualToString:@"Privacy_Accessibility"]; 
}]; 

[anchor reveal]; 

SBSystemPreferencesPane类来形成能够产生SBSystemPreferences.h文件:

sdef "/Applications/System Preferences.app" | sdp -fh --basename SBSystemPreferences -o SBSystemPreferences.h 
6

我发现下面的代码片段,其正常请求在OS X 10.9辅助功能的权限:

if (AXIsProcessTrustedWithOptions != NULL) { 
    // 10.9 and later 
    const void * keys[] = { kAXTrustedCheckOptionPrompt }; 
    const void * values[] = { kCFBooleanTrue }; 

    CFDictionaryRef options = CFDictionaryCreate(
      kCFAllocatorDefault, 
      keys, 
      values, 
      sizeof(keys)/sizeof(*keys), 
      &kCFCopyStringDictionaryKeyCallBacks, 
      &kCFTypeDictionaryValueCallBacks); 

    return AXIsProcessTrustedWithOptions(options); 
} 

// OS X 10.8 and older 
相关问题