#include "MID/Mid_Def.h" #include #include #include #include #pragma comment(lib, "hid.lib") #pragma comment(lib, "setupapi.lib") /* * MID 层:介于逻辑与 DRI 之间,负责描述 HID 设备匹配与数据格式。 * 高密注释用于教学,逐条解释如何把固件接口映射为 Win32 API 调用。 */ /* 构造 NKRO 接口的匹配条件(Usage Page / Usage 固定) */ Mid_Struct_DeviceMatch Mid_Func_GetNkroMatch(const Mid_Struct_DeviceConfig& DeviceConfig) { Mid_Struct_DeviceMatch Match; Match.VendorId = DeviceConfig.VendorId; Match.ProductId = DeviceConfig.ProductId; Match.UsagePage = MID_CONST_USAGE_PAGE_NKRO; Match.Usage = MID_CONST_USAGE_NKRO; Match.Name = QStringLiteral("NKRO Keyboard Interface"); return Match; } /* 构造 Consumer 接口的匹配条件 */ Mid_Struct_DeviceMatch Mid_Func_GetConsumerMatch(const Mid_Struct_DeviceConfig& DeviceConfig) { Mid_Struct_DeviceMatch Match; Match.VendorId = DeviceConfig.VendorId; Match.ProductId = DeviceConfig.ProductId; Match.UsagePage = MID_CONST_USAGE_PAGE_CONSUMER; Match.Usage = MID_CONST_USAGE_CONSUMER; Match.Name = QStringLiteral("Consumer Interface"); return Match; } /* 构造 Vendor(状态镜像)接口的匹配条件 */ Mid_Struct_DeviceMatch Mid_Func_GetVendorMatch(const Mid_Struct_DeviceConfig& DeviceConfig) { Mid_Struct_DeviceMatch Match; Match.VendorId = DeviceConfig.VendorId; Match.ProductId = DeviceConfig.ProductId; Match.UsagePage = MID_CONST_USAGE_PAGE_VENDOR; Match.Usage = MID_CONST_USAGE_VENDOR; Match.Name = QStringLiteral("Vendor State Mirror Interface"); return Match; } /* * Mid_Func_FindHidInterface:按照匹配条件扫描系统中的 HID 接口。 * 步骤:1) 获取 HID GUID 2) SetupAPI 列举接口 3) CreateFile 查询属性 4) 命中后返回路径和报文长度。 */ bool Mid_Func_FindHidInterface( const Mid_Struct_DeviceMatch& Match, QString* p_DevicePath, quint16* p_InputLength, quint16* p_OutputLength) { /* Win32 提供的 HID GUID:列举所有 HID 接口都靠它 */ GUID HidGuid; HidD_GetHidGuid(&HidGuid); /* 构建“当前存在 + 暴露接口”的设备集合 */ HDEVINFO DeviceInfoSet = SetupDiGetClassDevsW(&HidGuid, nullptr, nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (DeviceInfoSet == INVALID_HANDLE_VALUE) { return false; } bool IsFound = false; SP_DEVICE_INTERFACE_DATA InterfaceData = {}; InterfaceData.cbSize = sizeof(InterfaceData); for (DWORD Index = 0; SetupDiEnumDeviceInterfaces(DeviceInfoSet, nullptr, &HidGuid, Index, &InterfaceData); ++Index) { /* Query 设备路径前先得到所需缓冲区长度 */ DWORD NeedLength = 0; SetupDiGetDeviceInterfaceDetailW(DeviceInfoSet, &InterfaceData, nullptr, 0, &NeedLength, nullptr); if (NeedLength == 0) { continue; } /* 申请一段缓冲区存储 SP_DEVICE_INTERFACE_DETAIL_DATA_W */ QByteArray Buffer(static_cast(NeedLength), 0); auto* p_Detail = reinterpret_cast(Buffer.data()); p_Detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W); if (!SetupDiGetDeviceInterfaceDetailW(DeviceInfoSet, &InterfaceData, p_Detail, NeedLength, nullptr, nullptr)) { continue; } /* 只需 0 访问权限即可读取属性 */ HANDLE HandleQuery = CreateFileW( p_Detail->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); if (HandleQuery == INVALID_HANDLE_VALUE) { continue; } HIDD_ATTRIBUTES Attributes = {}; Attributes.Size = sizeof(Attributes); PHIDP_PREPARSED_DATA p_Preparsed = nullptr; HIDP_CAPS Caps = {}; /* 通过 Attributes + Caps 对比 VID / PID / Usage Page / Usage */ const bool IsMatch = HidD_GetAttributes(HandleQuery, &Attributes) && HidD_GetPreparsedData(HandleQuery, &p_Preparsed) && (HidP_GetCaps(p_Preparsed, &Caps) == HIDP_STATUS_SUCCESS) && (Attributes.VendorID == Match.VendorId) && (Attributes.ProductID == Match.ProductId) && (Caps.UsagePage == Match.UsagePage) && (Caps.Usage == Match.Usage); if (p_Preparsed != nullptr) { HidD_FreePreparsedData(p_Preparsed); } CloseHandle(HandleQuery); if (!IsMatch) { continue; } /* 命中:写回设备路径及报文长度 */ if (p_DevicePath != nullptr) { *p_DevicePath = QString::fromWCharArray(p_Detail->DevicePath); } if (p_InputLength != nullptr) { *p_InputLength = Caps.InputReportByteLength; } if (p_OutputLength != nullptr) { *p_OutputLength = Caps.OutputReportByteLength; } IsFound = true; break; } /* 枚举结束释放句柄 */ SetupDiDestroyDeviceInfoList(DeviceInfoSet); return IsFound; } /* 调试输出:把字节数组格式化成“AA BB CC” */ QString Mid_Func_GetHexText(const QByteArray& ByteArray) { QStringList TextList; for (int Index = 0; Index < ByteArray.size(); ++Index) { TextList.append(QStringLiteral("%1") .arg(static_cast(ByteArray.at(Index)), 2, 16, QLatin1Char('0')) .toUpper()); } return TextList.join(QLatin1Char(' ')); } /* 把 Modifier 位图翻译成人类可读的组合 */ QString Mid_Func_GetModifierText(quint8 Modifier) { QStringList TextList; if (Modifier == 0) { return QStringLiteral("无"); } if ((Modifier & 0x01) != 0) TextList.append(QStringLiteral("Left Ctrl")); if ((Modifier & 0x02) != 0) TextList.append(QStringLiteral("Left Shift")); if ((Modifier & 0x04) != 0) TextList.append(QStringLiteral("Left Alt")); if ((Modifier & 0x08) != 0) TextList.append(QStringLiteral("Left GUI")); if ((Modifier & 0x10) != 0) TextList.append(QStringLiteral("Right Ctrl")); if ((Modifier & 0x20) != 0) TextList.append(QStringLiteral("Right Shift")); if ((Modifier & 0x40) != 0) TextList.append(QStringLiteral("Right Alt")); if ((Modifier & 0x80) != 0) TextList.append(QStringLiteral("Right GUI")); return TextList.join(QStringLiteral(", ")); } /* 键盘 Usage -> 中文/英文标签,方便 UI 展示 */ QString Mid_Func_GetKeyboardUsageText(quint16 Usage) { switch (Usage) { case 0x0053: return QStringLiteral("Num Lock"); case 0x0054: return QStringLiteral("小键盘 /"); case 0x0055: return QStringLiteral("小键盘 *"); case 0x0056: return QStringLiteral("小键盘 -"); case 0x0057: return QStringLiteral("小键盘 +"); case 0x0058: return QStringLiteral("小键盘 Enter"); case 0x0059: return QStringLiteral("小键盘 1"); case 0x005A: return QStringLiteral("小键盘 2"); case 0x005B: return QStringLiteral("小键盘 3"); case 0x005C: return QStringLiteral("小键盘 4"); case 0x005D: return QStringLiteral("小键盘 5"); case 0x005E: return QStringLiteral("小键盘 6"); case 0x005F: return QStringLiteral("小键盘 7"); case 0x0060: return QStringLiteral("小键盘 8"); case 0x0061: return QStringLiteral("小键盘 9"); case 0x0062: return QStringLiteral("小键盘 0"); case 0x0063: return QStringLiteral("小键盘 ."); case 0x00E0: return QStringLiteral("Left Ctrl"); case 0x00E1: return QStringLiteral("Left Shift"); case 0x00E2: return QStringLiteral("Left Alt"); case 0x00E3: return QStringLiteral("Left GUI"); case 0x00E4: return QStringLiteral("Right Ctrl"); case 0x00E5: return QStringLiteral("Right Shift"); case 0x00E6: return QStringLiteral("Right Alt"); case 0x00E7: return QStringLiteral("Right GUI"); default: return QStringLiteral("未知 HID Usage"); } } /* Consumer Usage -> UI 标签 */ QString Mid_Func_GetConsumerUsageText(quint16 Usage) { switch (Usage) { case 0x0000: return QStringLiteral("释放"); case 0x00E2: return QStringLiteral("Mute"); case 0x00E9: return QStringLiteral("Volume Up"); case 0x00EA: return QStringLiteral("Volume Down"); default: return QStringLiteral("未知 Consumer Usage"); } }