首先,您无法自动允许应用程序使用可在沙盒环境中运行的可访问性 API,从而在应用程序商店中运行。推荐的方法是简单地引导用户,以便他们可以轻松地自己启用它。新的 API 调用AXIsProcessTrustedWithOptions
正是为此:
NSDictionary *options = @{(id) kAXTrustedCheckOptionPrompt : @YES};
AXIsProcessTrustedWithOptions((CFDictionaryRef) options);
现在,对于您的第一个和第二个问题(只是为了完整起见 - 它在沙盒中也不起作用):背后的想法AXMakeProcessTrusted
是您实际上创建了一个新的辅助应用程序,您从主应用程序以 root 身份运行。然后,此实用程序调用AXMakeProcessTrusted
传入主应用程序的可执行文件。最后,您必须重新启动主应用程序。API 调用在 OSX 10.9 中已被弃用。
要以 root 身份生成新进程,您必须使用launchd
using SMJobSubmit
。这将通过身份验证提示提示用户,说明应用程序正在尝试安装帮助工具以及是否应允许。具体来说:
+ (BOOL)makeTrustedWithError:(NSError **)error {
NSString *label = FMTStr(@"%@.%@", kShiftItAppBundleId, @"mktrusted");
NSString *command = [[NSBundle mainBundle] pathForAuxiliaryExecutable:@"mktrusted"];
AuthorizationItem authItem = {kSMRightModifySystemDaemons, 0, NULL, 0};
AuthorizationRights authRights = {1, &authItem};
AuthorizationFlags flags = kAuthorizationFlagInteractionAllowed | kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights;
AuthorizationRef auth;
if (AuthorizationCreate(&authRights, kAuthorizationEmptyEnvironment, flags, &auth) == errAuthorizationSuccess) {
// this is actually important - if from any reason the job was not removed, it won't relaunch
// to check for the running jobs use: sudo launchctl list
// the sudo is important since this job runs under root
SMJobRemove(kSMDomainSystemLaunchd, (CFStringRef) label, auth, false, NULL);
// this is actually the launchd plist for a new process
// https://developer.apple.com/library/mac/documentation/Darwin/Reference/Manpages/man5/launchd.plist.5.html#//apple_ref/doc/man/5/launchd.plist
NSDictionary *plist = @{
@"Label" : label,
@"RunAtLoad" : @YES,
@"ProgramArguments" : @[command],
@"Debug" : @YES
};
BOOL ret;
if (SMJobSubmit(kSMDomainSystemLaunchd, (CFDictionaryRef) plist, auth, (CFErrorRef *) error)) {
FMTLogDebug(@"Executed %@", command);
ret = YES;
} else {
FMTLogError(@"Failed to execute %@ as priviledged process: %@", command, *error);
ret = NO;
}
// From whatever reason this did not work very well
// seems like it removed the job before it was executed
// SMJobRemove(kSMDomainSystemLaunchd, (CFStringRef) label, auth, false, NULL);
AuthorizationFree(auth, 0);
return ret;
} else {
FMTLogError(@"Unable to create authorization object");
return NO;
}
}
至于重新启动,这通常也使用外部实用程序来完成,等待主应用程序完成并再次启动它(通过使用 PID)。如果您使用sparkle 框架,您可以重用现有的框架:
+ (void) relaunch {
NSString *relaunch = [[NSBundle bundleForClass:[SUUpdater class]] pathForResource:@"relaunch" ofType:@""];
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *pid = FMTStr(@"%d", [[NSProcessInfo processInfo] processIdentifier]);
[NSTask launchedTaskWithLaunchPath:relaunch arguments:@[path, pid]];
[NSApp terminate:self];
}
另一种选择是破解/Library/Application Support/com.apple.TCC/TCC.db
sqlite 数据库,使用辅助助手手动添加权限:
NSString *sqlite = @"/usr/bin/sqlite3";
NSString *sql = FMTStr(@"INSERT or REPLACE INTO access values ('kTCCServiceAccessibility', '%@', 1, 1, 1, NULL);", MY_BUNDLE_ID);
NSArray *args = @[@"/Library/Application Support/com.apple.TCC/TCC.db", sql];
NSTask *task = [NSTask launchedTaskWithLaunchPath:sqlite arguments:args];
[task waitUntilExit];
但是,这将取消该应用程序成为应用程序商店的资格。更重要的是,它实际上只是一个 hack,并且 db / schema 可以随时更改。一些应用程序(例如,用于执行此操作的 Divvy.app)在应用程序安装程序安装后脚本中使用了此 hack。这样可以防止对话框告诉应用程序正在请求安装辅助工具。