【问题标题】:Enable access for assistive devices programmatically on 10.9在 10.9 上以编程方式启用对辅助设备的访问
【发布时间】:2013-07-15 15:06:29
【问题描述】:

我想在 10.9 上以编程方式启用对辅助设备的访问。在 10.8 及更低版本中,我使用以下 Applescript 来启用辅助设备的访问权限:

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

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

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

我们能否在 10.9 上使用 Applescript 或任何其他 API 以编程方式启用对辅助设备的访问?任何解决此问题的帮助将不胜感激。

【问题讨论】:

  • 不,没有办法避免访问此屏幕的需要。它是操作系统的基本保护之一。几乎可以肯定的是,任何可以规避此问题的方法都会被修补。
  • 我认为这是无法规避的非常故意的行为。当您可以访问辅助功能时,您可以从文本框中复制文本,随机单击内容,并且通常会做一些非常粗略的事情。虽然这些东西在某些应用程序中非常有用,但他们不希望它在用户不知情的情况下发生。但这显然是他们的沙盒努力使事情更“安全”的副作用
  • 你可以“接受”它或不接受它,但这就是它的方式。我同意原始评论者的观点,如果您确实找到了规避此问题的方法,它将尽快被淘汰。
  • @KeithSmiley: AXIsTrustedProcess() 确实会在安全和隐私首选项中选中应用程序的复选框后立即开始报告“是”:但在我的测试中,新的事件点击仍然会默默地无法点击 keyup /keydown 事件,直到进程重新启动。 (这与 AXMakeProcessTrusted() 过去的工作方式一致。)
  • @rjobidon 我(终于!)发现您可以将应用程序从应用程序文件夹拖到列表中。 zoul 对此问题的回答中介绍了触发“...想要控制这台计算机”对话框。

标签: objective-c macos cocoa accessibility accessibility-api


【解决方案1】:

这并不能回答您的问题,但很高兴了解 10.9 中出现的一个新 API 调用,它可以让您显示授权屏幕或绕过它:

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

或使用目标 C:

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

这可以与第一个代码sn-p配对,通过将@NO传递给kAXTrustedCheckOptionPrompt来测试accessibilityEnabled是否出现,同时防止系统弹出窗口出现,而是直接打开辅助功能首选项窗格:

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]];
}

【讨论】:

  • 我以为“AXIsProcessTrustedWithOptions”只在 10.9 中引入?因此,使用上面的代码,您是否不必至少以 10.9 为目标才能编译它?还是将其放在针对 10.9 的条件代码块中?
  • 啊....我现在明白了 - 弱链接 ;-) stackoverflow.com/questions/17193066/…
  • @BradParks 你能发布你使用弱链接的最终代码吗?任何人都可以发布正确的方法来让它与 ARC 一起编译吗?注意:要编译,您需要将ApplicationServices.framework 添加到您的项目中,并将#import &lt;ApplicationServices/ApplicationServices.h&gt; 行添加到您的代码中。
  • @StanJames - 我不确定这是否会成功,但我认为您可以通过简单地转到“构建设置”选项卡并设置来修复它“Base SDK”改为“10.9”,但将“General | Deployment Target”改为“10.8”......我在试图帮助一个人让他的项目编译为 10.8 时遇到了这个问题(github.com/sdegutis/mjolnir/issues/156#issuecomment-48133485
  • 只想在此处添加注释。如果您在权利文件中设置 AppSandbox YES。不会显示请求提示。阅读更多:developer.apple.com/forums/thread/24288
【解决方案2】:

您可以直接编辑 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,虽然这不适用于通过 Mac App Store 分发的沙盒应用程序,对吧? (反正很有用,我只是想把事情弄清楚。)另外,你从哪里得知TCC.db
  • 需要重启吗?
  • 自从 sierra 以后再也不可能了
  • 这个数据库已经只读了一段时间,但是如果你暂时禁用了 SIP,你可以通过命令行以这种方式修改它。您的普通 Apple 用户不应让 SIP 禁用超过进行更改并启用 SIP 并再次重新启动所需的时间。如果您在 Anka 中自动化诸如隔离虚拟机构建代理之类的事情并且需要绕过任何人都不会看到的提示,这将非常有用。
【解决方案3】:

sqlite3 “hack”很棒。

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

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

【讨论】:

    【解决方案4】:

    谢谢大家。

    我发出以下从登录窗口触发的命令,以确保仅将控制权授予我们在每个会话中想要的项目:

    # 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)"
    

    【讨论】:

      【解决方案5】:

      虽然@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...
          }
      }
      

      【讨论】:

      • 为了帮助澄清这一点,CREATE TABLE 访问(服务 TEXT NOT NULL,客户端 TEXT NOT NULL,client_type 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,不知道:)
      • 从 Sierra 开始,这不再有效。即使对于 root,数据库文件现在也是只读的。您可以关闭操作系统的“系统完整性保护”(support.apple.com/en-us/HT204899),然后您可以使该文件再次可写,但这并不完全推荐,并且如果您试图让用户通过 App Review 将永远无法通过在 Mac 应用程序中执行此操作。
      • 没有主键,所以INSERT or REPLACE into总是会插入一条新记录。
      【解决方案6】:

      感谢 @NightFlight 提供的这个 shell 脚本示例,它们真的很有帮助。我在 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 编辑器中尝试此操作,请使用稍有不同的字符转义,如下所示:

      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
      

      对于 10.9 之前的 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 也可以在其他语言中使用。

      【讨论】:

      • 我将其复制并粘贴到 AppleScript 编辑器中,但它显示 an unknown token cant go after this identifier 并在 com.apple 中突出显示 com.。能否请您分享 AppleScript 编辑器代码。这也与 10.9 之前的版本兼容吗?
      • 看起来像是字符转义问题。在 AppleScript 编辑器中尝试:将 sh 设置为 "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);\"" 以管理员权限执行shell脚本sh
      • 非常感谢,我会对此进行测试并报告。我将打开 applescript 并进行编辑。这周和下周我真的很忙,所以我可能会再过几个星期。
      【解决方案7】:

      我自己也在为此苦苦挣扎,经过一番研究,我发现了以下问题:

      1. 破解 sqlite 数据库在使用授权服务方面存在主要缺陷。首先,这将弹出一个对话框,告诉用户一个应用程序想要安装一个实用程序助手(即使它只是使用SMJobSubmit 的一次启动提交)。其次,它不适用于沙盒应用,因此没有应用商店。

      2. @Max Al Faeakh 使用已弃用的 AuthorizationExecuteWithPrivileges。您需要将launchd 与上述SMJobSubmit 一起使用。无论如何,这仍然需要授权。它还需要像 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
      

      【讨论】:

        【解决方案8】:

        我发现以下代码 sn-p 在 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
        

        【讨论】:

          【解决方案9】:

          我建议不要使用所有 sqlite3 和 AppleScript hack,因为它们将来可能会停止工作,还有一个合适的 api 来解决这个问题。

          此外,您实际上可以监控用户是否点击了您应用的可访问性设置,以便在用户授予权限时执行一些操作。

          (Swift 5,在 Mojave、Catalina、Big Sur 测试)

          阅读权限

          private func readPrivileges(prompt: Bool) -> Bool {
              let options: NSDictionary = [kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: prompt]
              let status = AXIsProcessTrustedWithOptions(options)
              return status
          }
          

          监控可访问性的变化

          DistributedNotificationCenter.default().addObserver(forName: NSNotification.Name("com.apple.accessibility.api"), object: nil, queue: nil) { _ in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
              self.updatePrivileges()
            }
          }
          

          最好在收到通知后再次阅读权限,因为根据我的经验,通知本身并没有真正起作用。所以在updatePrivileges()里面,运行readPrivileges()来获取新的状态。

          您需要延迟,因为更改需要一些时间才能反映出来。

          在监控时需要记住的另一件事是,任何获得不同权限的应用都会触发通知,因此如果用户授予或撤消不同的应用,您仍然会收到通知。

          另外,当你不再需要观察者时,别忘了移除它。

          编辑:

          来源:Accessbility Testbench by Piddlesoft

          【讨论】:

          • 黄金答案。谢谢。
          猜你喜欢
          • 2012-10-11
          • 2016-11-16
          • 1970-01-01
          • 2012-04-05
          • 2012-02-26
          • 1970-01-01
          • 2016-01-04
          • 2013-10-24
          相关资源
          最近更新 更多