通过命令行将文件 URL 和参数发送到 (运行) macOS 应用程序

Send file URL and args to (running) macOS app via command line

我一直在尝试创建一种方法来告诉我的 (运行) macOS 应用程序打开一些文件并为命令提供一些额外的参数。

对于冷启动应用,使用

$ open MyApp.app fileA.txt --args --foo-arg

将启动该应用程序,我将能够通过 UserDefaults/CommandLine/ProcessInfo 检查 --foo-arg。但是,如果应用程序已经是 运行,--foo-argUserDefaults/ProcessInfo‌/CommandLine.

中丢失

我一直在努力寻找解决方案,因为我有一些要求让事情变得有点困难。

要求

  1. 发送到应用程序的文件路径必须opened/saved具有沙盒权限
  2. 参数和文件路径必须同时被app拦截。

可能的解决方案

XPC

有些人建议我使用 XPC,但在阅读之后,我不确定该解决方案的外观如何?

Apple 脚本

URL 方案

我可以注册我的应用程序以拥有自己的 URL 方案,但 NSApplicationDelegate 处理传入 URLs 的方式分为两批。首先,它可以打开的URL,其次是URL方案或它无法打开的文件路径。即:

open -a MyApp.app myapp:foo; open -a MyApp.app file.txt

我可能可以完成这项工作,但它有点俗气,我真的想以正确的方式做到这一点。

一个命令行工具可以提取它的参数并将它们转换为 Apple Events 是可行的方法。通过安装 BBEdit 命令行工具,然后在终端 window.[=] 中 运行ning man bbeditman bbdiff,您可以从用户的角度了解这是如何工作的32=]

从命令行工具的角度来看,“有趣”的部分是:

  1. 确定应用程序是否 运行ning:+[NSRunningApplication runningApplicationsWithBundleIdentifier:] 将对此有所帮助。

  2. 如果应用是不是运行ning,那么使用-[NSWorkspaceURLForApplicationWithBundleIdentifier:]先通过bundle ID定位应用,然后-[NSWorkspace launchApplicationAtURL:options:configuration:error:] 启动应用程序。这将 return 一个 NSRunningApplication 实例,或者 NIL 和一个错误。 (确保处理错误情况。)

  3. 使用从步骤 1 或步骤 2 获得的 NSRunningApplication 实例,您现在可以使用 NSAppleEventDescriptor APIs 或低级 AppleEvent C APIs 构造一个事件。 (更高级别的 API 可能更容易使用。)

那会是这样的:

  1. 使用 运行ning 应用程序中的 processIdentifier 构建目标描述符:

    targetDesc = [NSAppleEventDescriptor descriptorWithProcessIdentifier: myRunningApplication.processIdentifier;
    
  2. 构造一个“打开文档”事件,发送给您的目标应用程序:

    event = [NSAppleEventDescriptor appleEventWithEventClass: kCoreEventClass eventID: kAEOpenDocuments targetDescriptor: targetDesc returnID: kAutoGenerateReturnID transactionID: kAnyTransactionID];
    

    注意:我使用 kCoreEventClass/kAEOpenDocuments 作为示例 - 如果您尝试打开一个或多个包含附加信息的文件,那很好。如果您正在做一些其他工作,那么您应该为特定于您的应用程序的事件 class 发明一个四字符代码,以及一个对于您请求的操作唯一的四字符事件 ID .

  3. 将命令参数添加到事件中。对于每个参数,这包括根据参数的内部类型(布尔值、整数、字符串、文件 URL)创建适当的描述符,然后使用关键字参数将其添加到事件中。

    (Apple Event“关键字”是一个四字符代码。您可以发明自己的代码,但有限制(不要使用全小写字母,您可以使用 AEDataModel.hAERegistry.h 满足您需求的地方)。

    对于您创建的每个描述符,使用 -[setParamDescriptor: forKeyword:]:

    将其添加到事件中
    myURLParamDesc = [NSAppleEventDescriptor descriptorWithFileURL: myFileURL];
    [event setParamDescriptor: myURLParamDesc forKey: kMyFileParamKeyword];
    
  4. 将所有参数添加到事件后,发送它:

    [event sendWithOptions: kAENoReply timeout: FLOAT_MAX error: &error];
    

在应用程序方面,您需要使用 -[NSAppleEventManager setEventHandler: andSelector: forEventClass: andID:]。这将为您的自定义事件 class 和您在上面发明的 ID 调用,此时您可以使用描述符 APIs 将事件分开并 运行 您的操作。

沙盒会自行处理:您的应用程序会自动为通过 Apple Events 传递的文件获取沙盒扩展。

您的命令行工具沙盒化 -- 不可能,因为它 运行 来自终端和(可能)其他非沙盒化应用程序。

但是,该工具必须使用强化的 运行时间、com.apple.security.automation.apple-events = YEScom.apple.security.temporary-exception.apple-events 命名您的应用程序的包标识符进行签名,以便该工具可以发送 Apple 事件到您的应用程序。

(并且该工具将需要带有 NSAppleEventsUsageDescription 字符串的 Info.plist。)

我留了相当多的钱作为 reader 的练习;但希望这能让你入门。