1

我有以下代码可以将虚拟按键发送到给定 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]第一次返回 false。在第一个键之后,一切顺利。

为什么会这样?即使我在将我的流程推向前台后发布了关键事件。

4

3 回答 3

2

As the documentation doesn't say that activateWithOptions: waits, and it doesn't provide a completion block to callback when the application has come to the foreground we can assume that the method will return as soon as the validity of the switch have been checked and the activation message sent. There will inevitably be some latency between this happening and the application actually being ready to receive user input.

While we could hope that OS X would buffer the user input and send it to the application when it's ready there will always be a race condition in this case so it's prudent to code with the expectation that you need to wait.

Just waiting for a set time isn't a great idea, but you have the tools to determine what you should do and for how long - specifically using isActive. Also, it's prudent to check the response of activateWithOptions: to ensure we don't end up deadlocked.

Something like:

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

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

// send key press events
于 2015-03-01T20:57:28.050 回答
1

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

ProcessSerialNumber psn;
GetProcessForPID(app.processIdentifier, &psn);
于 2015-03-01T21:45:49.377 回答
1

我最近在前台问了一个类似的问题: Inject keyboard event into NSRunningApplication afteregrounding it

请检查 TheNextman 的答案。

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

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

我测试了肯(在他的回答中)概述的另一种方法,但它不起作用。我想这可能是因为不推荐使用的GetProcessForPID电话。

于 2019-07-13T21:39:18.223 回答