如何在Windows操作系统中将HID使用ID转换为虚拟密钥代码?

oxcyiej7  于 2023-04-07  发布在  Windows
关注(0)|答案(2)|浏览(241)

有没有一种方法可以将USB HID使用ID转换为Windows操作系统中的虚拟密钥代码?
例如,
HID使用ID 0x 04---〉虚拟密钥是0x 41(这是密钥A)
HID使用ID 0x 91---〉虚拟密钥为0xE 9(这是OEM特定密钥)
HID使用ID 0x 87---〉虚拟密钥为0xC 1(这是保留密钥代码)
...
我刚刚找到了一个virtual keys code table,但我找不到一个翻译表或一种有效地翻译它的方法。

zvokhttg

zvokhttg1#

没有API在HID使用ID和虚拟密钥代码之间进行转换。转换由设备驱动程序执行。
键盘的Usage ID在http://usb.org上的HID Usage Tables规范中发布(第10章“键盘/小键盘页(0x 07)”)。

xfyts7mz

xfyts7mz2#

由于历史原因,Windows使用I8042扫描码进行键盘输入(API依赖于扫描码)。扫描码由Windows Keyboard HID客户端驱动程序从内部使用USB HID键盘键并调用HidP_TranslateUsagesToI8042ScanCodes生成(单向hid usage -> scan code表在Hidparse.lib内部烘焙,通常不被用户代码使用。您可以找到此表为PDF here)。
代码如下:

std::vector<std::pair<uint32_t, uint16_t>> GetUsagesToScanCodes()
{
    std::vector<std::pair<uint32_t, uint16_t>> usagesToScanCodes;

    PHIDP_INSERT_SCANCODES insertFunc =
        [](PVOID context, PCHAR newScanCodes, ULONG length) -> BOOLEAN
    {
        auto usagesToScanCodes = reinterpret_cast<std::vector<std::pair<uint32_t, uint16_t>>*>(context);

        CHECK_LE(length, 3);

        uint16_t scanCode = 0;
        switch (length)
        {
        case 1:
            scanCode = MAKEWORD(newScanCodes[0] & 0x7f, 0);
            break;
        case 2:
            scanCode = MAKEWORD(newScanCodes[1], newScanCodes[0]);
            break;
        case 3:
            // The only known case is 0xe11d45 for Pause key. Map it to 0xe045 as Win32 does.
            scanCode = MAKEWORD(newScanCodes[2], 0xe0);
            break;
        }

        usagesToScanCodes->back().second = scanCode;

        return TRUE;
    };

    // Translate usages from HID_USAGE_PAGE_KEYBOARD page
    for (USAGE usage = 0; usage <= 0xff; ++usage)
    {
        HIDP_KEYBOARD_MODIFIER_STATE modifiers{};

        usagesToScanCodes.emplace_back(std::make_pair<uint32_t, uint16_t>(HID_USAGE_PAGE_KEYBOARD << 16 | usage, 0));

        NTSTATUS status = HidP_TranslateUsagesToI8042ScanCodes(
            &usage,
            1,
            HidP_Keyboard_Make,
            &modifiers,
            insertFunc,
            reinterpret_cast<PVOID>(&usagesToScanCodes));
        CHECK(status == HIDP_STATUS_SUCCESS || status == HIDP_STATUS_I8042_TRANS_UNKNOWN);

        // no mapping
        if (usagesToScanCodes.back().second == 0)
        {
            usagesToScanCodes.pop_back();
        }
    }

    // Additional scan codes that are mapped to HID_USAGE_PAGE_CONSUMER page
    // Looks like HidP_TranslateUsageAndPagesToI8042ScanCodes cannot be called from user-mode
    // So just add known buttons to the list:
    static struct
    {
        uint32_t usage;
        uint16_t scanCode;
    } consumer[] =
    {
        { 0x00b5, 0xe019 }, // Scan Next Track
        { 0x00b6, 0xe010 }, // Scan Previous Track
        { 0x00b7, 0xe024 }, // Stop
        { 0x00cd, 0xe022 }, // Play/Pause
        { 0x00e2, 0xe020 }, // Mute
        { 0x00e9, 0xe030 }, // Volume Increment
        { 0x00ea, 0xe02e }, // Volume Decrement
        { 0x0183, 0xe06d }, // AL Consumer Control Configuration
        { 0x018a, 0xe06c }, // AL Email Reader
        { 0x0192, 0xe021 }, // AL Calculator
        { 0x0194, 0xe06b }, // AL Local Machine Browser
        { 0x0221, 0xe065 }, // AC Search
        { 0x0223, 0xe032 }, // AC Home
        { 0x0225, 0xe069 }, // AC Back
        { 0x0226, 0xe068 }, // AC Forward
        { 0x0227, 0xe067 }, // AC Stop
        { 0x022a, 0xe066 }, // AC Refresh
        { 0x022a, 0xe06a }, // AC Previous Link
    };

    for (const auto& p : consumer)
    {
        usagesToScanCodes.emplace_back(std::make_pair(HID_USAGE_PAGE_CONSUMER << 16 | p.usage, p.scanCode));
    }


    return usagesToScanCodes;
}

它在我的系统上给出了这个列表:

(0x00070004, 0x001e)
(0x00070005, 0x0030)
(0x00070006, 0x002e)
(0x00070007, 0x0020)
(0x00070008, 0x0012)
(0x00070009, 0x0021)
(0x0007000a, 0x0022)
(0x0007000b, 0x0023)
(0x0007000c, 0x0017)
(0x0007000d, 0x0024)
(0x0007000e, 0x0025)
(0x0007000f, 0x0026)
(0x00070010, 0x0032)
(0x00070011, 0x0031)
(0x00070012, 0x0018)
(0x00070013, 0x0019)
(0x00070014, 0x0010)
(0x00070015, 0x0013)
(0x00070016, 0x001f)
(0x00070017, 0x0014)
(0x00070018, 0x0016)
(0x00070019, 0x002f)
(0x0007001a, 0x0011)
(0x0007001b, 0x002d)
(0x0007001c, 0x0015)
(0x0007001d, 0x002c)
(0x0007001e, 0x0002)
(0x0007001f, 0x0003)
(0x00070020, 0x0004)
(0x00070021, 0x0005)
(0x00070022, 0x0006)
(0x00070023, 0x0007)
(0x00070024, 0x0008)
(0x00070025, 0x0009)
(0x00070026, 0x000a)
(0x00070027, 0x000b)
(0x00070028, 0x001c)
(0x00070029, 0x0001)
(0x0007002a, 0x000e)
(0x0007002b, 0x000f)
(0x0007002c, 0x0039)
(0x0007002d, 0x000c)
(0x0007002e, 0x000d)
(0x0007002f, 0x001a)
(0x00070030, 0x001b)
(0x00070031, 0x002b)
(0x00070032, 0x002b)
(0x00070033, 0x0027)
(0x00070034, 0x0028)
(0x00070035, 0x0029)
(0x00070036, 0x0033)
(0x00070037, 0x0034)
(0x00070038, 0x0035)
(0x00070039, 0x003a)
(0x0007003a, 0x003b)
(0x0007003b, 0x003c)
(0x0007003c, 0x003d)
(0x0007003d, 0x003e)
(0x0007003e, 0x003f)
(0x0007003f, 0x0040)
(0x00070040, 0x0041)
(0x00070041, 0x0042)
(0x00070042, 0x0043)
(0x00070043, 0x0044)
(0x00070044, 0x0057)
(0x00070045, 0x0058)
(0x00070046, 0xe037)
(0x00070047, 0x0046)
(0x00070048, 0xe045)
(0x00070049, 0xe052)
(0x0007004a, 0xe047)
(0x0007004b, 0xe049)
(0x0007004c, 0xe053)
(0x0007004d, 0xe04f)
(0x0007004e, 0xe051)
(0x0007004f, 0xe04d)
(0x00070050, 0xe04b)
(0x00070051, 0xe050)
(0x00070052, 0xe048)
(0x00070053, 0x0045)
(0x00070054, 0xe035)
(0x00070055, 0x0037)
(0x00070056, 0x004a)
(0x00070057, 0x004e)
(0x00070058, 0xe01c)
(0x00070059, 0x004f)
(0x0007005a, 0x0050)
(0x0007005b, 0x0051)
(0x0007005c, 0x004b)
(0x0007005d, 0x004c)
(0x0007005e, 0x004d)
(0x0007005f, 0x0047)
(0x00070060, 0x0048)
(0x00070061, 0x0049)
(0x00070062, 0x0052)
(0x00070063, 0x0053)
(0x00070064, 0x0056)
(0x00070065, 0xe05d)
(0x00070066, 0xe05e)
(0x00070067, 0x0059)
(0x00070068, 0x0064)
(0x00070069, 0x0065)
(0x0007006a, 0x0066)
(0x0007006b, 0x0067)
(0x0007006c, 0x0068)
(0x0007006d, 0x0069)
(0x0007006e, 0x006a)
(0x0007006f, 0x006b)
(0x00070070, 0x006c)
(0x00070071, 0x006d)
(0x00070072, 0x006e)
(0x00070073, 0x0076)
(0x00070085, 0x007e)
(0x00070087, 0x0073)
(0x00070088, 0x0070)
(0x00070089, 0x007d)
(0x0007008a, 0x0079)
(0x0007008b, 0x007b)
(0x0007008c, 0x005c)
(0x00070090, 0x0072)
(0x00070091, 0x0071)
(0x00070092, 0x0078)
(0x00070093, 0x0077)
(0x00070094, 0x0076)
(0x000700e0, 0x001d)
(0x000700e1, 0x002a)
(0x000700e2, 0x0038)
(0x000700e3, 0xe05b)
(0x000700e4, 0xe01d)
(0x000700e5, 0x0036)
(0x000700e6, 0xe038)
(0x000700e7, 0xe05c)
(0x000700e9, 0xe05e)
(0x000700ea, 0xe05f)
(0x000700eb, 0xe063)
(0x000c00b5, 0xe019)
(0x000c00b6, 0xe010)
(0x000c00b7, 0xe024)
(0x000c00cd, 0xe022)
(0x000c00e2, 0xe020)
(0x000c00e9, 0xe030)
(0x000c00ea, 0xe02e)
(0x000c0183, 0xe06d)
(0x000c018a, 0xe06c)
(0x000c0192, 0xe021)
(0x000c0194, 0xe06b)
(0x000c0221, 0xe065)
(0x000c0223, 0xe032)
(0x000c0225, 0xe069)
(0x000c0226, 0xe068)
(0x000c0227, 0xe067)
(0x000c022a, 0xe066)
(0x000c022a, 0xe06a)

当你有一个扫描码,你可以转换扫描码到VK码通过活动键盘布局表(即嵌入在键盘布局dll文件-例如美国英语是kbdus.dll)。而这个VK码,你收到的WM_KEYDOWN等消息。
您可以通过调用设置了MAPVK_VSC_TO_VK_EX (3)标志的MapVirtualKeyEx来手动执行此scan code <-> VK code转换。
还有Windows SDK附带的hidusage.h。它包含一堆HID用法页面/ID,如果需要,可以在代码中作为常量使用。
至于I8042扫描码和HID使用之间的高效转换-您可以使用Chromium作者的dom_code_data.inc表。下面是我的示例代码。

相关问题