You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:04:52