Как добавить пункт меню в Mac OS Finder в Delphi XE2

31

Я работаю над приложением Delphi XE2, предназначенным для Mac OS и Windows. И я хочу интегрироваться в контекстное меню. Для окон это простая задача. Но для Mac OS я не знаю, как это сделать.

Я прочитал Предоставление службы документации и попробовал аналогичный код в Delphi, но не повезло.

Посмотрите на простой код для тестов интеграции Finder.

App.dpr

program App;
uses
   SysUtils,
{$IFDEF MACOS}
  AppKit, CocoaTypes, CoreFoundation,
  CoreServices, Foundation, Mach, ObjCRuntime,
  ObjectiveC, OCMarshal, OpenGL, QuartzCore, Security,
  SystemConfiguration,
{$ENDIF}
  MessageProvider;
{$IFDEF MACOS}
var
  app: NSApplication;
  provider: TMessageProvider;
{$ENDIF}

begin
  Application.Initialize;

{$IFDEF MACOS}
  provider := TMessageProvider.Create();

  app := TNSApplication.Alloc();
  app.setServicesProvider(provider);
{$ENDIF}

  Application.CreateForm(TFormOSVersion, FormOSVersion);
  Application.Run;
end.

MessageProvider.pas

unit MessageProvider;

interface

uses
  FMX.Dialogs
{$IFDEF MACOS}
  , AppKit, CocoaTypes, CoreFoundation,
  CoreServices, Foundation, Mach, ObjCRuntime,
  ObjectiveC, OCMarshal, OpenGL, QuartzCore, Security,
  SystemConfiguration
{$ENDIF}
  ;

type
  TMessageProvider = class
  public
    procedure simpleMessage(var userData: string; var error: string);
  end;

implementation

procedure TMessageProvider.simpleMessage(var userData: string; var error: string);
begin
  ShowMessage('Simple message from service.');
  error := '';
end;

end.

Добавлена ​​конфигурация к info.plist

<key>NSServices</key>
<array>
  <dict>
     <key>NSKeyEquivalent</key>
     <dict>
         <key>default</key>
         <string>e</string>
     </dict>
     <key>NSMenuItem</key>
     <dict>
         <key>default</key>
         <string>App/Message</string>
     </dict>
     <key>NSMessage</key>
     <string>simpleMesage</string>
     <key>NSPortName</key>
     <string>App</string>            
  </dict>
</array>

При запуске этого приложения Mac OS зависает и иногда сбой происходит с ошибкой "Ошибка шины".

Может ли кто-нибудь помочь с этой проблемой?

Или, может быть, Delphi XE2 не поддерживает такую ​​функциональность?

  • 3
    Связанный вопрос, предполагая, что вы используете Какао + ObjectiveC, может быть адаптирован, используя возможность DelphiXE2 / Firemonkey для вызова API на основе сообщений Какао / ObjectiveC: stackoverflow.com/questions/9420361/… - Я был бы соблазн написать всю разработку -services немного использует Cocoa / ObjectiveC и находит способ затем просто вызвать эту нативную разделяемую библиотеку ObjectiveC из вашего приложения Delphi.
  • 0
    Я думаю, что если это выполнимо, вы найдете это в бесплатной документации на Паскале или на форумах, поскольку XE2 использует бесплатный паскаль для OSX. И так как бесплатный паскаль был на OSX очень долго, я уверен, что он будет иметь больше, чем форумы Delphi.
Теги:
macos
delphi-xe2
finder

2 ответа

2

Наконец, я вернулся к этому проекту и успешно зарегистрировал поставщика услуг и обработал запрос на обслуживание.

Прежде всего, я попытался использовать метод NSRegisterServicesProvider, но такого метода нет в Macapi-источниках, поэтому я искал делегат applicationDidFinishLaunching. Используя его, я зарегистрировал поставщика услуг:

procedure TApplicationDelegate.applicationDidFinishLaunching(Notification: Pointer);
var
  autoReleasePool: NSAutoreleasePool;
  app: NSApplication;
  provider: TMessageProvider;
begin
  autoReleasePool := TNSAutoreleasePool.Create;
  try
    autoReleasePool.init();

    app := TNSApplication.Wrap(TNSApplication.OCClass.sharedApplication);

    provider := TMessageProvider.Create();
    app.setServicesProvider(provider.ObjId);
  finally
    autoReleasePool.release();
  end;
end;

Также я создал интерфейс для поставщика услуг (я думаю, что это необходимо для работы с мостом ObjectiveC-Delphi):

IMessageProvider = interface(IObjectiveC)['{1EA9319A-8F99-4445-B435-48D5E73876FA}']
    procedure simpleMessage(pBoard: Pointer; userData: Pointer; error: PPointer); cdecl;
end;

и унаследовал TMessageProvider из этого интерфейса и класса TOCLocal.

После этого мое приложение может отреагировать на запрос службы из контекстного меню.

Я поделился источниками моего проекта. Здесь они есть.

1

Я вижу две потенциальные проблемы

  • Вы выделяете свой собственный объект NSApplication. Я сомневаюсь, что это правильно - разве Delphi не создает внутри себя тоже? И даже если это не так, вам, вероятно, потребуется ввести метод NSApplication run в какой-то момент, чтобы он действительно мог обрабатывать сообщения.

  • Поставщики услуг должны быть зарегистрированы в методе делегата applicationDidFinishLaunching:. Вы пытаетесь зарегистрировать его сразу после создания экземпляра NSApplication.

Я думаю, вы можете избежать обеих проблем, если вы используете NSRegisterServicesProvider(id provider, NSString *portName) для регистрации своего сервиса, вместо использования NSApplication setServicesProvider:.

  • 0
    У меня были похожие мысли по поводу первого пункта. И, насколько я помню, я искал какой-нибудь способ получить объект NSApplication из TApplication. Я попробую метод NSRegisterServicesProvider, когда вернусь в этот проект. Как я понимаю, этот метод должен вызываться прямо перед методом Application.Run?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню