【发布时间】:2021-08-27 16:46:35
【问题描述】:
我在从 C++ 桌面应用程序中的 Windows Precision 触摸板获取使用数据时遇到问题。我已经完成了以下工作:
- 注册原始输入
- 我正在接收包含原始数据的 WM_INPUT 消息
- 我可以调用
GetRawInputData()来返回RAWINPUT结构
我的下一步是尝试使用Hidpi.h 实用程序来确定我的触摸板支持哪些用途(HidP_GetCaps(), HidP_GetValueCaps(), etc...),然后获取它们的值。为了让事情顺利进行,我只是尝试打印 X、Y、提示开关、联系人 ID 和最大联系人计数)。
为了使用上述实用程序,我从文档中注意到我通常需要两件事:预解析数据 (PHIDP_PREPARSED_DATA) 和实际的输入或特征报告。
这就是事情开始出错的地方。我读过的所有内容都说我应该能够使用HidD_GetPreparsedData() 来获得第一项。我传递的 HID 设备句柄是 raw_data_buffer->header.hDevice,其中 raw_data_buffer 是我从 GetRawInputData() 填充的 PRAWINPUT 类型。不幸的是,这失败了。调用 GetLastError() 返回 ERROR_INVALID_HANDLE。我找不到任何解释...句柄不是 NULL 并且与我尝试读取的 HID 设备匹配。
我找到了另一种获取预解析数据的方法:
...
preparsed_data_buffer = (PHIDP_PREPARSED_DATA)malloc( pp_data_buf_sz );
GetRawInputDeviceInfo( raw_data_buffer->header.hDevice, RIDI_PREPARSEDDATA, preparsed_data_buffer, &pp_data_buf_sz );
...
据我所知,数据是正确的。然后我能够使用*_GetCaps() 和_*GetValueCaps() 来查看支持的用法。
11 Input Button Capabilites Found
Index: 0, IsRange: 0, UsagePage: 0X000D, Usage: 0X0042
Index: 1, IsRange: 0, UsagePage: 0X000D, Usage: 0X0047
Index: 2, IsRange: 0, UsagePage: 0X000D, Usage: 0X0042
Index: 3, IsRange: 0, UsagePage: 0X000D, Usage: 0X0047
Index: 4, IsRange: 0, UsagePage: 0X000D, Usage: 0X0042
Index: 5, IsRange: 0, UsagePage: 0X000D, Usage: 0X0047
Index: 6, IsRange: 0, UsagePage: 0X000D, Usage: 0X0042
Index: 7, IsRange: 0, UsagePage: 0X000D, Usage: 0X0047
Index: 8, IsRange: 0, UsagePage: 0X000D, Usage: 0X0042
Index: 9, IsRange: 0, UsagePage: 0X000D, Usage: 0X0047
Index: 10, IsRange: 0, UsagePage: 0X0009, Usage: 0X0001
17 Input Value Capabilites Found
Index: 0, IsRange: 0, UsagePage: 0X000D, Usage: 0X0051
Index: 1, IsRange: 0, UsagePage: 0X0001, Usage: 0X0030
Index: 2, IsRange: 0, UsagePage: 0X0001, Usage: 0X0031
Index: 3, IsRange: 0, UsagePage: 0X000D, Usage: 0X0051
Index: 4, IsRange: 0, UsagePage: 0X0001, Usage: 0X0030
Index: 5, IsRange: 0, UsagePage: 0X0001, Usage: 0X0031
Index: 6, IsRange: 0, UsagePage: 0X000D, Usage: 0X0051
Index: 7, IsRange: 0, UsagePage: 0X0001, Usage: 0X0030
Index: 8, IsRange: 0, UsagePage: 0X0001, Usage: 0X0031
Index: 9, IsRange: 0, UsagePage: 0X000D, Usage: 0X0051
Index: 10, IsRange: 0, UsagePage: 0X0001, Usage: 0X0030
Index: 11, IsRange: 0, UsagePage: 0X0001, Usage: 0X0031
Index: 12, IsRange: 0, UsagePage: 0X000D, Usage: 0X0051
Index: 13, IsRange: 0, UsagePage: 0X0001, Usage: 0X0030
Index: 14, IsRange: 0, UsagePage: 0X0001, Usage: 0X0031
Index: 15, IsRange: 0, UsagePage: 0X000D, Usage: 0X0056
Index: 16, IsRange: 0, UsagePage: 0X000D, Usage: 0X0054
3 Feature Value Capabilites Found
Index: 0, IsRange: 0, UsagePage: 0X000D, Usage: 0X0059
Index: 1, IsRange: 0, UsagePage: 0X000D, Usage: 0X0055
Index: 2, IsRange: 0, UsagePage: 0XFF00, Usage: 0X00C5
我找到了我所期望的一切。 X (x1/x30)、Y (x1/x31) 和 Contact ID (xD/x51) 应该在我的输入报告中,Tip Switch 是一个按钮,Max Contact Count (xD/x55) 应该在我的输入报告中功能价值报告。
接下来关注输入/特征报告,我遇到了最初的问题。 HidP_GetUsageValue() 的第 7 个参数是输入或功能报告的 PCHAR,但我不知道如何获取它们。 HidD_GetInputReport() 和 HidD_GetFeature() 都失败,出现我在尝试通过该 API 获取预解析数据时看到的相同的无效句柄错误。
在浏览了大概 10 个线程或示例后,我注意到有人使用他们的原始(未解析)数据来获取输入值:
HidP_GetUsageValue( HidP_Input, usages[ usage_idx ].page, 0, usages[ usage_idx ].usage, &val,
preparsed_data, (PCHAR)raw_data_buffer->data.hid.bRawData, hid_capabilities.InputReportByteLength );
这行得通。我可以从输入报告中获取 x、y 和联系人 ID……我猜是因为原始输入报告必须是原始数据的第一块。但是,我仍然无法使用 HidP_GetUsageValue( HidP_Feature... 获取功能报告,因为我不知道该报告在原始输入中的位置。
我花了 10 多个小时研究可能的原因和解决方法,但一无所获。出于我的应用程序的目的,我可能只使用输入报告值就可以逃脱,但肯定有我遗漏的东西。
6/14 更新:
@RbMm @psmears 感谢您的 cmets。我没有意识到使用RIDI_DEVICENAME 从GetRawInputDeviceInfo() 返回的是为我的设备创建文件所需的符号路径。我不需要使用 SetupApi 或 cfgmgr32 来检查所有设备接口。不过,我仍然无法为我的设备创建文件。由于我可以在枚举所有设备接口时看到我正在寻找的路径,因此我决定通过为每个接口创建一个新文件来进行测试,以排除路径格式/编码问题。这是我的输出:
CM_Get_Device_Interface_List() found device #1.
Path: \\?\HID#VID_1FD2&PID_6103&MI_00&Col02#9&885ad2f&0&0001#{4d1e55b2-f16f-11cf-88cb-001111000030}
CrateFile() worked.
CM_Get_Device_Interface_List() found device #2.
Path: \\?\HID#VID_046D&PID_C077#6&b39df8e&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
CreateFile() failed with error: 0x5
CM_Get_Device_Interface_List() found device #3.
Path: \\?\HID#VID_B404&PID_0101&MI_01&Col01#7&22822429&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
CreateFile() failed with error: 0x20
CM_Get_Device_Interface_List() found device #4.
Path: \\?\HID#VID_B404&PID_0101&MI_01&Col02#7&22822429&0&0001#{4d1e55b2-f16f-11cf-88cb-001111000030}
CreateFile() failed with error: 0x20
CM_Get_Device_Interface_List() found device #5.
Path: \\?\HID#VID_1FD2&PID_6103&MI_01#9&20493974&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
CrateFile() worked.
CM_Get_Device_Interface_List() found device #6.
Path: \\?\HID#VID_0424&PID_274C&MI_01#8&2d3dcce0&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
CrateFile() worked.
CM_Get_Device_Interface_List() found device #7.
Path: \\?\HID#VID_B404&PID_0101&MI_01&Col03#7&22822429&0&0002#{4d1e55b2-f16f-11cf-88cb-001111000030}\KBD
CreateFile() failed with error: 0x5
CM_Get_Device_Interface_List() found device #8.
Path: \\?\HID#VID_B404&PID_0101&MI_00#7&3a45b06e&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\KBD
CreateFile() failed with error: 0x5
CM_Get_Device_Interface_List() found device #9.
Path: \\?\HID#VID_1FD2&PID_6103&MI_00&Col01#9&885ad2f&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
CreateFile() failed with error: 0x20
CM_Get_Device_Interface_List() found device #10.
Path: \\?\HID#VID_05AC&PID_0265&MI_00&Col01#8&7741f1a&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
CrateFile() worked.
CM_Get_Device_Interface_List() found device #11.
Path: \\?\HID#VID_05AC&PID_0265&MI_00&Col02#8&7741f1a&0&0001#{4d1e55b2-f16f-11cf-88cb-001111000030}
CrateFile() worked.
CM_Get_Device_Interface_List() found device #12.
Path: \\?\HID#VID_05AC&PID_0265&MI_01&Col02#8&1f37ab5f&0&0001#{4d1e55b2-f16f-11cf-88cb-001111000030}
CrateFile() worked.
CM_Get_Device_Interface_List() found device #13.
Path: \\?\HID#VID_05AC&PID_0265&MI_01&Col03#8&1f37ab5f&0&0002#{4d1e55b2-f16f-11cf-88cb-001111000030}
CrateFile() worked.
CM_Get_Device_Interface_List() found device #14.
Path: \\?\HID#VID_05AC&PID_0265&MI_02#8&36fb37a4&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
CrateFile() worked.
CM_Get_Device_Interface_List() found device #15.
Path: \\?\HID#VID_05AC&PID_0265&MI_03#8&1323f9e2&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
CrateFile() worked.
CM_Get_Device_Interface_List() found device #16.
Path: \\?\HID#VID_05AC&PID_0265&MI_01&Col01#8&1f37ab5f&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
THIS is the device we want a file handle for. Path matches what we expect.
CreateFile() failed with error: 0x20
我要连接的设备(此处为#16)是触摸板。最后几个接口与同一个 Apple Trackpad 设备相关联,为它们创建一个文件是可行的。错误“ERROR_FILE_NOT_FOUND (0x2)”对我来说没有任何意义。
这是我创建文件的方式:
file_handle = CreateFile( comp_hid_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
根据 OPEN_ALWAYS 的 MS 文档:“如果指定的文件不存在并且是可写位置的有效路径,则该函数将创建一个文件并将最后一个错误代码设置为零。”这意味着这不是一个有效的可写位置,但它肯定会在枚举我的接口时出现。
我是否无法将文件打开到设备,因为它只是使用 WM_INPUT 消息发送了原始输入?
【问题讨论】:
-
我认为您可能需要使用
CreateFile(device_path)来获取句柄。不确定您的设备路径是什么。 -
@JimRhodes 我考虑过走这条路。我几乎认为我的句柄可能是正确的,但文件可能已关闭/锁定?
-
HidD_GetPreparsedData需要文件句柄(它必须是打开和关闭的)。raw_data_buffer->header.hDevice不是文件句柄当然对HidD_GetPreparsedData无效 -
@RbMm 感谢您的捕获和以下示例!我最终尝试了以下directions 并且可以枚举我所有连接的设备。您知道我如何能够以编程方式确定是哪一个生成了我的 WM_INPUT 消息吗?例如,我不想获取每个隐藏文件句柄的预解析数据,并与我从 raw_data_buffer->header.hDevice 获得的数据进行比较...
-
@DannyLovell - 你需要在
pRawInputDeviceList->hDevice上调用GetRawInputDeviceInfoExW和RIDI_DEVICENAME- 它返回字符串 - 接口名称 - 如果CM_Get_Device_Interface_ListW返回的字符串与一个完全匹配(我认为使用CM_ api更简单当然更有效的比较 Setup api(内部调用 CM_ api)。示例 - stackoverflow.com/questions/67028399/…