虚拟按键进入错误的应用程序

Virtual keypress goes to wrong application

我有以下代码可以将虚拟按键发送到给定其 pid

的进程
    NSRunningApplication* app = [NSRunningApplication
                                 runningApplicationWithProcessIdentifier: pid];
    [app activateWithOptions: (NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];

    event1 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)cg_key_code, true);
    event2 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)cg_key_code, false);

    CGEventPost(kCGHIDEventTap, event1);
    CGEventPost(kCGHIDEventTap, event2);

正如预期的那样,我希望将按键发送到的进程立即出现在最前面。但问题是,在我的应用程序出现之前,第一个按键将转到位于前面的应用程序。测试时,[app isActive] returns false 第一次。第一个键之后,一切正常。

为什么会这样?尽管我在 之后发布了关键事件,但将我的流程推到了前面。

由于文档没有说明 activateWithOptions: 等待,并且当应用程序到达前台时它没有提供回调的完成块,我们可以假设该方法将 return 一旦检查了开关的有效性并发送了激活消息。在发生这种情况和应用程序实际准备好接收用户输入之间不可避免地会有一些延迟。

虽然我们希望 OS X 可以缓冲用户输入并在准备就绪时将其发送到应用程序,但在这种情况下总会出现竞争条件,因此在编写代码时要谨慎需要等待。

只是等待设定的时间并不是一个好主意,但您可以使用工具来确定您应该做什么以及多长时间 - 特别是使用 isActive。此外,谨慎的做法是检查 activateWithOptions: 的响应,以确保我们不会陷入僵局。

类似于:

if ([app activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)]) {

    while (![app isActive]) {
        app = [NSRunningApplication
                   runningApplicationWithProcessIdentifier: pid];
        [NSThread sleepForTimeInterval:0.05];
    }
}

// send key press events

不使用 CGEventPost(),而是使用 CGEventPostToPSN()。这将事件传递给特定进程。您可以使用这样的代码来获取流程序列号:

ProcessSerialNumber psn;
GetProcessForPID(app.processIdentifier, &psn);

我最近在以下网址问过类似的问题:

请检查 TheNextman 的答案。

它是 Ken Thomases 概述的两种方法之一的实现。

我刚刚测试过它并且有效。

我测试了 Ken 概述的另一种方法(在他的回答中),它 没有 工作。我想这可能是因为已弃用 GetProcessForPID 调用。