How do I distinguish between left and right keys (

2020-06-18 09:18发布

问题:

I started making use of Win32's raw input features to detect all keys on the keyboard. So far, everything is working great! I can distinguish between numbers on the top row and numbers in the keypad on the right. I can even detect between the left and right shift keys. However, the control and alt keys do not return unique scan codes. The control key returns 29, and the alt key returns 56.

The popular method for checking key states on these keys is GetAsyncKeyState. I have tested that function using VK_LCONTROL and VK_RCONTROL, and it works, but that only helps me for capturing key down events. I would really like to be able to capture key up events as well. It is obvious that the API is somehow aware of which key is being pressed; how do I get ahold of that information?

I am currently extracting the scan code from the RAWKEYBOARD structure's MakeCode field. That gives me information about every key (and its left/right alignment) except CTRL and ALT. How would I go about capturing the key up events (and knowing whether it is left/right)? Is it possible using just the RAWKEYBOARD structure? Or do I have to concoct some kind of workaround?

回答1:

If you want to get low level enough to detect key up events, you should process the WM_KEYDOWN and WM_KEYUP events:

  • http://msdn.microsoft.com/en-us/library/ms646267%28v=vs.85%29.aspx#_win32_Keystroke_Messages

Pressing a key causes a WM_KEYDOWN or WM_SYSKEYDOWN message to be placed in the thread message queue attached to the window that has the keyboard focus. Releasing a key causes a WM_KEYUP or WM_SYSKEYUP message to be placed in the queue.

Key-up and key-down messages typically occur in pairs, but if the user holds down a key long enough to start the keyboard's automatic repeat feature, the system generates a number of WM_KEYDOWN or WM_SYSKEYDOWN messages in a row. It then generates a single WM_KEYUP or WM_SYSKEYUP message when the user releases the key.

To distinguish between the left and right versions of the Shift, Ctrl, or Alt keys, you have to use the MapVirtualKey() function or the 'extended key' bit in the lParam passed with the virtual key's message. The following function will perform that translation for you - just pass in the virtual keycode and the lParam from the message, and you'll get back the left/right specific virtual keycodes as appropriate:

WPARAM MapLeftRightKeys( WPARAM vk, LPARAM lParam)
{
    WPARAM new_vk = vk;
    UINT scancode = (lParam & 0x00ff0000) >> 16;
    int extended  = (lParam & 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;
}

If the virtual keycode passed in isn't one that maps to a left/right version, the original keycode is passed back unchanged. So you can just run the WM_KEYDOWN/WM_KEYUP/WM_SYSKEYDOWN/WM_SYSKEYUP message parameters through the function whenever you need to distinguish between the left and right variants.



回答2:

GetAsyncKeyState's documentation says that:

... If the most significant bit is set, the key is down ...

which also means that if the MSB it cleared, the key is up.