2015-03-01 3 views
0

У меня есть следующий код для отправки виртуальных нажатий клавиш в процесс данного идентификатора процессаВиртуальное нажатие идет на неправильное применение

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 в первый раз. После первого ключа все идет хорошо.

Почему это происходит? Несмотря на то, что я отправил ключевое событие после того, как получил мой процесс на фронт.

ответ

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 
+0

Хорошая идея. Но есть проблема. 'app' никогда не узнает, что его активный и цикл while никогда не заканчиваются. Чтобы преодолеть это, я попытался получить 'app' дескриптор каждый раз внутри цикла. Но это как-то значительно замедляет мое приложение от выхода на передний план. Я не уверен, почему изменения в моем коде не позволяют ОС переносить мое приложение на передний план. –

+0

. Попробуйте увеличить время от 0,05 до 0,1, а не то, что ваш процесс забивает немного процессорного времени, должен быть серьезной проблемой для современной ОС. – Wain

+0

Я сделал он работает с 0,05, но, как я уже говорил, мне нужно каждый раз получать новый дескриптор приложения внутри цикла. (Задержка, о которой я упомянул в предыдущем комментарии, ушла, как-то!) Я отредактирую ваш ответ, чтобы включить этот факт и принять ваш ответ. –

1

Вместо использования CGEventPost() используйте CGEventPostToPSN(). Это обеспечивает события для конкретного процесса. Вы можете использовать этот код, чтобы получить серийный номер процесса:

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

Я совершенно не знаком с Obj-C. Но я думаю, что GetProcessForPID является API-интерфейсом Carbon и устарел, не так ли? Знаете ли вы какой-либо эквивалент для какао? –

+0

Это не совсем Углерод, но он устарел и устарел. Однако устаревшее не означает «никогда не использовать». Это означает, что «используйте его только тогда, когда это единственный способ достичь того, что вам нужно, и быть готовым к тому, чтобы он в конечном итоге ушел». Во всяком случае, используйте его для этого, потому что существует не устаревший API, который требует PSN и не имеет современного способа получить PSN. Кроме того, сообщите об ошибке с Apple, запрашивающей либо способ публикации событий на PID, либо современный способ получить PSN. –