UWP中HidDevice.FromIdAsync仅Read模式可用,求解决方案及测试工具
HID设备UWP读写权限问题及替代测试方案咨询
问题描述
已确认HID设备具备读写能力(WPF应用中验证:一个端点仅支持读取,另一个支持读写),但在UWP中调用HidDevice.FromIdAsync打开设备时,仅FileAccessMode.Read模式能获取非null实例,ReadWrite模式始终返回null。已在Package.appxmanifest中配置多个Usage权限,问题仍未解决。同时咨询除SuperMUTT(350美元)外的其他HID测试方法。
代码实现
//This method will be called on the UI thread private async Task OpenDevice() { string aqs = HidDevice.GetDeviceSelector(0x0001, 0x0005, 0xAAAA, 0xBBBB); //Two Endpoints are returned here, one of which can only be opened by Read, and the other cannot be opened by Read or ReadWrite var devices = await DeviceInformation.FindAllAsync(aqs); foreach (var deviceInfo in devices) { var access = DeviceAccessInformation.CreateFromId(deviceInfo.Id); if (access.CurrentStatus == DeviceAccessStatus.Allowed) { var deviceWithReadWrite = await HidDevice.FromIdAsync(deviceInfo.Id, FileAccessMode.ReadWrite); //deviceWithReadWrite is null var deviceWithRead = await HidDevice.FromIdAsync(deviceInfo.Id, FileAccessMode.Read); //deviceWithRead is not null } } }
Package.appxmanifest权限配置
<Capabilities> <DeviceCapability Name="humaninterfacedevice"> <Device Id="any"> <Function Type="usage:0001 *"/> <Function Type="usage:0009 *"/> <Function Type="usage:FF00 *"/> <Function Type="usage:FFF0 *"/> <Function Type="usage:FF80 *"/> </Device> </DeviceCapability> </Capabilities>
HID报告描述符
0x08, // Usage 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 0x09, 0x05, // Usage (Game Pad) 0xA1, 0x01, // Collection (Application) 0x85, 0x01, // Report ID (1) 0x09, 0x30, // Usage (X) 0x09, 0x31, // Usage (Y) 0x09, 0x32, // Usage (Z) 0x09, 0x35, // Usage (Rz) 0x15, 0x00, // Logical Minimum (0) 0x26, 0xFF, 0x00, // Logical Maximum (255) 0x75, 0x08, // Report Size (8) 0x95, 0x04, // Report Count (4) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x09, 0x39, // Usage (Hat switch) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x07, // Logical Maximum (7) 0x35, 0x00, // Physical Minimum (0) 0x46, 0x3B, 0x01, // Physical Maximum (315) 0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter) 0x75, 0x04, // Report Size (4) 0x95, 0x01, // Report Count (1) 0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State) 0x65, 0x00, // Unit (None) 0x05, 0x09, // Usage Page (Button) 0x19, 0x01, // Usage Minimum (0x01) 0x29, 0x11, // Usage Maximum (0x11) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x01, // Logical Maximum (1) 0x75, 0x01, // Report Size (1) 0x95, 0x11, // Report Count (17) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) 0x09, 0x20, // Usage (0x20) 0x75, 0x03, // Report Size (3) 0x95, 0x01, // Report Count (1) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 0x09, 0x33, // Usage (Rx) 0x09, 0x34, // Usage (Ry) 0x15, 0x00, // Logical Minimum (0) 0x26, 0xFF, 0x00, // Logical Maximum (255) 0x75, 0x08, // Report Size (8) 0x95, 0x02, // Report Count (2) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) 0x09, 0x21, // Usage (0x21) 0x95, 0x36, // Report Count (54) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x85, 0x05, // Report ID (5) 0x09, 0x22, // Usage (0x22) 0x95, 0x1F, // Report Count (31) 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0x03, // Report ID (3) 0x0A, 0x21, 0x27, // Usage (0x2721) 0x95, 0x2F, // Report Count (47) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x06, 0x80, 0xFF, // Usage Page (Vendor Defined 0xFF80) 0x85, 0xE0, // Report ID (-32) 0x09, 0x57, // Usage (0x57) 0x95, 0x02, // Report Count (2) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0xC0, // End Collection 0x06, 0xF0, 0xFF, // Usage Page (Vendor Defined 0xFFF0) 0x09, 0x40, // Usage (0x40) 0xA1, 0x01, // Collection (Application) 0x85, 0xF0, // Report ID (-16) 0x09, 0x47, // Usage (0x47) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF1, // Report ID (-15) 0x09, 0x48, // Usage (0x48) 0x95, 0x3F, // Report Count (63) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF2, // Report ID (-14) 0x09, 0x49, // Usage (0x49) 0x95, 0x0F, // Report Count (15) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x85, 0xF3, // Report ID (-13) 0x0A, 0x01, 0x47, // Usage (0x4701) 0x95, 0x07, // Report Count (7) 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) 0x09, 0x00, // Usage (0x00) 0x85, 0x10, // Report ID (16) 0x15, 0x00, // Logical Minimum (0) 0x25, 0xFF, // Logical Maximum (-1) 0x35, 0x00, // Physical Minimum (0) 0x45, 0xFF, // Physical Maximum (-1) 0x75, 0x08, // Report Size (8) 0x95, 0x3F, // Report Count (63) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) 0x09, 0x20, // Usage (0x20) 0x85, 0xDD, // Report ID (-35) 0x75, 0x08, // Report Size (8) 0x95, 0x3F, // Report Count (63) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x85, 0x0F, // Report ID (15) 0x09, 0x22, // Usage (0x22) 0x95, 0x3F, // Report Count (63) 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0xC0, // End Collection
问题分析与解决方案
1. ReadWrite模式返回null的原因排查
(1)端点匹配问题
UWP的HID API会将设备的每个HID集合视为单独的设备实例,你需要确认遍历到的deviceInfo是否对应支持读写的端点。可以打印deviceInfo.Name或deviceInfo.Properties中的设备细节来区分两个端点,避免误操作只读端点。
(2)权限配置遗漏
UWP的HID权限需要精确匹配报告描述符中的Usage Page和Usage组合,仅通配符可能无法覆盖所有必要权限。建议补充以下精确匹配的权限:
<Function Type="usage:0001 0005"/> <!-- Game Pad --> <Function Type="usage:FF00 0020"/> <Function Type="usage:FF00 0021"/> <Function Type="usage:FF00 0022"/> <Function Type="usage:FF80 0057"/> <Function Type="usage:FFF0 0040"/> <Function Type="usage:FFF0 0047"/> <Function Type="usage:FFF0 0048"/> <Function Type="usage:FFF0 0049"/> <Function Type="usage:FFF0 4701"/>
同时确保Package.appxmanifest根节点添加了受限能力命名空间:
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" IgnorableNamespaces="uap mp rescap"
(3)设备独占访问冲突
如果有其他进程(如WPF测试应用)正在以读写模式占用设备,UWP会无法获取ReadWrite权限。测试时请关闭所有其他访问该设备的程序,再尝试打开。
(4)可写报告识别问题
UWP要求设备必须支持至少一个可写报告才能以ReadWrite模式打开。你可以在成功以Read模式打开设备后,通过deviceWithRead.OutputReportTypes属性确认是否识别到可写报告,若为空则说明UWP未正确解析设备的Output报告描述。
2. 替代SuperMUTT的HID测试方案
- 低成本硬件模拟:
- 用Arduino/ESP32开发板配合
hidapi库编写固件,自定义HID报告描述符和读写逻辑,成本仅几十元,完全满足测试需求。
- 用Arduino/ESP32开发板配合
- Windows内置工具:
- 设备管理器:查看HID设备属性中的报告描述符,确认端点和报告类型。
hidtest.exe(Windows SDK自带,路径通常为C:\Program Files (x86)\Windows Kits\10\bin\<版本>\x64):直接测试HID设备的读写功能。USB Device Viewer:详细查看USB设备的端点、接口和HID集合信息。
- 第三方免费工具:
- HID Explorer:可视化查看报告描述符,支持发送Output报告和接收Input报告。
- USBlyzer/Wireshark(配合USBPcap):捕获HID通信数据包,分析设备交互逻辑。
内容的提问来源于stack exchange,提问作者Victor
相关产品推荐
相关产品推荐

