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

调用GetRawInputDeviceInfo(RIDI_DEVICENAME)返回无效内存问题求助

GetRawInputDeviceInfoA返回无效内存的问题排查与解决

问题描述

调用GetRawInputDeviceInfoA并传入RIDI_DEVICENAME指令时,期望函数将设备名字符串填充到第三个参数中并返回字符串长度,但函数返回后该参数指向无效内存(调试器显示为0x234449485c3f5c5c)。调用未显式失败:返回值为83而非-1或0,且GetLastError返回0。尝试手动分配内存、传入NULL指针及宽字符版本均得到返回值83且指向无效内存(如0x005c003f005c005c),使用设备为PS4控制器。

完整代码如下:

#include <windows.h>
#include <stdint.h>

#define assert(x) do{if(!(x))__debugbreak();}while(0)

typedef size_t usize;
typedef uint8_t u8;
typedef unsigned int uint;

struct Raw_Input_Memory {
  usize size;
  usize used;
  u8   *data;
};

#define raw_input_memory_push_array(memory, type, count) (type *)raw_input_memory_push_size(memory, sizeof(type) * count)
#define raw_input_memory_push_struct(memory, type) (type *)raw_input_memory_push_size(memory, sizeof(type))
void *raw_input_memory_push_size(Raw_Input_Memory *memory, usize size) {
  assert(memory->used + size <= memory->size);
  
  void *result = memory->data + memory->used;
  memory->used += size;
  
  return result;
}

int main() {
  HINSTANCE module_instance = GetModuleHandle(NULL);
  
  WNDCLASSA window_class = {};
  window_class.lpfnWndProc = DefWindowProc;
  window_class.hInstance = module_instance;
  window_class.hCursor = LoadCursorA(NULL, IDC_ARROW);
  window_class.lpszClassName = "Toplevel";
  
  ATOM window_class_atom = RegisterClassA(&window_class);
  if (window_class_atom) {
    HWND window = CreateWindowA(window_class.lpszClassName, "Raw Input",
                                WS_VISIBLE | WS_OVERLAPPEDWINDOW,
                                CW_USEDEFAULT, CW_USEDEFAULT,
                                CW_USEDEFAULT, CW_USEDEFAULT,
                                NULL, NULL, module_instance, NULL);
    if (window) {
      Raw_Input_Memory raw_input_memory = {};
      raw_input_memory.size = 1024 * 1024;
      raw_input_memory.used = 0;
      raw_input_memory.data = (u8 *)VirtualAlloc(NULL, raw_input_memory.size,
                                                 MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
      
      RAWINPUTDEVICE raw_input_device;
      
      raw_input_device.usUsagePage = 0x01;
      raw_input_device.usUsage = 0x05;
      raw_input_device.dwFlags = 0;
      raw_input_device.hwndTarget = 0;
      
      if (RegisterRawInputDevices(&raw_input_device, 1,
                                  sizeof(RAWINPUTDEVICE))) {
        MSG window_message = {};
        while (GetMessageA(&window_message, NULL, 0, 0) > 0) {
          UINT message_kind = window_message.message;
          WPARAM wparam     = window_message.wParam;
          LPARAM lparam     = window_message.lParam;
          
          switch (message_kind) {
            case WM_QUIT: {
              break;
            } break;
            
            case WM_INPUT: {
              if (raw_input_memory.data) {
                raw_input_memory.used = 0;
                
                UINT input_size;
                if (GetRawInputData((HRAWINPUT)lparam, RID_INPUT, NULL, &input_size,
                                    sizeof(RAWINPUTHEADER)) != -1) {
                  RAWINPUT *input = (RAWINPUT *)raw_input_memory_push_size(&raw_input_memory,
                                                                           input_size);
                  if (GetRawInputData((HRAWINPUT)lparam, RID_INPUT, input,
                                      &input_size, sizeof(RAWINPUTHEADER)) == input_size) {
                    assert(input->header.dwType == RIM_TYPEHID);
                    
                    uint device_name_length = 128;
                    char *device_name = NULL;
                    device_name = raw_input_memory_push_array(&raw_input_memory, char,
                                                              device_name_length);
                    assert(device_name);
                    INT got_device_name = GetRawInputDeviceInfoA(input->header.hDevice,
                                                                 RIDI_DEVICENAME,
                                                                 &device_name,
                                                                 &device_name_length);
                    if (got_device_name) {
                      /* ... */
                    }
                  }
                }
              }
            } break;
            
            default: {
              TranslateMessage(&window_message);
              DispatchMessageA(&window_message);
            }
          }
        }
      }
    }
  }

  return 0;
}

错误分析

核心问题出在GetRawInputDeviceInfoA的调用参数上:

INT got_device_name = GetRawInputDeviceInfoA(input->header.hDevice,
                                             RIDI_DEVICENAME,
                                             &device_name,
                                             &device_name_length);

这里第三个参数传入了&device_name(即char**类型),但GetRawInputDeviceInfoA的第三个参数要求是LPVOID类型,当使用RIDI_DEVICENAME时,应该传入指向缓冲区的指针(char*),而不是指针的地址。

函数会将设备名直接写入你传入的地址,当你传入&device_name时,函数实际上把设备名的字符数据写到了device_name这个指针变量本身的内存中,覆盖了原本指向有效缓冲区的地址值,导致device_name变成了无效的内存地址(调试器看到的乱码值就是被覆盖后的指针内容)。

返回值83是设备名的实际长度(包含终止符),这说明函数本身执行成功,但因为参数传递错误,导致缓冲区指针被破坏。

解决方案

1. 修正参数传递

将第三个参数从&device_name改为device_name:

INT got_device_name = GetRawInputDeviceInfoA(input->header.hDevice,
                                             RIDI_DEVICENAME,
                                             device_name,
                                             &device_name_length);

2. 优化内存分配(可选但推荐)

当前代码固定分配128字节的缓冲区,可能存在长度不足的情况。更稳妥的方式是先调用一次GetRawInputDeviceInfoA获取所需的缓冲区长度,再分配对应大小的内存:

// 先获取设备名长度
INT name_length = GetRawInputDeviceInfoA(input->header.hDevice,
                                         RIDI_DEVICENAME,
                                         NULL,
                                         &device_name_length);
if (name_length != -1) {
    // 分配足够的内存
    char *device_name = raw_input_memory_push_array(&raw_input_memory, char, name_length);
    assert(device_name);
    // 再次调用获取设备名
    INT got_device_name = GetRawInputDeviceInfoA(input->header.hDevice,
                                                 RIDI_DEVICENAME,
                                                 device_name,
                                                 &name_length);
    if (got_device_name != -1) {
        // 处理设备名
    }
}

这样可以避免固定长度缓冲区不够的问题,确保能完整存储设备名。

补充说明

  • 如果你使用宽字符版本GetRawInputDeviceInfoW,同样需要注意参数类型匹配,传入wchar_t*而非wchar_t**。
  • 确保raw_input_memory的剩余空间足够容纳设备名,避免内存越界。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 01:50:14