Go调用Windows API GetRawInputDeviceInfoA的错误修复求助
问题排查与解决方案
核心问题分析
你的代码存在几个关键错误,导致调用Windows API时出现内存访问错误或参数错误:
- 结构体类型不匹配:
rawInputDeviceList中的DeviceHandle定义为uint64,但Windows的HANDLE类型对应Go的uintptr(适配32/64位系统),类型不匹配会导致句柄传递错误。 - 设备列表切片创建错误:
make([]rawInputDeviceList, size*devCount)会创建远多于实际设备数量的元素,后续访问会触发内存越界。 GetRawInputDeviceInfoA调用流程错误:该API需要先调用一次获取缓冲区大小,再分配缓冲区读取设备名称,原始代码直接传无效参数导致错误。- 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) } }
关键修正说明
- 结构体类型修正:将
DeviceHandle改为uintptr,确保与Windows的HANDLE类型匹配,避免跨平台或位数不兼容问题。 - 设备列表创建修正:
make([]rawInputDeviceList, devCount)直接按设备数量创建切片,避免内存浪费和越界。 - API调用流程修正:
- 首次调用
GetRawInputDeviceInfoA时传入0作为缓冲区指针,获取所需的缓冲区大小。 - 根据返回的大小分配字节数组,再次调用API读取设备名称。
- 首次调用
- 参数传递修正:直接传递
DeviceHandle的值(uintptr),而非指针;size参数在首次调用时传递指针以接收缓冲区长度。
内容的提问来源于stack exchange,提问作者geenSf
相关产品推荐
相关产品推荐

