KeyboardProc, возвращающий TRUE, вызывает падение производительности

Я разрабатываю интерфейс для дополнения к игре. Я не могу использовать игровой API (по нескольким причинам, включая тот факт, что код должен быть независимым от игры), и мне нужно получать ввод с клавиатуры от пользователя, поэтому я решил использовать клавиатурный хук (WH_KEYBOARD) для обработки пользователя. ввода при выполнении определенных условий.
Проблема в том, что, хотя я могу правильно получать и обрабатывать ввод, когда мой хук возвращает TRUE вместо CallNextHookEx системе требуется много времени (намного более 800 мс), прежде чем все пойдет как положено, и это неприемлемо потому что он даже не позволяет плавно печатать.
Чего мне нужно добиться, так это предотвратить попадание сообщения о нажатии клавиши в WndProc, поэтому вопрос в том, что я могу сделать, чтобы достичь своей цели, не навредив игре. производительность настолько велика, что результат будет неприемлемым?
РЕДАКТИРОВАТЬ: из-за особых требований (игры, использующие античиты, которые могут создать проблемы с моим кодом, несмотря на то, что они не связаны с читерством), создание подкласса активного wndproc не является вариантом.


person em70    schedule 27.06.2009    source источник


Ответы (4)


  1. Сначала вам нужно, чтобы ваша DLL была внедрена в целевой процесс с помощью перехватчиков или любого по-другому.

  2. Найдите дескриптор интересующего окна.

  3. Получите текущую оконную процедуру этого окна, вызвав GetWindowLongPtr(wnd, GWLP_WNDPROC), и сохраните ее.

  4. Создайте подкласс окна, вызвав SetWindowLongPtr( wnd, GWLP_WNDPROC, &NewWndProc ), где NewWndProc — ваша процедура обработки сообщений, реализованная в библиотеке DLL.

Внутри NewWndProc вы захотите обрабатывать сообщения клавиатуры (их дюжина, введите «ввод с клавиатуры» в индексе MSDN, я не могу опубликовать более 1 ссылки). Для остальных сообщений Windows вызывается исходная оконная процедура, которую вы сохранили во время (3), и возвращаете значение, которое она вернула. Не вызывайте его напрямую, вместо этого используйте CallWindowProc.

Этот способ не очень надежен, некоторым антивирусам и программам защиты от ботов (например, «клиент надзирателя») он может не понравиться, а отладка может быть сложной.

Однако это должно работать.

person Soonts    schedule 27.06.2009
comment
Спасибо за ваше умное предложение, но, как вы указали, античитам это не понравится, и хотя я не делаю это в целях мошенничества, я не могу позволить себе вызывать ложные срабатывания. Хорошая часть хука клавиатуры заключается в том, что античиты не будут помечать это, потому что многие программы используют это в законных целях... - person em70; 27.06.2009
comment
Подклассы Windows также используются многими программами в законных целях. - person Soonts; 27.06.2009

Крюк клавиатуры не должен замедлять работу. Вероятно, что-то еще происходит, что вызывает задержку в 800 мс. Это все еще медленно, если ваш хук ничего не делает и просто возвращает TRUE?

person Assaf Lavie    schedule 27.06.2009
comment
Да, просто вернуть TRUE достаточно, чтобы вызвать замедление при возврате FALSE, даже если я обрабатываю событие клавиатуры, это вообще не влияет на производительность (но это не нормально для меня, потому что нажатия клавиш, которые я обрабатываю, не должны обрабатываться игрой). Я тестировал его в двух разных играх с разными движками, и результаты одинаковы. - person em70; 27.06.2009

Если вы хотите, чтобы сообщение не поступало в WndProc, вам необходимо создать подкласс, используя SetWindowLong, таким образом вы сможете перехватывать все сообщения и решать, продолжать ли их маршрут.

person Shay Erlichmen    schedule 27.06.2009
comment
Спасибо, но, как было сказано выше, создание подклассов невозможно. - person em70; 28.06.2009

Как бы мне не нравилось отвечать на мой собственный вопрос, я нашел причину задержки. Насос сообщений в играх, на которых я тестировал свой код, был реализован с помощью while(PeekMessage) { GetMessage... }, и удаление сообщения ввода с клавиатуры каким-то образом приводило к блокировке GetMessage на какое-то время. Использование PostMessage и WM_NULL помогло предотвратить блокировку GetMessage.

person em70    schedule 28.06.2009