C#从RAWHID结构体获取字节数组遇System.AccessViolationException问题
我之前在处理HID设备的RAWINPUT数据时也踩过几乎一模一样的坑,给你几个实际验证过的解决方向,应该能帮你搞定:
1. 先确认RAWHID结构体的定义完全匹配MSDN规范
这是最容易出错的地方,很多人会直接把bRawData定义成byte[],但实际上因为HID数据长度是动态的,必须用IntPtr来指向内存区域,而且结构体的布局必须严格是顺序布局:
[StructLayout(LayoutKind.Sequential)] public struct RAWHID { public int dwSizeHid; // 单个HID报表的大小 public int dwCount; // 报表的数量 public IntPtr bRawData; // 指向原始HID数据的指针 }
如果这里定义错了,后续所有的内存操作都会指向错误的地址,直接触发System.AccessViolationException。
2. 正确计算数据长度并执行Marshal拷贝
拿到RAWHID实例后,不能直接硬写拷贝长度,必须用dwSizeHid * dwCount来计算实际需要拷贝的字节数,这样才不会超出内存范围:
// 假设你已经从RAWINPUT中拿到了有效的RAWHID对象 RAWHID rawHid = rawInput.hid; int totalDataLength = rawHid.dwSizeHid * rawHid.dwCount; byte[] hidData = new byte[totalDataLength]; // 执行拷贝,这里一定要确保长度不超过内存实际大小 Marshal.Copy(rawHid.bRawData, hidData, 0, totalDataLength);
我之前就是因为偷懒直接写了固定长度,结果触发了和你一样的异常,改成动态计算长度就好了。
3. 检查RAWINPUT结构体的FieldOffset是否准确
RAWINPUT是一个包含不同设备类型的共用体,所以必须用LayoutKind.Explicit和FieldOffset来确保各字段的内存位置正确,比如:
[StructLayout(LayoutKind.Explicit)] public struct RAWINPUT { [FieldOffset(0)] public RAWINPUTHEADER header; // 用RAWINPUTHEADER的实际大小作为偏移量,确保位置准确 [FieldOffset(Marshal.SizeOf(typeof(RAWINPUTHEADER)))] public RAWHID hid; // 如果需要处理鼠标、键盘,可以继续添加对应的字段 // [FieldOffset(Marshal.SizeOf(typeof(RAWINPUTHEADER)))] // public RAWMOUSE mouse; }
如果FieldOffset设置错了,你读取到的RAWHID数据根本不是HID设备的实际数据,自然会导致内存访问错误。
4. 确保GetRawInputData的缓冲区分配正确
在获取RAWINPUT数据时,一定要先调用一次GetRawInputData获取所需的缓冲区大小,再分配足够的内存,不能直接用固定大小的缓冲区:
uint requiredSize = 0; // 第一次调用获取需要的缓冲区大小 GetRawInputData(rawInputHandle, RID_INPUT, IntPtr.Zero, ref requiredSize, Marshal.SizeOf(typeof(RAWINPUTHEADER))); // 分配足够的内存 IntPtr rawInputBuffer = Marshal.AllocHGlobal((int)requiredSize); try { uint result = GetRawInputData(rawInputHandle, RID_INPUT, rawInputBuffer, ref requiredSize, Marshal.SizeOf(typeof(RAWINPUTHEADER))); if (result != requiredSize) { // 处理获取数据失败的情况 return; } // 转换为RAWINPUT结构体 RAWINPUT rawInput = Marshal.PtrToStructure<RAWINPUT>(rawInputBuffer); // 接下来就可以处理rawInput.hid了 } finally { // 务必释放分配的内存,避免内存泄漏 Marshal.FreeHGlobal(rawInputBuffer); }
如果缓冲区大小不够,拷贝的时候就会越界,触发异常。
你可以从这几个点逐一排查,我当时就是结构体定义和FieldOffset的问题,调整后就正常读取HID数据了。
内容的提问来源于stack exchange,提问作者Ian.V
相关产品推荐
相关产品推荐

