6

グローバルキーダウンイベントを使用することがその操作の要件になるアプリに取り組んでいます。また、これをApp Store経由で厳密に配布する予定です. (これは Mac アプリであり、iOS ではありません。) addGlobalMonitorForEventsMatchingMask を介して動作するグローバル イベントをリッスンする例を取得しましたが、注意が必要です。

注: 私は最新の API を使用することを選択し、以前の Carbon ホットキー メソッドには依存しません。それらが最終的に廃止される場合、後でこの問題を解決する必要はありません。

主な問題は、グローバル イベントを検出するには、アプリが信頼されている必要があることです。それ以外の場合は、すべてのアプリでアクセシビリティを有効にする必要があります。アクセシビリティを有効にすると、イベントが正常に検出されます。この要件は、https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/EventOverview/MonitoringEvents/MonitoringEvents.htmlに記載されています。

ユーザーにとって、アクセシビリティを有効にする必要がないことを望みます。私が行った他の調査から、AXMakeProcessTrusted を呼び出してからアプリケーションを再起動することで、アプリケーションを信頼できるようにすることができます。

私が使用しているコードでは、認証プロンプトが表示されません。アプリは再起動しますが、まだ信頼されていません (認証プロンプトが表示されないためと思われます)。この部分の私のコードは次のとおりです。

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    if (!AXAPIEnabled() && !AXIsProcessTrusted()) {

        NSString *appPath = [[NSBundle mainBundle] bundlePath];
        AXError error = AXMakeProcessTrusted( (CFStringRef)CFBridgingRetain(appPath) );

        [self restartApp];
    }
}

- (void)restartApp{

    NSTask *task = [[NSTask alloc] init];
    NSMutableArray *args = [NSMutableArray array];
    [args addObject:@"-c"];
    [args addObject:[NSString stringWithFormat:@"sleep %d; open \"%@\"", 3, [[NSBundle mainBundle] bundlePath]]];
    [task setLaunchPath:@"/bin/sh"];
    [task setArguments:args];
    [task launch];
    [NSApp terminate:nil];
}

さらに、 https://developer.apple.com/library/archive/documentation/Security/Conceptual/authorization_concepts/03authtasks/authtasks.html#//apple_ref/doc/uid/の認証サービスタスクのドキュメントを見てきましたTP30000995-CH206-BCIGAIAG .

最初に気になるのは、この情報ボックスです。「重要: 認証サービス API は、権限のエスカレーションが許可されているため、アプリ サンドボックス内ではサポートされていません。」

アプリを再起動する前に認証プロンプトを取得するためにこの API が必要な場合、アクセシビリティ機能を有効にしないとグローバル イベントを取得できない可能性があります。

要約すると、私の具体的な質問は次のとおりです。

  1. 認証プロンプトを表示する方法に関するサンプル コードにエラーはありますか?

  2. 認証プロンプトを表示するには、Authorization Services API を使用する必要がありますか?

  3. グローバルイベントにアクセスできるサンドボックス化されたアプリを持つことは可能ですか、それとも不可能ですか?

4

3 に答える 3

5

まず第一に、アプリがサンドボックス環境、つまりアプリ ストアで機能するアクセシビリティ API の使用を自動的に許可する方法はありません。推奨される方法は、ユーザーが自分で簡単に有効にできるようにガイドすることです。新しい API 呼び出しAXIsProcessTrustedWithOptionsはまさにそのためのものです。

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

さて、最初と 2 番目の質問に (完全を期すために - サンドボックスでは機能しません): 背後にあるアイデアは、メイン アプリケーションからルートとして実行AXMakeProcessTrustedする新しい補助アプリケーションを実際に作成することでした。AXMakeProcessTrusted次に、このユーティリティは、メイン アプリケーションの実行可能ファイルの受け渡しを呼び出します。最後に、メイン アプリを再起動する必要があります。API 呼び出しは、OSX 10.9 で非推奨になりました。

新しいプロセスをルートとして生成するにはlaunchdSMJobSubmit. これにより、アプリケーションがヘルパー ツールをインストールしようとしていることと、それを許可する必要があるかどうかを示す認証プロンプトがユーザーに表示されます。具体的には:

    + (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.dbsqlite データベースをハックして、補助ヘルパーを使用して手動でアクセス許可を追加することです。

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

ただし、これにより、アプリがアプリ ストアとしての資格を失うことになります。さらに、実際には単なるハックであり、データベース/スキーマはいつでも変更できます。一部のアプリケーション (例: これを行うために使用される Divvy.app) は、アプリケーション インストーラーのポスト インストール スクリプト内でこのハックを使用しました。これにより、アプリが補助ツールのインストールを要求していることを示すダイアログが表示されなくなります。

于 2014-06-07T18:45:26.053 に答える
0

GitHub で潜在的な解決策を見つけました。

https://github.com/K8TIY/CW-Station

メインアプリケーションへのアクセスを要求するためにルートで実行される補助アプリケーションがあります。少し時代遅れで、廃止された関数を使用しているため、最新化に取り組んでいます。それは良い出発点のように見えます。

于 2013-08-04T03:59:43.637 に答える