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

Go调用Windows API GetRawInputDeviceInfoA的错误修复求助

问题排查与解决方案

核心问题分析

你的代码存在几个关键错误,导致调用Windows API时出现内存访问错误或参数错误:

  1. 结构体类型不匹配:rawInputDeviceList中的DeviceHandle定义为uint64,但Windows的HANDLE类型对应Go的uintptr(适配32/64位系统),类型不匹配会导致句柄传递错误。
  2. 设备列表切片创建错误:make([]rawInputDeviceList, size*devCount)会创建远多于实际设备数量的元素,后续访问会触发内存越界。
  3. GetRawInputDeviceInfoA调用流程错误:该API需要先调用一次获取缓冲区大小,再分配缓冲区读取设备名称,原始代码直接传无效参数导致错误。
  4. API参数传递错误:函数参数类型与Windows API要求不匹配,比如句柄不需要传指针、size参数在首次调用时需传递指针接收所需长度。

修正后的完整代码

package main

import (
    "fmt"
    "syscall"
    "unsafe"
)

// 正确匹配Windows RAWINPUTDEVICELIST结构
type rawInputDeviceList struct {
    DeviceHandle uintptr // 替换uint64为uintptr,适配HANDLE类型
    Type         uint32
}

const (
    RIDI_DEVICENAME uint32 = 0x20000007
)

var (
    user32                    = syscall.NewLazyDLL("user32.dll")
    getRawInputDeviceListProc = user32.NewProc("GetRawInputDeviceList")
    getRawInputDeviceInfoProc = user32.NewProc("GetRawInputDeviceInfoA")
)

func main() {
    dl := rawInputDeviceList{}
    itemSize := uint32(unsafe.Sizeof(dl))

    // 第一步:获取设备总数
    var devCount uint32
    _, _, err := getRawInputDeviceListProc.Call(0, uintptr(unsafe.Pointer(&devCount)), uintptr(itemSize))
    if err != syscall.Errno(0) && err != syscall.ERROR_INSUFFICIENT_BUFFER {
        fmt.Printf("获取设备总数失败: %v\n", err)
        return
    }

    if devCount == 0 {
        fmt.Println("未检测到输入设备")
        return
    }

    // 第二步:分配内存并获取设备列表
    devices := make([]rawInputDeviceList, devCount)
    ret, _, err := getRawInputDeviceListProc.Call(
        uintptr(unsafe.Pointer(&devices[0])),
        uintptr(unsafe.Pointer(&devCount)),
        uintptr(itemSize),
    )
    if ret == uintptr(syscall.INVALID_HANDLE_VALUE) {
        fmt.Printf("获取设备列表失败: %v\n", err)
        return
    }

    // 第三步:遍历设备获取名称
    for _, dev := range devices {
        fmt.Printf("设备类型: %d, 设备句柄: %x\n", dev.Type, dev.DeviceHandle)

        // 先调用一次获取所需缓冲区大小
        var bufSize uint32
        _, _, err := getRawInputDeviceInfoProc.Call(
            dev.DeviceHandle,
            uintptr(RIDI_DEVICENAME),
            0,
            uintptr(unsafe.Pointer(&bufSize)),
        )
        if err != syscall.Errno(0) && err != syscall.ERROR_INSUFFICIENT_BUFFER {
            fmt.Printf("获取缓冲区大小失败: %v\n", err)
            continue
        }

        // 分配缓冲区
        buf := make([]byte, bufSize)
        ret, _, err := getRawInputDeviceInfoProc.Call(
            dev.DeviceHandle,
            uintptr(RIDI_DEVICENAME),
            uintptr(unsafe.Pointer(&buf[0])),
            uintptr(unsafe.Pointer(&bufSize)),
        )
        if ret == uintptr(-1) {
            fmt.Printf("获取设备名称失败: %v\n", err)
            continue
        }

        // 转换为字符串(去掉结尾的null字符)
        devName := syscall.ByteSliceToString(buf[:bufSize-1])
        fmt.Printf("设备名称: %s\n\n", devName)
    }
}

关键修正说明

  1. 结构体类型修正:将DeviceHandle改为uintptr,确保与Windows的HANDLE类型匹配,避免跨平台或位数不兼容问题。
  2. 设备列表创建修正:make([]rawInputDeviceList, devCount)直接按设备数量创建切片,避免内存浪费和越界。
  3. API调用流程修正:
    • 首次调用GetRawInputDeviceInfoA时传入0作为缓冲区指针,获取所需的缓冲区大小。
    • 根据返回的大小分配字节数组,再次调用API读取设备名称。
  4. 参数传递修正:直接传递DeviceHandle的值(uintptr),而非指针;size参数在首次调用时传递指针以接收缓冲区长度。

内容的提问来源于stack exchange,提问作者geenSf

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 13:30:59