c# - Windows HID USB 设备 C# API 替代方案
问题描述
我在为自制键盘编写测试应用程序(WPF、C#、.NET 4.7)时遇到了一个问题。键盘通过 USB 连接并显示为键盘类型的 HID 设备。现在我想向键盘发送一些数据(配置、背光信息……)。
在键盘端有一个 ARM 微控制器,它使用键盘的 HID-Report-Descriptor 初始化自身,其中包含一个额外的 OUTPUT 标签,用于将一些配置数据发送到键盘。(4字节)
以下是 MCU 的当前报告描述符:
__ALIGN_BEGIN static uint8_t
JKP_HID_ReportDesc_FS[USBD_JKP_HID_REPORT_DESC_SIZE] __ALIGN_END =
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs) ; Modifier Byte (1 byte)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x03, // INPUT (Cnst,Var,Abs) ; Reserved Byte (1 byte)
0x95, 0x05, // REPORT_COUNT (5)
0x75, 0x01, // REPORT_SIZE (1)
0x05, 0x08, // USAGE_PAGE (LEDs)
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
0x29, 0x05, // USAGE_MAXIMUM (Kana)
0x91, 0x02, // OUTPUT (Data,Var,Abs) ; LED Report
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x03, // REPORT_SIZE (3)
0x91, 0x03, // OUTPUT (Cnst,Var,Abs) ; LED Report Padding (LEDs 1 byte)
0x95, USBD_JKP_HID_REPORT_KEY_ROLLOVER, // REPORT_COUNT (USBD_JKP_HID_REPORT_KEY_ROLLOVER)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs) ; Key Arrays (6 bytes)
0x06, 0x00, 0xff, // USAGE_PAGE(Vendor Defined Page 1)
0x09, 0x01, // USAGE(Venor Usage 1)
0x15, 0x00, // LOGICAL_MINIMUM(0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM(255)
0x75, 0x08, // REPORT_SIZE(8)
0x95, 0x04, // REPORT_COUNT(4)
0x91, 0x02, // OUTPUT(Data,Var,Abs) ; JKP Config Info (4 bytes)
0xc0 // END_COLLECTION
};
在 PC 端,我有一个 WPF 应用程序,用 C# 编写并使用 Windows.Devices.HumanInterfaceDevice API。
这是 WPF 应用程序的代码:
Console.WriteLine("Init HID Device Write...");
byte[] data = new byte[] { 0x00, 0xaa, 0xaa, 0xaa, 0xaa};
string device_selector = HidDevice.GetDeviceSelector(0x01, 0x06, 1155, 1024);
var devices = await DeviceInformation.FindAllAsync(device_selector);
if (devices.Any())
{
Console.WriteLine("HID Devices Found: " + devices.Count);
DeviceInformation devinfo = devices[0];
Console.WriteLine($"HID Selected Device Info: ID: {devinfo.Id}, Name: {devinfo.Name}");
HidDevice device = await HidDevice.FromIdAsync(devinfo.Id, Windows.Storage.FileAccessMode.ReadWrite);
if (device != null)
{
Console.WriteLine("HID Device writing...");
HidOutputReport report = device.CreateOutputReport(2);
DataWriter dataWriter = new DataWriter();
dataWriter.WriteBytes(data);
report.Data = dataWriter.DetachBuffer();
await device.SendOutputReportAsync(report);
}
else
{
Console.WriteLine("Failed to open HID device.");
}
}
else
{
Console.WriteLine("No devices found.");
}
尽管找到了键盘设备,但 Windows.Devices.HumanInterfaceDevice API 会阻止与键盘类型 HID 设备的所有连接。
是否有支持此类连接的 Windows API 替代方案?
我已经尝试过 HIDSharp 和 hidlibrary。另外,我不想使用 USB 复合设备。
提前致谢。
解决方案
您无法在 Windows 上使用键盘使用(使用 0x01 使用页面和 0x06 使用 ID)与 HID 设备通信,因为它们是由 RIM(原始输入管理器)以独占模式打开的。这是针对键盘记录器的安全措施。请参阅https://docs.microsoft.com/windows-hardware/drivers/hid/hid-architecture#hid-clients-supported-in-windows
作为一种解决方法,我可以建议在 HID 描述符的 0xFF00 使用页面(特定于供应商)中添加另一个顶级集合,并在其中发送\接收您的自定义数据。这样,系统 HID 驱动程序将创建两个设备接口 - 一个用于键盘(您无法读取/写入),另一个用于您的自定义设备使用(可以从应用程序中自由使用)。请参阅https://docs.microsoft.com/windows-hardware/drivers/hid/top-level-collections
这就是 Logitech Unifying Receiver 在 Windows 设备管理器中的使用情况页面 0xFFBC 和使用情况 0x0088 供应商定义设备的显示方式:
推荐阅读
- php - PHP:获取 ICU 版本
- spring - The bean 'dataSource', defined in BeanDefinition defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$H
- python - 从另一个文件继承一个类会产生错误
- visual-studio-code - VS Code:如何使所有文本加粗,除了评论?
- android - 导航回上一个活动
- python - 最重要的字符提取
- mocha.js - 从 promise 返回的数组解构未按预期工作
- batch-file - 使用增量计数器查找和替换文本
- javascript - 如何从开始和结束匹配并替换javascript中的字符串
- node.js - 如何使用 exits 和 actions2 在sails 1.0 中设置动态viewTemplatePath?