Я работаю над приложением 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 не поддерживает такую функциональность?
Наконец, я вернулся к этому проекту и успешно зарегистрировал поставщика услуг и обработал запрос на обслуживание.
Прежде всего, я попытался использовать метод 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.
После этого мое приложение может отреагировать на запрос службы из контекстного меню.
Я поделился источниками моего проекта. Здесь они есть.
Я вижу две потенциальные проблемы
Вы выделяете свой собственный объект NSApplication
. Я сомневаюсь, что это правильно - разве Delphi не создает внутри себя тоже? И даже если это не так, вам, вероятно, потребуется ввести метод NSApplication
run
в какой-то момент, чтобы он действительно мог обрабатывать сообщения.
Поставщики услуг должны быть зарегистрированы в методе делегата applicationDidFinishLaunching:
. Вы пытаетесь зарегистрировать его сразу после создания экземпляра NSApplication
.
Я думаю, вы можете избежать обеих проблем, если вы используете NSRegisterServicesProvider(id provider, NSString *portName)
для регистрации своего сервиса, вместо использования NSApplication
setServicesProvider:
.