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

问题描述 投票:5回答:1

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

对于冷启动应用程序,使用

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

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

我一直在努力解决这个问题,因为我有一些要求,使事情变得有些困难。

要求

  1. 发送到应用程序的文件路径必须使用沙箱权限打开/保存
  2. 参数和文件路径必须同时被应用程序拦截。

潜在的解决方案

XPC

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

  • 我必须创建始终运行的Launch Agent应用程序伴侣,以便它可以检测命令行操作并将其传递给我的应用程序吗?
  • 由于每个进程都有自己的权限,此方法如何与沙盒一起使用?

Apple脚本

  • 我是否应该使用Apple脚本告诉我的应用使用参数打开这些文件,从而避开沙箱功能?
  • 通过AppleScript打开文件时,我可以保存这些文件吗?

URL方案

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

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

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

macos cocoa applescript appkit xpc
1个回答
9
投票

一个命令行工具可以提取其参数并将其转换为Apple Events。通过安装BBEdit命令行工具,然后在“终端”窗口中运行man bbeditman bbdiff,您可以从用户的角度查看其工作方式。

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

  1. 找出应用程序是否正在运行:+[NSRunningApplication runningApplicationsWithBundleIdentifier:]将对此有所帮助。

  2. 如果应用程序正在运行[[not,则使用-[NSWorkspaceURLForApplicationWithBundleIdentifier:]首先通过捆绑软件ID查找应用程序,然后使用-[NSWorkspace launchApplicationAtURL:options:configuration:error:]启动应用程序。这将返回一个NSRunningApplication实例,或者NIL和一个错误。 (确保处理错误情况。)

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

那会是这样:

    从正在运行的应用程序中使用processIdentifier构造目标描述符:
  • targetDesc = [NSAppleEventDescriptor descriptorWithProcessIdentifier: myRunningApplication.processIdentifier;
      构造一个“打开文档”事件,发送给目标应用程序:
  • event = [NSAppleEventDescriptor appleEventWithEventClass: kCoreEventClass eventID: kAEOpenDocuments targetDescriptor: targetDesc returnID: kAutoGenerateReturnID transactionID: kAnyTransactionID];
    注意:我以kCoreEventClass / kAEOpenDocuments为例-如果您要尝试打开一个或多个带有其他信息的文件,那很好。如果您正在做其他工作,则应该为应用程序特定的事件类创建一个四个字符的代码,并为您所请求的操作创建一个四个字符的事件ID。]

      将命令参数添加到事件中。对于每个参数,这包括根据参数的内在类型(布尔,整数,字符串,文件URL)创建适当的描述符,然后使用关键字参数将其添加到事件中。
  • (Apple Event的“关键字”是一个四个字符的代码。您可以发明自己的带有约束的代码(不要使用全小写形式,可以使用在AEDataModel.hAERegistry.h中定义的合适的代码)满足您的需求)。

    对于您创建的每个描述符,请使用-[setParamDescriptor: forKeyword:]将其添加到事件中:

    myURLParamDesc = [NSAppleEventDescriptor descriptorWithFileURL: myFileURL]; [event setParamDescriptor: myURLParamDesc forKey: kMyFileParamKeyword];

      将所有参数添加到事件后,发送它:
  • [event sendWithOptions: kAENoReply timeout: FLOAT_MAX error: &error];
    在应用程序端,您需要使用-[NSAppleEventManager setEventHandler: andSelector: forEventClass: andID:]。这将针对您在上面发明的自定义事件类和ID进行调用,此时您可以使用描述符API将事件拆开并运行您的操作。

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

    您的命令行工具是

    not

    沙盒-不能,因为它是从Terminal和(可能)其他非沙盒应用程序运行的。但是,该工具必须使用强化的运行时进行签名,并使用com.apple.security.automation.apple-events = YEScom.apple.security.temporary-exception.apple-events命名应用程序的捆绑包标识符,以便该工具可以将Apple Events发送到您的应用程序。

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

    我为读者做了大量练习;但希望这会帮助您入门。

  • © www.soinside.com 2019 - 2024. All rights reserved.