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



我想列举当前键盘布局支持的所有可能的按键组合的列表(包括虚拟按键代码、扫描代码及其Unicode值)。将远程用户输入映射到按键以模拟它们。

我希望有一个API,比如VC++的UCKeyTranslate(ObjectiveC),可以接受虚拟键代码和修饰符(ALT、SHIFT、CTRL),并为我提供扫描代码,但找不到类似的东西。

经过大量的研究和整整两天的时间,除了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)组合的键的扫描代码。有没有什么方法可以提供修饰符的组合作为函数的输入,这样我就可以生成所需的组合键?。

如有任何帮助,我们将不胜感激。提前谢谢。

最终得到了一个解决方案,引用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] = '';
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);
}

相关内容

最新更新