题 如何在Delphi XE2中将菜单项添加到Mac OS Finder


我正在研究面向Mac OS和Windows的Delphi XE2应用程序。我希望集成到上下文菜单中。对于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不支持这种功能?


31
2017-11-04 17:03


起源


假设您正在使用Cocoa + ObjectiveC的相关问题,可以使用DelphiXE2 / Firemonkey调用cocoa / objectiveC基于消息的API来调整: stackoverflow.com/questions/9420361/...   - 我很想用Cocoa / ObjectiveC编写整个make-services位,然后找到一种方法然后从你的delphi应用程序中简单地调用native nativeC共享库。 - Warren P
我认为如果它是可行的,你会在免费的pascal文档或论坛中找到它,因为XE2使用免费的pascal for OSX。而且由于免费的pascal已经在OSX上工作了很长时间,我相信它会比Delphi论坛更多。 - adrianj98


答案:


最后,我回到了这个项目并成功注册了服务提供商并处理了服务请求。

首先,我尝试使用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;

并从此接口和TOCLocal类继承了TMessageProvider。

在此之后我的应用程序可以从上下文菜单响应服务请求。

我已经分享了我的项目来源。 这里 他们是。


2
2018-03-23 15:50





我看到两个潜在的问题

  1. 你正在分配自己的 NSApplication 目的。我怀疑这是正确的 - Delphi内部也没有创建一个吗?即使它没有,你可能需要输入 NSApplicationrun 方法在某些时候使它实际上能够处理消息。

  2. 服务提供商必须注册 applicationDidFinishLaunching: 委托方法。您创建后立即尝试立即注册 NSApplication 实例。

我认为如果你使用,你可以避免这两个问题 NSRegisterServicesProvider(id provider, NSString *portName) 注册您的服务提供,而不是使用 NSApplicationsetServicesProvider:


1
2017-10-05 13:24



我对第一项有类似的想法。而且,正如我记得的那样,我正在寻找一些从TApplication获取NSApplication对象的方法。我将在返回该项目时尝试NSRegisterServicesProvider方法。据我所知,应该在Application.Run方法之前调用此方法? - GothAr