Как мне отличить левую клавишу от правой (CTRL и ALT)?

#c #c #winapi #input #keyboard

#c #c #winapi #ввод #клавиатура

Вопрос:

Я начал использовать функции необработанного ввода Win32 для определения всех клавиш на клавиатуре. Пока все работает отлично! Я могу различать цифры в верхней строке и цифры на клавиатуре справа. Я даже могу определить разницу между левой и правой клавишами shift. Однако клавиши control и alt не возвращают уникальные коды сканирования. control Клавиша возвращает 29, а alt клавиша возвращает 56.

Популярный метод проверки состояний клавиш на этих клавишах GetAsyncKeyState . Я протестировал эту функцию с помощью VK_LCONTROL и VK_RCONTROL , и она работает, но это помогает мне только для захвата событий нажатия клавиши вниз. Мне бы очень хотелось также иметь возможность фиксировать события нажатия клавиш. Очевидно, что API каким-то образом знает, какая клавиша нажата; как мне получить доступ к этой информации?

В настоящее время я извлекаю код сканирования из поля RAWKEYBOARD структуры MakeCode . Это дает мне информацию о каждой клавише (и ее выравнивании по левому / правому краю), кроме CTRL и ALT. Как бы я мог фиксировать события key up (и знать, является ли это левой / правой)? Возможно ли это, используя только RAWKEYBOARD структуру? Или я должен придумать какой-то обходной путь?

Комментарии:

1. Полезная ссылка об обработке ввода в Windows: msdn.microsoft.com/en-us/library/ms171535.aspx

2. Я не знаю ответа на ваш вопрос, но если вы можете обнаруживать события key down по отдельности, но только одно комбинированное событие key up , то простым обходным путем было бы отслеживать, была ли последняя нажатая клавиша нажата левой или правой клавишей, и предположим, что это та же клавиша, которая отпускается во время события key-up. Конечно, это не сработает, если они нажмут обе клавиши одновременно; является ли это проблемой, зависит от вашего варианта использования…

3. Да, я определенно буду рассматривать это как жизнеспособный обходной путь. Ни одна система не будет идеальной, но это определенно было бы удовлетворительным решением. Мне просто странно, что win32 так непоследовательно передает эту информацию программисту.

4. GetAsyncKeyState предназначен для получения моментального снимка состояния клавиатуры. Это не предназначено для того, чтобы сказать вам, когда все изменится. Вот для чего существуют события WM_KEYDOWN , WM_SYSKEYDOWN WM_KEYUP и WM_SYSKEYUP . Я не понимаю, почему вы называете это непоследовательным. Это две принципиально разные вещи.

5. Позволяет ли ваша проблема использовать обычные оконные сообщения (WM_KEYDOWN, WM_KEYUP и т.д.) И проверять параметр LPARAM? Почему информация о левой / правой стороне в RAWKEYBOARD::Flags поле не подходит для CTRL / ALT, если она работает для других клавиш?

Ответ №1:

Если вы хотите получить достаточно низкий уровень для обнаружения событий нажатия клавиш, вам следует обработать события WM_KEYDOWN и WM_KEYUP:

Нажатие клавиши приводит к тому, что сообщение WM_KEYDOWN или WM_SYSKEYDOWN помещается в очередь сообщений потока, прикрепленную к окну, в фокусе которого находится клавиатура. При отпускании клавиши сообщение WM_KEYUP или WM_SYSKEYUP помещается в очередь.

Сообщения о нажатии клавиши вверх и клавиши вниз обычно появляются парами, но если пользователь удерживает клавишу нажатой достаточно долго, чтобы запустить функцию автоматического повтора клавиатуры, система генерирует несколько сообщений WM_KEYDOWN или WM_SYSKEYDOWN подряд. Затем он генерирует одно сообщение WM_KEYUP или WM_SYSKEYUP, когда пользователь отпускает клавишу.

Чтобы различать левую и правую версии клавиш Shift, Ctrl или Alt, вы должны использовать MapVirtualKey() функцию или бит ‘extended key’ в параметре lParam, передаваемом с сообщением виртуального ключа. Следующая функция выполнит этот перевод за вас — просто передайте виртуальный код ключа и параметр lParam из сообщения, и вы получите обратно соответствующие виртуальные коды клавиш для левой / правой клавиш:

 WPARAM MapLeftRightKeys( WPARAM vk, LPARAM lParam)
{
    WPARAM new_vk = vk;
    UINT scancode = (lParam amp; 0x00ff0000) >> 16;
    int extended  = (lParam amp; 0x01000000) != 0;

    switch (vk) {
    case VK_SHIFT:
        new_vk = MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX);
        break;
    case VK_CONTROL:
        new_vk = extended ? VK_RCONTROL : VK_LCONTROL;
        break;
    case VK_MENU:
        new_vk = extended ? VK_RMENU : VK_LMENU;
        break;
    default:
        // not a key we map from generic to left/right specialized
        //  just return it.
        new_vk = vk;
        break;    
    }

    return new_vk;
}
  

Если переданный виртуальный код ключа не соответствует левой / правой версии, исходный код ключа передается обратно без изменений. Таким образом, вы можете просто запускать WM_KEYDOWN / WM_KEYUP / WM_SYSKEYDOWN / WM_SYSKEYUP параметры сообщения через функцию всякий раз, когда вам нужно различать левый и правый варианты.

Комментарии:

1. Почему это принятый ответ? Это не отвечает на вопрос. Я знаю, что WM_KEYDOWN существует, но как вы отличаете, например, LSHIFT и RSHIFT друг от друга в нем?

2. @cib: Вы правы — я обновил ответ подробностями по этой части вопроса.

Ответ №2:

В документации GetAsyncKeyState говорится, что:

… Если установлен старший бит, клавиша находится в нижнем положении…

что также означает, что если MSB очищен, клавиша открыта.

Комментарии:

1. Кажется, это шаг в правильном направлении, но это все еще оставляет меня неуверенным, какая клавиша была поднята. Я фиксирую код сканирования 29, поэтому я знаю, что была поднята клавиша управления. Однако, если обе клавиши теперь активированы, как я узнаю, какая из них была едва поднята? Я предполагаю, что мне нужно отслеживать эти клавиши, как было предложено BlueRaja ранее.

2. ну, если вы отслеживаете события нажатия клавиш вверх / вниз, тогда вы должны обрабатывать сообщения WM_KEYUP и WM_KEYDOWN. параметры этих сообщений подскажут вам, какая клавиша была нажата / отпущена.

Ответ №3:

Чтобы проверить флаг расширенной клавиши для «правой» клавиши в lParam, также можно сделать этот способ.

Если true — это правый Ctrl или Alt, основанный на wParam == VK_CONTROL или VK_MENU

в противном случае это левый Ctrl или Alt >

 (HIWORD(lParam) amp; KF_EXTENDED) == KF_EXTENDED
  

Чтобы получить код сканирования, также можно сделать это >

 BYTE scan_code = LOBYTE(HIWORD(lParam));
  

для использования

 MapVirtualKey(scan_code, MAPVK_VSC_TO_VK_EX);
  

для определения VK_LSHIFT или VK_RSHIFT на основе wParam == VK_SHIFT