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

时间:2015-03-01 20:15:59

标签: objective-c cocoa cgeventtap

我有以下代码将虚拟按键发送到给定其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。在第一把钥匙之后,一切都很顺利。

为什么会这样?即使我在将我的流程放到前面后发布关键事件

3 个答案:

答案 0 :(得分:1)

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

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

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

类似的东西:

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

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

// send key press events

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

我最近在以下位置提出了类似的问题:  Inject keyboard event into NSRunningApplication immediately after foregrounding it

请检查TheNextman的答案。

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

我刚刚对其进行了测试,并且可以正常工作。

我测试了Ken(在他的回答中)概述的另一种方法,但没有奏效。我想这可能是由于GetProcessForPID调用被弃用了。