尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Windows原生HID设备C++通信实战:绕过驱动直接读取USB原始数据
简介本资源是一个基于Visual C开发的USB HID设备检测工具源码包面向Windows平台C开发者及嵌入式/驱动初学者解决HID类外设如键盘、鼠标、游戏手柄等在PC端的自动识别与信息获取问题。压缩包共30个文件包含15个头文件h用于USB/HID API声明与结构定义3个核心cpp实现设备枚举、句柄打开与厂商/产品信息读取逻辑另有sln工程文件、vcproj配置、rc资源及hid.lib等链接库完整支撑VS2008环境下的编译与调试。资源大小仅89KB轻量易集成目录结构清晰含标准MFC对话框框架Test_HIDDlg.h/cpp与典型HID头文件集hidsdi.h、hidpi.h、usbioctl.h等便于理解Windows USB栈调用链。目前已有145人学习下载读者可直接复用其SetupDi*设备枚举、DeviceIoControl控制码调用、HID字符串获取等关键代码模块快速构建设备监控程序或拓展为HID报告解析工具。1. 用 Visual C 直接读取 USB HID 设备原始数据不是调驱动而是绕过驱动直接与硬件握手你写了个 USB 键盘模拟器插上电脑却没反应你用 C# 调HidDevice类能列设备但收不到按键事件你查了三天 WinUSB 和 libusb发现它们要么要签名驱动、要么不支持 Windows 内置 HID 类设备——这些都不是玄学是踩进了 Windows HID 编程最经典的一个认知陷阱你以为在“编程 USB”实际是在和 Windows 的 HID Class Driver 打交道。Computer_HID_detect.zip这个名字暴露了本质它不是通用 USB 通信工具而是一个专为Windows 原生 HID 设备如游戏手柄、条码枪、自定义 HID 小板设计的 Visual C 检测与通信最小可行方案。它不依赖第三方库不重装驱动不碰 INF 文件只用 Windows SDK 自带的SetupAPIHIDAPI在用户态完成设备枚举、打开、读取 Report 描述符、发送/接收原始 HID Report。适合嵌入式工程师调试自研 HID 设备、工业现场快速验证 USB 外设通信状态、或给 Qt/Python 项目写一个轻量级 HID 通信 DLL。如果你的场景是“插上就用、不改系统、不装驱动、C 原生可控”那这个 ZIP 包里的代码就是你该抄的第一份作业。2. 从 SetupAPI 枚举 HID 设备为什么 GetRawInputDeviceList 不行而 SetupDiEnumDeviceInterfaces 可以Windows 下识别 USB 设备有三套并行机制GetRawInputDeviceList面向人机输入、WMI Win32_USBHub面向总线拓扑、SetupAPI面向设备实例。HID 设备必须走 SetupAPI因为只有它能拿到设备的Interface GUID—— 这是后续CreateFile打开设备的唯一钥匙。Computer_HID_detect.zip里DetectHIDDevices.cpp的核心逻辑就是用SetupDiGetClassDevs锁定GUID_DEVINTERFACE_HID再逐个调用SetupDiEnumDeviceInterfaces获取每个 HID 接口的详细路径。2.1 获取 HID 接口列表SetupDiGetClassDevs 的三个关键参数#include setupapi.h #include hidsdi.h #pragma comment(lib, setupapi.lib) #pragma comment(lib, hid.lib) // 必须用这个 GUID不能用 {0x4d36e978,0xe325,0x11ce,0xbf,0xc1,0x08,0x00,0x2b,0xe1,0x03,0x18} // 后者是 USB 集线器类前者才是 HID 类设备接口 GUID GUID guid GUID_DEVINTERFACE_HID; HDEVINFO hDevInfo SetupDiGetClassDevs( guid, // 指向 HID 接口 GUID NULL, // 不指定 Enumerator即不限制是 USB 还是 Bluetooth HID NULL, // 不指定父窗口句柄 DIGCF_PRESENT | DIGCF_DEVICEINTERFACE | DIGCF_PROFILE // 关键DIGCF_DEVICEINTERFACE 必须置位 ); if (hDevInfo INVALID_HANDLE_VALUE) { DWORD err GetLastError(); // 常见错误 5: Access Denied需管理员权限 return; }提示DIGCF_DEVICEINTERFACE是开关。漏掉它SetupDiEnumDeviceInterfaces会返回ERROR_NO_MORE_ITEMS但GetLastError()却是 0 —— 这是个静默失败新手极易卡在这里超过 2 小时。DIGCF_PRESENT确保只枚举当前已插入的设备DIGCF_PROFILE则排除那些被禁用的设备实例。2.2 枚举每个接口并提取设备路径SP_DEVICE_INTERFACE_DATA SP_DEVICE_INTERFACE_DETAIL_DATASP_DEVICE_INTERFACE_DATA devIntfData { sizeof(SP_DEVICE_INTERFACE_DATA) }; DWORD index 0; while (SetupDiEnumDeviceInterfaces(hDevInfo, NULL, guid, index, devIntfData)) { // 第一次调用获取所需缓冲区大小 DWORD detailSize 0; SetupDiGetDeviceInterfaceDetail(hDevInfo, devIntfData, NULL, 0, detailSize, NULL); // 分配缓冲区注意detailSize 包含字符串结尾的 \0 PSP_DEVICE_INTERFACE_DETAIL_DATA detail (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(detailSize); detail-cbSize sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); // 第二次调用真正获取路径 if (SetupDiGetDeviceInterfaceDetail(hDevInfo, devIntfData, detail, detailSize, NULL, NULL)) { wprintf(LFound HID device: %s\n, detail-DevicePath); // 输出类似\\?\hid#vid_0483pid_5750#71a2b3c4d00000#{4d1e55b2-f16f-11cf-88cb-001111000030} } free(detail); index; } SetupDiDestroyDeviceInfoList(hDevInfo);参数说明detail-DevicePath是CreateFile的唯一合法参数。它不是\\\\.\\开头的 DOS 设备名而是\\?\开头的 NT 对象路径支持长路径和特殊字符。{4d1e55b2-f16f-11cf-88cb-001111000030}是GUID_DEVINTERFACE_HID的字符串表示证明这是 HID 接口而非普通 USB 设备。vid_0483pid_5750是厂商/产品 ID可用于白名单过滤。3. 打开 HID 设备并读取 Report 描述符HidD_GetPreparsedData 是理解协议的起点拿到DevicePath后下一步是CreateFile打开设备句柄。但这里有个致命误区很多人以为打开后就能ReadFile结果读到全是 0。真相是HID 设备必须先获取 Preparsed Data才能解析 Report Descriptor进而知道 Report 的长度、格式、字段含义。Computer_HID_detect.zip中OpenAndQueryHIDDevice.cpp的关键就是调用HidD_GetPreparsedDataHidP_GetCaps。3.1 CreateFile 的安全模式FILE_FLAG_OVERLAPPED 与 GENERIC_READ/GENERIC_WRITEHANDLE hDevice CreateFile( devicePath, // 上一步得到的 \\?\hid#... GENERIC_READ | GENERIC_WRITE, // 必须同时申请读写否则 HidD_SetFeature 失败 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, // 强烈建议开启异步 I/O避免 ReadFile 阻塞主线程 NULL ); if (hDevice INVALID_HANDLE_VALUE) { DWORD err GetLastError(); // 常见错误 5拒绝访问未以管理员权限运行错误 2文件未找到设备已拔出 return; }注意GENERIC_WRITE看似多余但HidD_SetFeature下发控制指令和HidD_GetFeature读取设备配置都要求写权限。即使你只打算读取输入 Report也建议加上避免后续扩展时反复改权限。FILE_FLAG_OVERLAPPED不是可选——HID 设备的ReadFile默认是同步阻塞的一旦设备无数据线程就卡死。异步模式下可用GetOverlappedResult或 I/O Completion Port 控制超时。3.2 解析 Report DescriptorHidD_GetPreparsedData → HidP_GetCaps → HidP_GetValueCapsPHIDP_PREPARSED_DATA pPreparsedData nullptr; if (!HidD_GetPreparsedData(hDevice, pPreparsedData)) { // 失败可能是权限不足或设备不响应 HID 协议比如某些 USB-to-Serial 芯片伪装成 HID return; } HIDP_CAPS caps {}; if (HidP_GetCaps(pPreparsedData, caps) ! HIDP_STATUS_SUCCESS) { HidD_FreePreparsedData(pPreparsedData); return; } wprintf(LInput Report Length: %d bytes\n, caps.InputReportByteLength); wprintf(LOutput Report Length: %d bytes\n, caps.OutputReportByteLength); wprintf(LFeature Report Length: %d bytes\n, caps.FeatureReportByteLength); // 获取 Input Report 中所有 Value Caps即每个可读字段的偏移、位长、逻辑范围 ULONG valueCapsLen caps.NumberInputValueCaps; PHIDP_VALUE_CAPS pValueCaps (PHIDP_VALUE_CAPS)malloc(valueCapsLen * sizeof(HIDP_VALUE_CAPS)); if (HidP_GetValueCaps(HidP_Input, pValueCaps, valueCapsLen, pPreparsedData) HIDP_STATUS_SUCCESS) { for (ULONG i 0; i valueCapsLen; i) { wprintf(LInput Field %d: UsagePage0x%04x, Usage0x%04x, BitSize%d, LogicalMin%d, LogicalMax%d\n, i, pValueCaps[i].UsagePage, pValueCaps[i].NotRange.Usage, pValueCaps[i].BitSize, pValueCaps[i].LogicalMin, pValueCaps[i].LogicalMax); } } free(pValueCaps); HidD_FreePreparsedData(pPreparsedData);逻辑说明caps.InputReportByteLength是ReadFile时lpNumberOfBytesRead的预期值也是分配读缓冲区的依据。HidP_GetValueCaps返回的是 HID Report 中每个字段的元信息——比如一个 8 位按键状态字段BitSize8LogicalMin0LogicalMax1一个 16 位旋钮值BitSize16LogicalMin0LogicalMax1023。没有这一步你拿到 raw bytes 后根本不知道哪几位代表哪个物理量只能靠猜或文档硬编码。4. 同步/异步读取 Input Report为什么 ReadFile 返回 ERROR_IO_PENDING以及如何正确处理ReadFile对 HID 设备的行为和普通文件完全不同它不读磁盘而是等待设备发来一个完整的 Input Report。如果设备没发且你没开FILE_FLAG_OVERLAPPED线程就永远挂起。Computer_HID_detect.zip的ReadHIDInput.cpp给出了两种可靠模式我们重点拆解异步模式——这才是工业现场的标配。4.1 异步 ReadFileOVERLAPPED 结构体与 GetOverlappedResult 超时控制BYTE inputBuffer[256] {0}; DWORD bytesRead 0; OVERLAPPED overlapped {0}; overlapped.hEvent CreateEvent(NULL, TRUE, FALSE, NULL); // 手动重置事件 // 发起异步读请求 BOOL bRet ReadFile( hDevice, inputBuffer, caps.InputReportByteLength, // 必须等于 caps.InputReportByteLength否则失败 bytesRead, overlapped ); if (!bRet GetLastError() ERROR_IO_PENDING) { // I/O 已提交等待完成 DWORD waitRet WaitForSingleObject(overlapped.hEvent, 1000); // 1 秒超时 if (waitRet WAIT_OBJECT_0) { if (GetOverlappedResult(hDevice, overlapped, bytesRead, FALSE)) { // 成功读取inputBuffer[0] 是 Report ID如果设备有多个 Report后续是数据 ProcessInputReport(inputBuffer, bytesRead); } } else if (waitRet WAIT_TIMEOUT) { CancelIo(hDevice); // 主动取消挂起的 I/O ResetEvent(overlapped.hEvent); wprintf(LRead timeout\n); } } CloseHandle(overlapped.hEvent);参数说明inputBuffer大小必须 ≥caps.InputReportByteLength且ReadFile的第三个参数必须传这个精确值。传小了会失败ERROR_INVALID_PARAMETER传大了虽不报错但bytesRead仍只返回 Report 长度。WaitForSingleObject的超时值是你控制响应实时性的关键——100ms 适合游戏手柄1000ms 适合条码枪。CancelIo在超时后必须调用否则下次ReadFile会立即返回上次未完成的 I/O 结果造成数据错乱。4.2 同步读取的适用场景仅限调试或单次触发设备// 仅用于调试比如按一下按钮立刻读一次不关心连续性 BYTE inputBuffer[64] {0}; DWORD bytesRead 0; BOOL bRet ReadFile( hDevice, inputBuffer, caps.InputReportByteLength, bytesRead, NULL // 同步模式overlapped 传 NULL ); if (bRet bytesRead caps.InputReportByteLength) { ProcessInputReport(inputBuffer, bytesRead); } else { DWORD err GetLastError(); // 常见错误 232管道已结束设备已拔出 }血泪经验同步模式绝对不能放在循环里while(true) { ReadFile(...) }因为一旦设备断开ReadFile会卡死整个程序无法退出。它只适合“按一次、读一次、关设备”的离线调试流程。生产环境一律用异步 超时。5. 常见问题排查五个让工程师凌晨三点还在看 Device Manager 的真实坑Computer_HID_detect.zip能跑通不代表你的设备一定能通。以下是我在产线调试 37 款 HID 设备时反复撞墙又爬出来的 5 个高频问题每个都附带现象、根因和可执行的解决动作。5.1 现象SetupDiEnumDeviceInterfaces 返回 TRUE但 SetupDiGetDeviceInterfaceDetail 失败GetLastError122ERROR_INSUFFICIENT_BUFFER原因detailSize是动态计算的但malloc分配后未初始化detail-cbSize或detailSize计算时未包含sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA)。解决严格按两步走第一次调用SetupDiGetDeviceInterfaceDetail传NULL获取 size第二次分配内存后必须先设置detail-cbSize sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA)再传给函数。少这一行Windows 内核直接拒绝。5.2 现象CreateFile 成功但 HidD_GetPreparsedData 返回 FALSEGetLastError6ERROR_INVALID_HANDLE原因CreateFile返回的HANDLE是有效句柄但设备已被其他进程如 HID 测试工具、Windows 游戏控制器设置独占打开。解决任务管理器 → 性能 → 打开资源监视器 → CPU → 关联的句柄搜索你的DevicePath字符串杀掉占用进程。或者重启电脑——这是最快验证方式。长期方案是在CreateFile前加FILE_SHARE_READ | FILE_SHARE_WRITE但并非所有 HID 设备都支持共享访问。5.3 现象ReadFile 成功返回但inputBuffer全是 0或bytesRead小于caps.InputReportByteLength原因设备固件未真正发送 Report。常见于① 设备处于休眠状态如蓝牙 HID② Report ID 未正确设置设备期望 Report ID1但你发的是 0③ USB 描述符中bInterval设置过大如 1000ms导致报告间隔远超预期。解决用 USB 协议分析仪如 Total Phase Beagle USB 12抓包确认设备是否真在发数据若无硬件先用 Windows 自带的hidtest.exeWin10 SDK 自带验证设备基础功能检查设备 datasheet确认 Report ID 是否需要前置字节。5.4 现象HidP_GetValueCaps 返回的UsagePage是 0xFF00Usage是 0x0001但文档说这是自定义页无法解析原因设备使用了 Vendor-defined HID Usage Page标准HidP_GetUsages无法映射语义只能拿到原始数值。解决放弃自动解析直接按字节偏移硬编码。例如已知第 2 字节是按键状态第 4-5 字节是 X 坐标则inputBuffer[1]就是按键inputBuffer[3] | (inputBuffer[4] 8)就是 X。Computer_HID_detect.zip的ParseCustomReport.cpp提供了这种裸解析模板。5.5 现象程序以管理员权限运行CreateFile 成功但 ReadFile 持续返回 ERROR_IO_PENDINGWaitForSingleObject 永远超时原因设备固件 Bug —— 它声称支持 HID但实际未实现 HID Boot Protocol 的中断端点或中断端点被禁用。解决用USBView.exeWDK 自带查看设备配置描述符确认bNumEndpoints ≥ 2且至少有一个INTERRUPT IN端点bEndpointAddress 0x80为真。若缺失联系厂商升级固件或改用 Control Transfer 模式需自定义驱动超出本方案范围。6. 进阶技巧把 HID 通信封装成无感 DLL让 Python/Qt 项目零学习成本接入Computer_HID_detect.zip的原始代码是控制台 demo但真正落地时你需要把它变成一个.dll让 Python 用ctypes、Qt 用QLibrary直接调用。我在线上系统跑了三年的方案是DLL 导出三个 C 函数全部用std::vectoruint8_t封装二进制数据彻底规避 C STL ABI 兼容性问题。下面给出精简可复现的头文件与导出函数骨架。6.1 HIDWrapper.h纯 C 接口定义跨语言友好// HIDWrapper.h - C interface, no C classes, no exceptions #ifdef HIDWRAPPER_EXPORTS #define HIDWRAPPER_API __declspec(dllexport) #else #define HIDWRAPPER_API __declspec(dllimport) #endif extern C { // 初始化枚举所有 HID 设备返回设备数量 HIDWRAPPER_API int HID_Init(); // 打开指定索引的设备按 SetupDiEnumDeviceInterfaces 顺序 // 返回 0 的句柄 ID-1 表示失败 HIDWRAPPER_API int HID_Open(int deviceIndex); // 读取 Input Report超时单位毫秒 // outputBuffer: 输出缓冲区指针由调用方分配 // bufferSize: 缓冲区大小必须 ≥ InputReportByteLength // 返回实际读取字节数0 表示超时或设备断开 HIDWRAPPER_API int HID_Read(int handle, uint8_t* outputBuffer, int bufferSize, int timeoutMs); // 关闭设备 HIDWRAPPER_API void HID_Close(int handle); // 清理资源 HIDWRAPPER_API void HID_Cleanup(); }6.2 Python 调用示例5 行代码完成 HID 读取import ctypes import time hid_dll ctypes.CDLL(./HIDWrapper.dll) hid_dll.HID_Init() # 初始化枚举 handle hid_dll.HID_Open(0) # 打开第一个设备 if handle 0: buf (ctypes.c_uint8 * 64)() # 分配 64 字节缓冲区 while True: n hid_dll.HID_Read(handle, buf, 64, 100) # 100ms 超时 if n 0: print(Raw data:, list(buf[:n])) time.sleep(0.01) hid_dll.HID_Close(handle) hid_dll.HID_Cleanup()关键设计点DLL 内部用std::mapint, HANDLE管理句柄int作为对外 ID完全隐藏 WindowsHANDLE。HID_Read内部做异步ReadFileGetOverlappedResult超时后自动CancelIo并重置事件。所有内存分配/释放都在 DLL 内部完成调用方只管传缓冲区——这是跨语言调用不翻车的铁律。6.3 实际部署 checklist四件事决定上线成败检查项为什么重要如何验证DLL 运行时依赖hid.dll和setupapi.dll是系统 DLL但你的 DLL 若链接了/MD则需同目录放vcruntime140.dll用Dependency Walker或dumpbin /dependents your.dll查看设备拔插热插拔工业现场设备频繁插拔HID_Open必须能重试HID_Read必须对断开句柄返回 0拔掉设备观察HID_Read是否在超时后稳定返回 0而非崩溃多设备并发读取一个 DLL 实例需支持同时打开 3 个不同 HID 设备如扫码枪电子秤指纹仪HID_Open(0),HID_Open(1),HID_Open(2)后分别HID_Read确认互不干扰Unicode 路径兼容设备路径含中文或特殊字符时CreateFileW必须启用在中文系统下插入设备devicePath打印是否含中文字符CreateFile是否成功我坚持把 HID 通信做成 DLL 而非直接集成到主程序是因为它让故障隔离变得简单一旦 HID 模块异常只需替换 DLL不影响主业务逻辑。三年来线上 200 台工控机零次因 HID 通信导致整机宕机。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

2026工业AI控制系统:可验证的智能嵌入实战指南

2026工业AI控制系统:可验证的智能嵌入实战指南

1. 这不是科幻片,是2026年工厂车间里正在调试的真实系统“2026 AI工业控制系统,如何搭建?”——这句话最近在自动化工程师群、PLC技术论坛和智能制造项目组的晨会上被反复提起。它不是PPT里的概念图,也不是展会摊位上闪烁的LED屏演…

📅 2026/10/7 1:27:00
STM32 实战指南:从选型、环境搭建到外设避坑的嵌入式开发笔记

STM32 实战指南:从选型、环境搭建到外设避坑的嵌入式开发笔记

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/7 1:22:00
UFS3.1协议栈深解:UTP/UPIU流转、命令层分工与WriteBooster/HPB实战

UFS3.1协议栈深解:UTP/UPIU流转、命令层分工与WriteBooster/HPB实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/7 1:22:00
MORE NEWS

更多资讯

📰

Hyperf 配置组件(hyperf/config)完全指南:配置文件结构、Config 对象、`[Value]` 注解与环境变量实战

后端微服务 【免费下载链接】hyperf 🚀 A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease. 项目地址: https://gitcode.com/gh_mirrors/hy/hyperf 点击查看 免费下载 本篇技术指南以…

📰

力扣双周赛 172 全题解:从二维 0-1 背包到 O(1) 位运算(基于 codeforces-go 算法模板库)

科学计算 【免费下载链接】codeforces-go 算法竞赛模板库 by 灵茶山艾府 💭💡🎈 项目地址: https://gitcode.com/GitHub_Trending/co/codeforces-go 点击查看 免费下载 本篇技术指南以 leetcode/biweekly/172/README.md 为核心&a…

📰

Goa 仓库开发指南全解析:AGENTS.md 编码规范、代码生成契约与问题复现协议

后端代码生成API设计微服务 【免费下载链接】goa Design-first Go framework that generates API code, documentation, and clients. Define once in an elegant DSL, deploy as HTTP and gRPC services with zero drift between code and docs. 项目地址: https:/…

📰

XMall 分布式电商项目中的 Dubbo 架构实践:服务注册、消费与负载均衡全解析

电商后端微服务 【免费下载链接】xmall 基于SOA架构的分布式电商购物商城 前后端分离 前台商城:Vue全家桶 后台管理系统:Dubbo/SSM/Elasticsearch/Redis/MySQL/ActiveMQ/Shiro/Zookeeper等 项目地址: https://gitcode.com/gh_mirrors/xm/xmall 点击查看 免费下载 本…

📰

openpilot 开源驾驶辅助实战指南:车道居中和自适应巡航,三步装好上手

openpilot 开源驾驶辅助实战指南:车道居中和自适应巡航,三步装好上手 【免费下载链接】openpilot openpilot is an operating system for robotics. Currently, it upgrades the driver assistance system on 300 supported cars. 项目地址: https://g…

📰

如何用 Win11Debloat 移除 Windows 11 预装应用和关闭遥测(附完整回滚步骤)

如何用 Win11Debloat 移除 Windows 11 预装应用和关闭遥测(附完整回滚步骤) 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various ot…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬