В мире разработки игр и приложений на Delphi часто возникает необходимость отслеживать состояние клавиш клавиатуры. Для этой цели Windows API предоставляет функцию GetAsyncKeyState(). Однако, как показывает обсуждение на форуме, неправильное использование этой функции может привести к неожиданным результатам. Давайте разберемся, как правильно использовать GetAsyncKeyState() в Delphi и какие альтернативы существуют.
Проблема:
Автор темы столкнулся с тем, что после обновления Lazarus, код, использующий boolean(GetAsyncKeyState(VK_NUMPAD4)), перестал работать корректно. Ранее код полагался на то, что любое ненулевое значение, возвращаемое GetAsyncKeyState(), интерпретируется как True.
Решение 1: Битовая маска и оператор and
Функция GetAsyncKeyState() возвращает 16-битную битовую маску, где:
Старший бит (bit 15, $8000) указывает, нажата ли клавиша в данный момент.
Младший бит (bit 0) указывает, была ли клавиша нажата с момента последнего вызова GetAsyncKeyState().
Чтобы проверить, нажата ли клавиша в данный момент, необходимо проверить старший бит. Для этого можно использовать оператор and и шестнадцатеричное значение $8000:
if (GetAsyncKeyState(VK_NUMPAD8) and $8000) <> 0 then
begin
// Клавиша NUMPAD8 нажата
end;
Этот код выполняет побитовое "И" между результатом GetAsyncKeyState(VK_NUMPAD8) и $8000. Если старший бит установлен (клавиша нажата), результат будет ненулевым, и условие ( ... ) <> 0 будет истинным.
Решение 2: Проверка на неравенство нулю (<> 0)
Более простой способ проверить, нажата ли клавиша, - это использовать оператор неравенства (<> 0):
if GetAsyncKeyState(VK_NUMPAD4) <> 0 then
begin
// Клавиша NUMPAD4 нажата (или была нажата с момента последнего вызова)
end;
В этом случае мы проверяем, является ли возвращаемое значение GetAsyncKeyState() ненулевым. Этот метод подходит, если нам нужно знать, была ли клавиша нажата вообще, а не только в данный момент.
Решение 3: Сравнение с нулем (< 0)
Поскольку возвращаемое значение GetAsyncKeyState() является знаковым (signed), можно использовать оператор сравнения < 0 для проверки старшего бита:
if GetAsyncKeyState(VK_NUMPAD4) < 0 then
begin
// Клавиша NUMPAD4 нажата
end;
Этот метод эквивалентен использованию битовой маски $8000.
Решение 4: Приведение к WordBool
Предложено приводить результат GetAsyncKeyState() к типу WordBool перед сравнением с нулем. Это может улучшить читаемость кода и снизить риск ошибок, связанных с неправильной интерпретацией результата:
if WordBool(GetAsyncKeyState(VK_NUMPAD4)) then
begin
// Клавиша NUMPAD4 нажата (или была нажата с момента последнего вызова)
end;
Почему boolean(GetAsyncKeyState(VK_NUMPAD8)) больше не работает?
Ранее компилятор Delphi мог допускать неявное преобразование любого ненулевого значения к True. Однако, современные компиляторы стали более строгими в отношении типов данных. Тип Boolean в Delphi строго определен: False - это 0, а True - это 1. Попытка преобразовать другие значения к Boolean может привести к непредсказуемым результатам.
Альтернативные решения:
Использование компонентов Delphi: Delphi предоставляет компоненты, такие как TActionList, которые упрощают обработку нажатий клавиш.
Использование библиотек: Существуют сторонние библиотеки, которые предоставляют более удобные и мощные инструменты для работы с клавиатурой и другими устройствами ввода.
Пример кода с использованием битовой маски для нескольких клавиш:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if ((GetAsyncKeyState(VK_NUMPAD4) and $8000) <> 0) or
((GetAsyncKeyState(VK_NUMPAD1) and $8000) <> 0) or
((GetAsyncKeyState(VK_NUMPAD7) and $8000) <> 0) or
((GetAsyncKeyState(VK_LEFT) and $8000) <> 0) then
begin
// rotate left
Label1.Caption := 'Поворот влево';
end else
Label1.Caption := 'Стоим';
end;
Важно помнить:
GetAsyncKeyState() может давать неверные результаты в многопоточных приложениях, если несколько потоков одновременно используют эту функцию.
Функция GetAsyncKeyState() возвращает состояние клавиши на момент вызова. Если клавиша была нажата и отпущена между вызовами GetAsyncKeyState(), вы можете это не заметить.
Заключение:
Функция GetAsyncKeyState() является полезным инструментом для отслеживания состояния клавиш в Delphi. Однако, важно понимать, как она работает и использовать ее правильно. Использование битовой маски и оператора and или сравнение с нулем (<> 0 или < 0) – надежные способы проверить, нажата ли клавиша. Приведение к WordBool может улучшить читаемость кода. Не забывайте о возможных проблемах в многопоточных приложениях и рассматривайте альтернативные решения, если они лучше подходят для вашей задачи.
В Delphi для точного определения состояния клавиши с помощью GetAsyncKeyState() необходимо использовать битовую маску или сравнение с нулем, так как функция возвращает 16-битное значение, содержащее информацию о текущем и предыдущем состояниях клавиши.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.