VC ++:枚举虚拟键码列表以及所有可能键组合的扫描码

时间:2017-11-12 08:09:09

标签: visual-c++ keyboard virtual-keyboard sendinput scancodes

我想枚举当前键盘布局支持的所有可能组合键的列表(使用虚拟键码,扫描码及其Unicode值)。将远程用户输入映射到键以进行模拟。

我期待VC ++的UCKeyTranslate(ObjectiveC)API可以接受虚拟键码和修饰符(ALT,SHIFT,CTRL)并提供扫描码,但找不到任何东西与此相似。

经过大量研究并花了整整2天,除了MapVirtualKeyEx之外别无其他选择。

我带了以下代码但是有很多问题,

BOOL PopulateKeyMap()
{
    TCHAR Buff[120 * sizeof(TCHAR)] = { 0 };
    GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, Buff, sizeof(Buff));
    HKL hKeyboardLayout = ::LoadKeyboardLayout(Buff, KLF_ACTIVATE);

    {
        lock_guard<recursive_mutex> lockHolder(cs_populate_key);

        if (hCurrentKeyboardLayout)
        {
            UnloadKeyboardLayout(hCurrentKeyboardLayout);
        }

        hCurrentKeyboardLayout = hKeyboardLayout;

        // Prepopulate keyCodeDictionary with common key combinations.
        for (int keyIndex = 0; keyIndex < KEY_CODES_DICT_SIZE; ++keyIndex)
        {
            {
                unsigned int Vk;

                tstring key_name = GetKeyName(keyIndex, Vk);

                if (key_name.compare(_T("")) != 0)
                {
                    SmartPtr<KeyCodeInfo> key = (new KeyCodeInfo);

                    if (key)
                    {
                        key->nIndex = keyIndex;
                        key->sVKCode = keyIndex;
                        key->nScanCode = Vk;

                        keyboard_map[key_name] = key;
                    }
                }// End if
            }
        }// End for

        bKeyMapInitialized = TRUE;
    }

    return TRUE;
}

tstring GetKeyName(unsigned int virtualKey, unsigned int &scanCode)
{
    if (!hCurrentKeyboardLayout)
    {
        PopulateKeyMap();
    }

    scanCode = MapVirtualKeyEx(virtualKey, MAPVK_VK_TO_VSC, hCurrentKeyboardLayout);

    // because MapVirtualKey strips the extended bit for some keys
    switch (virtualKey)
    {
        case VK_LEFT: case VK_UP: case VK_RIGHT: case VK_DOWN: // arrow keys
        case VK_PRIOR: case VK_NEXT: // page up and page down
        case VK_END: case VK_HOME:
        case VK_INSERT: case VK_DELETE:
        case VK_DIVIDE: // numpad slash
        case VK_NUMLOCK:
        {
            scanCode |= KF_EXTENDED; // set extended bit
            break;
        }
    }

    TCHAR keyName[256];
    if (GetKeyNameText(scanCode << 16, keyName, sizeof(keyName)) != 0)
    {
        return keyName;
    }
    else
    {
        return _T("");
    }
}

MapVirtualKeyEx仅提供基本扫描代码列表,而不提供具有修饰符组合(ALT,CTRL,SHIFT)的键的扫描代码。有没有什么方法可以提供修饰符的组合作为函数的输入,以便我可以生成所需的键组合?。

任何帮助将不胜感激。提前谢谢。

1 个答案:

答案 0 :(得分:1)

最后得到了一个解决方案,参考https://dxr.mozilla.org/mozilla-central/source/widget/windows/KeyboardLayout.cpp

void
FillKbdState(PBYTE aKbdState,
    const ShiftState aShiftState)
{
    if (aShiftState & STATE_SHIFT) {
        aKbdState[VK_SHIFT] |= 0x80;
    }
    else {
        aKbdState[VK_SHIFT] &= ~0x80;
        aKbdState[VK_LSHIFT] &= ~0x80;
        aKbdState[VK_RSHIFT] &= ~0x80;
    }

    if (aShiftState & STATE_CONTROL) {
        aKbdState[VK_CONTROL] |= 0x80;
    }
    else {
        aKbdState[VK_CONTROL] &= ~0x80;
        aKbdState[VK_LCONTROL] &= ~0x80;
        aKbdState[VK_RCONTROL] &= ~0x80;
    }

    if (aShiftState & STATE_ALT) {
        aKbdState[VK_MENU] |= 0x80;
    }
    else {
        aKbdState[VK_MENU] &= ~0x80;
        aKbdState[VK_LMENU] &= ~0x80;
        aKbdState[VK_RMENU] &= ~0x80;
    }

    if (aShiftState & STATE_CAPSLOCK) {
        aKbdState[VK_CAPITAL] |= 0x01;
    }
    else {
        aKbdState[VK_CAPITAL] &= ~0x01;
    }
}

inline int32_t GetKeyIndex(uint8_t aVirtualKey)
{
    // Currently these 68 (NS_NUM_OF_KEYS) virtual keys are assumed
    // to produce visible representation:
    // 0x20 - VK_SPACE          ' '
    // 0x30..0x39               '0'..'9'
    // 0x41..0x5A               'A'..'Z'
    // 0x60..0x69               '0'..'9' on numpad
    // 0x6A - VK_MULTIPLY       '*' on numpad
    // 0x6B - VK_ADD            '+' on numpad
    // 0x6D - VK_SUBTRACT       '-' on numpad
    // 0x6E - VK_DECIMAL        '.' on numpad
    // 0x6F - VK_DIVIDE         '/' on numpad
    // 0x6E - VK_DECIMAL        '.'
    // 0xBA - VK_OEM_1          ';:' for US
    // 0xBB - VK_OEM_PLUS       '+' any country
    // 0xBC - VK_OEM_COMMA      ',' any country
    // 0xBD - VK_OEM_MINUS      '-' any country
    // 0xBE - VK_OEM_PERIOD     '.' any country
    // 0xBF - VK_OEM_2          '/?' for US
    // 0xC0 - VK_OEM_3          '`~' for US
    // 0xC1 - VK_ABNT_C1        '/?' for Brazilian
    // 0xC2 - VK_ABNT_C2        separator key on numpad (Brazilian or JIS for Mac)
    // 0xDB - VK_OEM_4          '[{' for US
    // 0xDC - VK_OEM_5          '\|' for US
    // 0xDD - VK_OEM_6          ']}' for US
    // 0xDE - VK_OEM_7          ''"' for US
    // 0xDF - VK_OEM_8
    // 0xE1 - no name
    // 0xE2 - VK_OEM_102        '\_' for JIS
    // 0xE3 - no name
    // 0xE4 - no name

    static const int8_t xlat[256] =
    {
        // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
        //-----------------------------------------------------------------------
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // 00
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // 10
        0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // 20
        1,  2,  3,  4,  5,  6,  7,  8,  9, 10, -1, -1, -1, -1, -1, -1,   // 30
        -1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,   // 40
        26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, -1, -1,   // 50
        37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, -1, 49, 50, 51,   // 60
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // 70
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // 80
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // 90
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // A0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 53, 54, 55, 56, 57,   // B0
        58, 59, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // C0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 61, 62, 63, 64, 65,   // D0
        -1, 66, 67, 68, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // E0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1    // F0
    };

    return xlat[aVirtualKey];
}

void PopulateKeyMap(HKL aLayout)
{
    BYTE kbdState[256];
    memset(kbdState, 0, sizeof(kbdState));

    BYTE originalKbdState[256];
    // Bitfield with all shift states that have at least one dead-key.
    uint16_t shiftStatesWithDeadKeys = 0;
    // Bitfield with all shift states that produce any possible dead-key base
    // characters.
    uint16_t shiftStatesWithBaseChars = 0;

    ::GetKeyboardState(originalKbdState);

    int index = 0;

    // For each shift state gather all printable characters that are produced
    // for normal case when no any dead-key is active.

    for (ShiftState shiftState = 0; shiftState < 16; shiftState++) 
    {
        FillKbdState(kbdState, shiftState);

        for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) 
        {
            int32_t vki = GetKeyIndex(virtualKey);

            if (vki < 0) 
            {
                continue;
            }

            wchar_t uniChars[5];
            int32_t ret =
                ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)uniChars,
                    ArrayLength(uniChars), 0, hCurrentKeyboardLayout);

            // neither a dead-key nor there is no translation
            if (ret > 0)
            {
                if (ret == 1) 
                {
                    // dead-key can pair only with exactly one base character.
                    shiftStatesWithBaseChars |= (1 << shiftState);
                }


                {
                    index++;

                    uniChars[ret] = '\0';

                    CString key_name(uniChars);

                    unsigned int scanCode = MapVirtualKeyEx(virtualKey, MAPVK_VK_TO_VSC, hCurrentKeyboardLayout);

                    // because MapVirtualKey strips the extended bit for some keys
                    switch (virtualKey)
                    {
                    case VK_LEFT: case VK_UP: case VK_RIGHT: case VK_DOWN: // arrow keys
                    case VK_PRIOR: case VK_NEXT: // page up and page down
                    case VK_END: case VK_HOME:
                    case VK_INSERT: case VK_DELETE:
                    case VK_DIVIDE: // numpad slash
                    case VK_NUMLOCK:
                    {
                        scanCode |= KF_EXTENDED; // set extended bit
                        break;
                    }
                    }

                    if (false == key_name.IsEmpty())
                    {
                        SmartPtr<KeyCodeInfo> key = (new KeyCodeInfo)->template DetachObject<KeyCodeInfo>();

                        if (key)
                        {
                            key->nIndex = index;
                            key->sVKCode = virtualKey;
                            key->nScanCode = scanCode;

                            keyboard_map[tstring(key_name.GetBuffer())] = key;
                        }
                    }// End if
                }
            }
        }
    }

    ::SetKeyboardState(originalKbdState);
}