8

According to MSDN wparam should hold the key code. The problem is, when pressing shift, the code is 16(VK_SHIFT), but I need to distinguish between VK_LSHIFT and VK_RSHIFT.

For VK_CONTROL, there seems to be a workaround:

if(wParam == VK_CONTROL) {
    if ( lParam&EXTENDED_KEYMASK )
        wParam = VK_RCONTROL;
    else
        wParam = VK_LCONTROL;
}

However, the same won't work for VK_SHIFT:

if(wparam == VK_SHIFT) {
    if ( lParam&EXTENDED_KEYMASK )
        wParam = VK_RSHIFT;
    else
        wParam = VK_LSHIFT;
}

In the latter example, it will just always assume LSHIFT.

4

4 回答 4

15

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.

By using MapVirtualKey() you don't need to know the lore about the left-shift and right-shift scancodes being 0x2a and 0x36 - the API takes care of that detail. And if they ever do happen to be different (not that that will ever really happen), Windows will be responsible for dealing with it, not you.

So in your WM_KEYDOWN/WM_KEYUP/WM_SYSKEYDOWN/WM_SYSKEYUP handlers you just have to add a line of code that looks like:

wparam = MapLeftRightKeys(wparam, lparam);

and the rest of your code can act on the left/right-specific VK codes as if the system message just gave them to you in the first place.

于 2013-04-12T17:40:56.280 回答
8

There's ancient history behind your question. The original IBM PC keyboard didn't have the right Alt and Ctrl keys. They got added later on the extended keyboard layout, the keyboard controller sends them with the 0xe0 prefix to the scan code to distinguish them as extended keys. But the original keyboard layout always had two shift keys so they have their own non-extended scan codes. Which is why your code doesn't work.

David's answer is a good way to solve your problem. But you actually can get it out of the message, those scan codes are cast in stone by the Windows logo requirements. Available in lParam, the left shift key has scan code 42, the right shift key is 54. No #define in the windows headers for them unfortunately, that makes it ugly.

于 2013-04-12T09:38:41.087 回答
6

Call GetKeyState passing VK_LSHIFT or VK_RSHIFT.

于 2013-04-12T08:37:15.830 回答
1

"According to MSDN":

  • VK_LSHIFT
  • VK_RSHIFT
  • VK_LCONTROL
  • VK_RCONTROL
  • VK_LMENU
  • VK_RMENU

These left- and right-distinguishing constants are available to an application only through the GetKeyboardState, SetKeyboardState, GetAsyncKeyState, GetKeyState, and MapVirtualKey functions.

于 2013-04-12T09:19:17.397 回答