web-dev-qa-db-ja.com

NSWorkspaceのrunningApplicationsはどの程度信頼できますか?

時間の経過後に一部のアプリケーションの実行を終了したい。 NSWorkspaceのrunningApplicationsを監視して、監視するものがないことをポーリングしています(アプリケーションが実行されているだけの場合、何か通知されますか?)

私の問題は、アプリケーションが時々終了するだけであり、時々(内部タイマーにより)アプリケーションを閉じる必要がある時間から数秒かかり、時々まったく終了しないことです!

TerminateメソッドとforceTerminateメソッドの両方を使用してみました。

コードスニペットのapps _は、アプリケーション名を含む文字列のベクトルです。それは別のスレッドによって定期的に更新され、そのデータは以下のコードを実行する前に受信されます。それらはすべてes_handler_block_t内で実行されます

_NSArray<NSRunningApplication *> *running_apps = [NSWorkspace sharedWorkspace].runningApplications;
for (const auto &app_ : apps_) {
    //std::cout << app_ << "\n";
    for (NSRunningApplication *app in running_apps) {
        if ([[NSString stringWithUTF8String:app_.c_str()] isEqualToString:[app.executableURL lastPathComponent]] ) {
            std::string app_name = [[app.executableURL absoluteString] UTF8String];
            std::cout << "Terminating app " << app_name << "\n";
            bool res_f = [app forceTerminate];
            bool res_t = [app terminate];
            LOG_DBG("Force terminate: %d", res_f);
            LOG_DBG("Terminate: %d", res_t);
            break;
        }
    }
}
_

このプロパティは、メインの実行ループが共通モードで実行されている場合にのみ変更されます」と、runningApplicationsのドキュメントを読みました。どういう意味ですか?

上記のコードに(ifチェックの前に)ブレークポイントを挿入し、実行を再開すると、実行中のアプリケーションが即座に強制終了されるため、runningApplicationのポーリングに関連していると思います。

メイン機能をブロックしていません。私はフレームワークのEndpoint Securityクラスしか持っておらず、ネットワーキングは他のいくつかのスレッドで行われ、returnNSApplicationMain(argc, argv);で実行しています

問題は何でしょうか?ありがとう。

編集:私はCocoaフレームワークを利用して、システムトレイにのみ表示され、root権限を持つエージェントを作成しています。 Endpoint Security Frameworkを使用すると、アプリの起動を防ぐことができますが、毎回動作する既に実行中のアプリケーションを強制終了する確実な方法は見つかりませんでした。

後の編集:_[[NSWorkspace sharedWorkspace] notificationCenter]_にオブザーバーを追加することができましたが、実行中のアプリケーションに対してどの通知をサブスクライブする必要がありますか?非表示にしてみましたが、ユーザーが赤いウィンドウボタンをクリックしただけでは機能せず、ドックからアプリを非表示にした場合にのみ機能します。しかし、ユーザーと実行中のアプリの間に相互作用がない場合でも、私はそれを閉じたいと思っています。

4
Razvan Axinie

MacOSでのアプリの確実な終了は、次のような多くの要因に依存します。

  1. 資格
  2. アプリの起動方法
  3. メインアプリがクラッシュするか、別のプロセスによって終了するとすぐに、メインアプリを存続させるバックグラウンドアプリがある場合

たとえば、アプリは1つのアプリを終了することを許可されている場合がありますが、たとえば、次のようなplistを使用してlaunchdコマンドでKeepAliveによって起動されたプロセスを強制終了するとします。

<?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>KeepAlive</key>
    <true/>
    <key>Label</key>
    <string>com.bundleidentifierOf.AnUnquitabbleApp</string>
    <key>ProgramArguments</key>
    <array>
        <string>/path/to/the/AnUnquitableApp</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

すると、launchdがすぐに再起動し、アプリが強制終了されなかったかのように見えます。アプリを強制終了しようとするたびにPIDが変化すると、アプリが再起動されていることがわかります。

次のコマンドを使用して確認できます。

ps -ax

ターミナルで、Macで実行されているすべてのプロセスを一覧表示します。アプリケーションが実行されているかどうかを知りたい場合は、次のコマンドを使用します。

ps -ax | grep "AnUnquitableAppNameHere"

そして、あなたはそのPIDを使ってそれを殺すことができます

kill <PID>

これは、単純なQUITシグナルでどのアプリを強制終了できるかを判断するのに役立ちます。これらのアプリは、強力な信号を使用して終了する必要があり、強制終了できないアプリです。それらを強制終了するには、アプリにない権限または資格が必要になるためです。ターゲットアプリの詳細については、psおよびkillのmanページをご覧ください。

MacOSで永続性を取得する方法は他にもたくさんあります。アプリを確実に終了する方法を理解したい場合は、アプリが永続性を取得した方法を最初に理解する必要があります。

たとえば、ユーザーが管理者/スーパーユーザー権限を持っている場合は、/Library/LaunchDaemonsにデーモンをインストールすることで永続化できます。アプリにルート権限がない場合、そのプロセスを強制終了することはできません。

アプリがlaunchd plist以外に永続性を維持するもう1つの方法は、たとえば、メインアプリが強制的に終了するとすぐにメインアプリを再起動するバックグラウンドアプリを用意することです。例として、killを使用してMicrosoft Wordの最新バージョンを強制終了すると、Wordが強制終了されたと不平を言うヘルパーによって再起動されることがわかります。

アプリを強制終了する最も良い方法は、実際にはアプリにQuit Appleイベントを送信することです。

私はあなたを落胆させたくはありませんが、あなたが達成しようとしていることは、そもそもそれが思われるよりも難しいです。また、成功した場合でも、ユーザーがアプリを強制終了する可能性があり、launchd plistまたは別の形式の永続性によって維持されない限り、達成しようとしていることを無効にすることに注意してください。

ポーリングなしで強制終了または起動されたアプリを確認したい場合は、 TN205 をお読みになることをお勧めします。

次に、特定の質問に回答します。

「このプロパティは、メインの実行ループが共通モードで実行されるときにのみ変更されます」

つまり、NSRunningApplicationを更新するには実行ループが必要です。 GUIアプリがある場合、そのようなループはすでにインストールされています。アプリがCLIの場合、これはインストールされていないため、このような実行中のループを自分でインストールする必要があります。 Objective-Cを使用している場合は、この question で推奨されているように実行できます。

したがって、達成しようとしていることには多くの注意事項があり、続行する前にそれらに注意する必要があります。

2
jvarela