{"id":106306,"date":"2022-03-03T07:00:00","date_gmt":"2022-03-03T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=106306"},"modified":"2022-03-03T06:47:56","modified_gmt":"2022-03-03T14:47:56","slug":"20220303-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220303-00\/?p=106306","title":{"rendered":"Filtering out fake keyboards from the GetRawInputDeviceList function"},"content":{"rendered":"<p>Last time, we used the <a title=\"How can I detect whether the system has a keyboard attached? On the GetRawInputDeviceList function\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220302-00\/?p=106303\"> <code>Get\u00adRaw\u00adInput\u00adDevice\u00adList<\/code> to enumerate all the input devices<\/a>, with a focus on keyboards. You may notice that on some systems, the function reports a lot of phantom keyboards. What&#8217;s up with that?<\/p>\n<p>The phantom keyboards are devices that report themselves to HID as keyboard devices, even though they aren&#8217;t keyboards in a traditional sense. They might be a separate numeric keypad, or buttons on the chassis for volume or brightness control that report themselves as keyboard keys. You can see all the keyboard devices in Device Manager under <i>Keyboards<\/i>, including the phantoms. Can we filter out the phantoms?<\/p>\n<p>I don&#8217;t know, but I&#8217;m going to try.<\/p>\n<pre>bool SmellsLikeARealKeyboard(HANDLE hDevice)\r\n{\r\n    RID_DEVICE_INFO info;\r\n    UINT size = sizeof(info);\r\n    UINT actualSize = GetRawInputDeviceInfoW(\r\n        hDevice, RIDI_DEVICEINFO, &amp;info, &amp;size);\r\n    if (actualSize == (UINT)-1 || actualSize &lt; sizeof(info))\r\n    {\r\n        \/\/ Weird failure.\r\n        return false;\r\n    }\r\n    assert(info.dwType == RIM_TYPEKEYBOARD);\r\n    return info.keyboard.dwNumberOfKeysTotal &gt;= 30;\r\n}\r\n<\/pre>\n<p>I&#8217;m going to say that a keyboard device smells like a real keyboard if it has at least 30 keys. This will rule out most devices that pretend to be a keyboard just so they can provide a handful of hardware buttons. (There is also a field that gives the keyboard type, but I&#8217;ve found that to be largely useless. One of my real keyboards reports as <i>Unknown<\/i>, and I have a phantom keyboard that reports as <i>Generic 101<\/i>.)<\/p>\n<p>We can use this function to try to filter out phantom keyboards:<\/p>\n<pre>int main(int argc, char** argv)\r\n{\r\n  auto devices = <a title=\"How can I detect whether the system has a keyboard attached? On the GetRawInputDeviceList function\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220302-00\/?p=106303\">GetRawInputDevices<\/a>();\r\n  int mouseCount = 0;\r\n  int keyboardCount = 0;\r\n  int otherCount = 0;\r\n  for (auto const&amp; device : devices) {\r\n    switch (device.dwType)\r\n    {\r\n    case RIM_TYPEKEYBOARD:\r\n      <span style=\"color: blue;\">if (SmellsLikeARealKeyboard(device.hDevice))<\/span> {\r\n        keyboardCount++;\r\n      }\r\n      break;\r\n    case RIM_TYPEMOUSE: mouseCount++; break;\r\n    default: otherCount++; break;\r\n    }\r\n  }\r\n  printf(\"There are %d keyboards, %d mice, and %d other things\\n\",\r\n         keyboardCount, mouseCount, otherCount);\r\n  return 0;\r\n}\r\n<\/pre>\n<p>It helps a little. Some of the phantom keyboards disappear. But at least on my machine, there&#8217;s one phantom keyboard left: It describes itself as a <i>Virtual HID Framework (VHF) HID device<\/i>. This appears to be a <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows-hardware\/drivers\/hid\/virtual-hid-framework--vhf-\"> facility for creating virtual devices<\/a>.<\/p>\n<p>I don&#8217;t know enough about Windows device management to find an easy way to filter out these virtual keyboards. Even <a href=\"https:\/\/docs.microsoft.com\/uwp\/api\/Windows.Devices.Input.KeyboardCapabilities.KeyboardPresent\"> KeyboardCapabilities.KeyboardPresent<\/a> appears to be faked out by them. Maybe somebody can pick up the ball here.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Maybe look to see if it has any keys.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-106306","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Maybe look to see if it has any keys.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106306","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=106306"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106306\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=106306"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=106306"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=106306"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}