Отправить URL-адрес файла и аргументы в (работающее) приложение macOS через командную строку

Я пытался создать способ сказать моему (работающему) приложению macOS открыть некоторые файлы и предоставить дополнительные аргументы команде.

Для приложений с холодным запуском с помощью

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

запустит приложение, и я смогу проверить --foo-arg через UserDefaults/CommandLine/ProcessInfo. Однако, если приложение уже запущено, --foo-arg отсутствует в UserDefaults/ProcessInfo‌/CommandLine.

Я изо всех сил пытался найти решение здесь, потому что у меня есть несколько требований, которые немного усложняют ситуацию.

Требования

  1. Пути к файлам, отправляемые в приложение, должны открываться/сохраняться с разрешениями песочницы.
  2. Аргументы и пути к файлам должны быть перехвачены приложением одновременно.

Возможные решения

XPC

Некоторые люди предлагали мне использовать XPC, но, прочитав об этом, я не уверен, как это решение может выглядеть?

  • Нужно ли мне создавать приложение-компаньон агента запуска, которое всегда работает, чтобы оно могло обнаруживать операции командной строки и передавать их моему приложению?
  • Как это работает с песочницей, потому что у каждого процесса есть свои собственные разрешения?

Сценарий Apple

  • Должен ли я использовать сценарий Apple, чтобы указать моему приложению открывать эти файлы с аргументами, чтобы обойти функцию песочницы?
  • При открытии файлов через AppleScript могу ли я сохранить эти файлы?

Схема URL

Я могу зарегистрировать свое приложение, чтобы иметь собственную схему URL-адресов, но способ, которым NSApplicationDelegate обрабатывает входящие URL-адреса, состоит из двух пакетов. Во-первых, URL-адреса, которые он может открыть, а затем схемы URL-адресов или пути к файлам, которые он не может открыть. то есть:

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

Я, вероятно, могу сделать эту работу, но это немного безвкусно, и я действительно хочу сделать это правильно.


person Andyy    schedule 30.05.2020    source источник


Ответы (1)


Инструмент командной строки, который принимает свои аргументы и передает их в Apple Events, — это то, что нужно. Вы можете увидеть, как это работает с точки зрения пользователя, установив инструменты командной строки BBEdit, а затем запустив man bbedit или man bbdiff в окне терминала.

С точки зрения вашего инструмента командной строки интересными частями являются:

  1. Выясните, запущено ли приложение: +[NSRunningApplication runningApplicationsWithBundleIdentifier:] поможет в этом.

  2. Если приложение не запущено, используйте -[NSWorkspaceURLForApplicationWithBundleIdentifier:], чтобы сначала найти приложение по идентификатору пакета, а затем -[NSWorkspace launchApplicationAtURL:options:configuration:error:], чтобы запустить приложение. Это вернет экземпляр NSRunningApplication или NIL и ошибку. (Обязательно обработайте случай ошибки.)

  3. Используя экземпляр NSRunningApplication, полученный либо на шаге 1, либо на шаге 2, теперь вы можете использовать API-интерфейсы NSAppleEventDescriptor или низкоуровневые API-интерфейсы AppleEvent C для создания события. (API более высокого уровня, вероятно, проще в использовании.)

Это будет выглядеть примерно так:

  1. Создайте целевой дескриптор, используя processIdentifier из работающего приложения:

    targetDesc = [NSAppleEventDescriptor descriptorWithProcessIdentifier: myRunningApplication.processIdentifier;
    
  2. Создайте событие открытия документов, адресованное вашему целевому приложению:

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

    Примечание. В качестве примера я использую kCoreEventClass/kAEOpenDocuments — если вы пытаетесь открыть один или несколько файлов с дополнительной информацией, это нормально. Если вы выполняете какую-то другую работу, вам следует придумать четырехсимвольный код для класса события, характерного для вашего приложения, и четырехсимвольный идентификатор события, уникальный для запрашиваемой операции.

  3. Добавьте аргументы команды к событию. Для каждого аргумента это состоит в создании соответствующего дескриптора на основе встроенного типа аргумента (логическое значение, int, строка, URL-адрес файла) и последующем добавлении его к событию с использованием параметра ключевого слова.

    (Ключевое слово Apple Event — это четырехсимвольный код. Вы можете придумать свой собственный код с ограничениями (не используйте только строчные буквы, и вы можете использовать те, которые определены в AEDataModel.h или AERegistry.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:]. Это будет вызвано для вашего пользовательского класса события и идентификатора, которые вы придумали выше, после чего вы можете использовать API-интерфейсы дескриптора, чтобы разделить событие и запустить свою операцию.

Песочница сама о себе позаботится: ваше приложение автоматически получает расширение песочницы для файлов, переданных через Apple Events.

Ваш инструмент командной строки не находится в изолированной программной среде — этого не может быть, потому что он запускается из Терминала и (потенциально) других приложений, не находящихся в изолированной программной среде.

Однако инструмент должен быть подписан с защищенной средой выполнения, а также с com.apple.security.automation.apple-events = YES и com.apple.security.temporary-exception.apple-events, обозначающими идентификатор пакета вашего приложения, чтобы инструмент мог отправлять Apple Events в ваше приложение.

(И инструменту потребуется Info.plist со строкой NSAppleEventsUsageDescription.)

Я оставил изрядное количество в качестве упражнения для читателя; но, надеюсь, это поможет вам начать.

person siegel    schedule 01.06.2020