NSWorkspace 的 runningApplications 有多可靠?

How reliable is NSWorkspace's runningApplications?

我想在一段时间后终止某些应用程序的执行。我正在轮询 NSWorkspace 的 运行ningApplications 是否缺少可观察的内容(如果应用程序只是 运行ning,它会通知任何事情吗?)

我的问题是应用程序只是有时会终止,有时它们会在应该关闭的时间过后几秒钟(根据内部计时器),有时它们根本不会终止!

我尝试同时使用终止和强制终止方法。

在代码片段中,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;
        }
    }
}

我在 运行ningApplications 文档中读到“这个 属性 只会在主 运行 循环 运行 处于公共模式时发生变化”。这是什么意思?

我想这与 运行ningApplication 的轮询有关,因为在上面的代码中插入一个断点(在 if 检查之前)然后立即恢复执行会杀死应用程序,否则该应用程序仍然 运行.

我没有屏蔽主要功能。我的框架只有一个端点安全 class,网络是在其他线程上完成的,我 returnNSApplicationMain(argc, argv);

可能是什么问题?谢谢

编辑:我正在利用 Cocoa 框架创建一个仅显示在系统托盘中且具有根权限的代理。使用 Endpoint Security Framework 成功阻止应用程序启动,但我没有找到一种可靠的方法来杀死已经 运行ning 每次都有效的应用程序。

后期编辑:我设法将观察者添加到 [[NSWorkspace sharedWorkspace] notificationCenter],但是我应该为 运行ning 的应用程序订阅什么通知?我尝试使用 hide,但如果用户仅单击红色 window 按钮,它就不起作用,只有当他从 dock 中隐藏该应用程序时。但即使用户与 运行ning 应用程序之间没有交互,我仍然想关闭它。

在 macOS 上可靠地终止应用程序取决于很多因素,例如:

  1. 权利
  2. 应用程序是如何启动的
  3. 如果有任何后台应用程序在主应用程序崩溃或被另一个进程退出时保持活动状态

例如,您的应用程序可能被允许终止一个应用程序,但是,例如,如果您使用这样的 plist 使用 KeepAlive 命令终止由 launchd 启动的进程:

<?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 信号终止,哪些应用程序需要使用更强的信号来终止它们,哪些是不可终止的,因为终止它们需要您的应用程序的特权或授权可能没有。阅读 pskill 的手册页以了解有关您的目标应用程序的更多信息。

在 macOS 上还有许多其他方法可以获得持久性,如果您想了解如何可靠地退出应用程序,您首先需要了解它们是如何获得持久性的。

例如,如果用户具有管理员/超级用户权限,则可以通过在/Library/LaunchDaemons中安装守护进程来获得持久性。如果您的应用没有 root 权限,则您不太可能终止该进程。

除了 launchd plist 之外,应用程序保持持久性的另一种方法是,例如,有一个后台应用程序,一旦主应用程序被迫退出,它就会重新启动主应用程序。一个例子是,如果你使用 kill 杀死最新版本的 Microsoft Word 你会看到它会被这样一个助手重新启动,它会抱怨 Word 被强制退出。

关闭应用程序的最好方法实际上是向它发送退出 Apple 事件,这就是我通常做的。

我不想让你气馁,但你想要实现的目标比一开始看起来要困难得多。即使您成功了,请记住,用户可能会杀死您的应用程序,这将打败您想要实现的目标,除非它通过启动的 plist 或其他形式的持久性保持活力。

现在如果你想观察哪些应用程序在没有轮询的情况下被杀死或启动,我绝对推荐你阅读TN2050

现在回复您的具体问题:

"this property will only change when the main run loop runs in a common mode"

这意味着您需要有一个 运行 循环才能更新 NSRunningApplication。如果您有 GUI 应用程序,那么已经为您安装了这样的循环。如果您的应用程序是 CLI,则不会安装它,您需要自己安装这样一个 运行 循环。如果您使用的是 Objective-C,那么您可以按照 question.

中的建议进行操作

因此,您要实现的目标有很多注意事项,您需要在继续之前了解它们。