使用XPC与另一个应用程序通信

28

我有一个窗口应用程序,为了添加一些功能,我需要另一个应用程序,在登录时启动并在可用时将数据同步到服务器。

我试着使用NSDistributionNotification,但在沙盒应用程序中实际上是无用的。我查阅了XPC并希望它能工作,但我不知道如何让它与辅助程序一起工作。到目前为止,我已经使用XPC做到了这一点。

主应用程序

    NSXPCInterface *remoteInterface = [NSXPCInterface interfaceWithProtocol:@protocol(AddProtocol)];
    NSXPCConnection *xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.example.SampleService"];

    xpcConnection.remoteObjectInterface = remoteInterface;

    xpcConnection.interruptionHandler = ^{
        NSLog(@"Connection Terminated");
    };

    xpcConnection.invalidationHandler = ^{
        NSLog(@"Connection Invalidated");
    };

    [xpcConnection resume];

    NSInteger num1 = [_number1Input.stringValue integerValue];
    NSInteger num2 = [_number2Input.stringValue integerValue];

    [xpcConnection.remoteObjectProxy add:num1 to:num2 reply:^(NSInteger result) {
        NSLog(@"Result of %d + %d = %d", (int) num1, (int) num2, (int) result);
    }];

XPC服务

In main () ...
SampleListener *delegate = [[SampleListener alloc] init];
NSXPCListener *listener = [NSXPCListener serviceListener];
listener.delegate = delegate;
[listener resume];

// In delegate
-(BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
    NSXPCInterface *interface = [NSXPCInterface interfaceWithProtocol:@protocol(AddProtocol)];
    newConnection.exportedInterface = interface;
    newConnection.exportedObject = [[SampleObject alloc] init];
    [newConnection resume];
    return YES;
}

// In Exported Object class

-(void)add:(NSInteger)num1 to:(NSInteger)num2 reply:(void (^)(NSInteger))respondBack {
    resultOfAddition = num1 + num2;
    respondBack(resultOfAddition);
}

这个方法很好用,但现在我需要将结果传递给Helper app。我该怎么做?如果XPC不是通信的答案,那我应该使用哪种通信方式?请给我一些指导。

3个回答

24

对于一直苦苦挣扎的人,我最终成功地通过使用NSXPCConnection在两个应用程序进程之间建立了100%的通信。

需要注意的关键点是,只能将NSXPCConnection创建为三种对象之一:

  1. XPCService。您只能通过名称连接到XPCService。
  2. Mach服务。您也可以只通过名称连接到Mach服务。
  3. NSXPCEndpoint。这就是我们要寻找的,用于在两个应用程序进程之间通信。

问题在于,无法直接从一个应用程序传输NSXPCEndpoint到另一个应用程序。

需要创建一个machservice Launch Agent(参见此示例),其中包含一个NSXPCEndpoint属性。一个应用程序可以连接到该machservice,将该属性设置为其自己的[NSXPCListener anonymousListener].endpoint

然后,另一个应用程序可以连接到该machservice,并请求该端点。

使用该端点,可以创建一个NSXPCConnection,这将成功地在两个应用程序之间建立桥梁。我已测试了来回发送对象的功能,一切都像预期的那样工作。

请注意,如果您的应用程序被沙箱化,您将不得不创建一个XPCService,作为您的应用程序和Machservice之间的中间人。

我很高兴自己能够让它起作用--我在SO上非常活跃,所以如果有任何人对源代码感兴趣,只需添加评论,我可以花费精力发布更多详细信息。

我遇到的一些障碍:

您必须启动machservice,以下是相关代码:

   OSStatus                    err;
   AuthorizationExternalForm   extForm;

   err = AuthorizationCreate(NULL, NULL, 0, &self->_authRef);
   if (err == errAuthorizationSuccess) {
      NSLog(@"SUCCESS AUTHORIZING DAEMON");
   }
   assert(err == errAuthorizationSuccess);

   Boolean             success;
   CFErrorRef          error;

   success = SMJobBless(
                        kSMDomainSystemLaunchd,
                        CFSTR("DAEMON IDENTIFIER HERE"),
                        self->_authRef,
                        &error
                        );

此外,每次您重新构建守护程序时,都需要使用以下Bash命令卸载先前的启动代理:

sudo launchctl unload /Library/LaunchDaemons/com.example.apple-samplecode.EBAS.HelperTool.plist
sudo rm /Library/LaunchDaemons/com.example.apple-samplecode.EBAS.HelperTool.plist
sudo rm /Library/PrivilegedHelperTools/com.example.apple-samplecode.EBAS.HelperTool

(当然要使用相应的标识符)


1
如果有的话,请发布更多的代码。需要在我的应用程序和使用我构建的SDK的其他应用程序之间进行通信。 - jarora
1
你遇到了什么问题?已经有一段时间了,代码分散在许多地方,如果你能帮忙进一步解释问题,我可以尝试为你发布更相关的代码。 - A O
这在生产应用程序中可能吗? - Tibin Thomas
我几年前就离开了公司,所以我不确定他们最终是否将其移至生产环境 - 我知道在应用商店上沙盒应用程序存在问题,可能需要其他人回答这个问题,或者您可以自己尝试一下。 - A O
是的,对于任何想在沙盒应用程序中了解如何执行此操作的人,苹果有这个演示:https://developer.apple.com/library/archive/samplecode/EvenBetterAuthorizationSample/Introduction/Intro.html - kakaiikaka

14

如果您正在寻找如何在Swift中完成此操作的方法。我编写了一篇关于如何做到这一点的教程:

https://rderik.com/blog/creating-a-launch-agent-that-provides-an-xpc-service-on-macos/

您必须首先创建一个启动代理(或守护进程,如果需要更多特权),以公开XPC服务。 XPC服务将注册为您的代理提供的mach服务。 因此,您的代理将需要创建像以下内容的侦听器:

let listener = NSXPCListener(machServiceName: "com.rderik.exampleXPC" )

如果您想从其他客户端使用该服务,则需要创建一个NSXPCConnection到该mach服务。就像这样:

let connection = NSXPCConnection(machServiceName: "com.rderik.exampleXPC")
在幕后,发生的简化过程是您的代理将您的mach服务注册到launchd中。当您的“客户端”想要连接到一个mach服务时,launchd已经将其注册,因此它将在两者之间建立连接。
希望这有所帮助。

我是Swift的新手,你能用Objective-C来写吗? - ChenSmile

2
我认为我找到了如何做这件事的方法。您只需要在Xcode中创建一个命令行帮助工具,将其安装为Launchd作业(根据所需特权要求选择守护进程或代理程序)。您可以使用定义的协议与帮助程序进行通信。请参考苹果提供的以下示例代码以了解如何完成此操作。
苹果提供的示例代码: https://developer.apple.com/library/mac/samplecode/EvenBetterAuthorizationSample/Listings/Read_Me_About_EvenBetterAuthorizationSample_txt.html#//apple_ref/doc/uid/DTS40013768-Read_Me_About_EvenBetterAuthorizationSample_txt-DontLinkElementID_17 阅读下面的链接以了解您真正想要的是守护进程还是代理: https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/DesigningDaemons.html#//apple_ref/doc/uid/10000172i-SW4-BBCBHBFB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接