如何在Visual Studio 2019中用C++和DirectX8读取USB Joystick数据
DirectInput 特定摇杆设备读取完整实现
核心特性
- 基于*VID(厂商ID)、PID(产品ID)*精准匹配目标摇杆,不会误识别其他设备
- 实时读取摇杆轴、按压按钮、POV视角帽数据,直接输出到控制台
- 兼容VS2019 C++编译环境,依赖官方DirectX 8库,无第三方依赖
前置配置
你需要先获取目标摇杆的VID和PID:打开Windows设备管理器 → 人体学输入设备 → 找到对应摇杆 → 右键属性 → 详细信息 → 选择「硬件ID」,即可看到类似HID\VID_045E&PID_028E的字符串,替换代码中TARGET_VID、TARGET_PID的对应值即可。
完整代码
#include <iostream> using namespace std; #include <Windows.h> #define DIRECTINPUT_VERSION 0x0800 #include <dinput.h> #pragma comment(lib, "dinput8.lib") #pragma comment(lib, "dxguid.lib") // 替换为你的目标摇杆VID和PID #define TARGET_VID 0x045E // 示例为Xbox 360手柄厂商ID #define TARGET_PID 0x028E // 示例为Xbox 360手柄产品ID LPDIRECTINPUT8 di; HRESULT hr; LPDIRECTINPUTDEVICE8 joystick; DIDEVCAPS capabilities; BOOL CALLBACK enumCallback(const DIDEVICEINSTANCE* instance, VOID* context) { // 从产品GUID中提取VID和PID WORD vid = LOWORD(instance->guidProduct.Data1); WORD pid = HIWORD(instance->guidProduct.Data1); // 不匹配目标设备则继续枚举 if (vid != TARGET_VID || pid != TARGET_PID) { return DIENUM_CONTINUE; } // 匹配到目标设备后创建设备实例 hr = di->CreateDevice(instance->guidInstance, &joystick, NULL); if (FAILED(hr)) { return DIENUM_CONTINUE; } // 打印匹配到的设备名称 wcout << L"匹配到目标设备:" << instance->tszProductName << endl; return DIENUM_STOP; } BOOL CALLBACK enumAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, VOID* context) { DIPROPRANGE propRange; propRange.diph.dwSize = sizeof(DIPROPRANGE); propRange.diph.dwHeaderSize = sizeof(DIPROPHEADER); propRange.diph.dwHow = DIPH_BYID; propRange.diph.dwObj = instance->dwType; propRange.lMin = -1000; propRange.lMax = +1000; if (FAILED(joystick->SetProperty(DIPROP_RANGE, &propRange.diph))) { return DIENUM_STOP; } return DIENUM_CONTINUE; } HRESULT poll(DIJOYSTATE2* js) { HRESULT hr; if (joystick == NULL) { return S_OK; } hr = joystick->Poll(); if (FAILED(hr)) { hr = joystick->Acquire(); while (hr == DIERR_INPUTLOST) { hr = joystick->Acquire(); } if (hr == DIERR_INVALIDPARAM || hr == DIERR_NOTINITIALIZED) { return E_FAIL; } if (hr == DIERR_OTHERAPPHASPRIO) { return S_OK; } } if (FAILED(hr = joystick->GetDeviceState(sizeof(DIJOYSTATE2), js))) { return hr; } return S_OK; } int main() { // 初始化DirectInput if (FAILED(hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&di, NULL))) { cout << "DirectInput初始化失败,错误码:" << hr << endl; return hr; } // 枚举所有已连接的游戏控制器 if (FAILED(hr = di->EnumDevices(DI8DEVCLASS_GAMECTRL, enumCallback, NULL, DIEDFL_ATTACHEDONLY))) { cout << "设备枚举失败,错误码:" << hr << endl; return hr; } if (joystick == NULL) { cout << "未匹配到目标摇杆设备,请确认设备已连接且VID/PID配置正确" << endl; return E_FAIL; } // 配置设备 if (FAILED(hr = joystick->SetDataFormat(&c_dfDIJoystick2))) { cout << "设置数据格式失败,错误码:" << hr << endl; return hr; } if (FAILED(hr = joystick->SetCooperativeLevel(GetConsoleWindow(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND))) { cout << "设置协作等级失败,错误码:" << hr << endl; return hr; } capabilities.dwSize = sizeof(DIDEVCAPS); if (FAILED(hr = joystick->GetCapabilities(&capabilities))) { cout << "获取设备能力失败,错误码:" << hr << endl; return hr; } if (FAILED(hr = joystick->EnumObjects(enumAxesCallback, NULL, DIDFT_AXIS))) { cout << "枚举轴设备失败,错误码:" << hr << endl; return hr; } // 首次获取设备 joystick->Acquire(); cout << "设备读取已启动,按ESC键退出" << endl; // 主循环,读取并输出数据 while (!(GetAsyncKeyState(VK_ESCAPE) & 0x8000)) { DIJOYSTATE2 js = {0}; if (SUCCEEDED(poll(&js))) { // 清屏(可选,注释掉可保留历史数据) system("cls"); // 输出轴数据 cout << "=== 轴数据 ===" << endl; cout << "X轴:" << js.lX << " Y轴:" << js.lY << " Z轴:" << js.lZ << endl; cout << "RX轴:" << js.lRx << " RY轴:" << js.lRy << " RZ轴:" << js.lRz << endl; // 输出POV帽数据 cout << endl << "=== POV视角帽 ===" << endl; for (int i = 0; i < capabilities.dwPOVs; i++) { cout << "POV" << i+1 << ":" << (js.rgdwPOV[i] == 0xFFFFFFFF ? "中立" : to_string(js.rgdwPOV[i]/100) + "°") << endl; } // 输出按钮状态 cout << endl << "=== 按钮状态 ===" << endl; for (int i = 0; i < capabilities.dwButtons; i++) { if (js.rgbButtons[i] & 0x80) { cout << "按钮" << i+1 << ":按下 | "; } } cout << endl; } // 控制刷新频率 Sleep(100); } // 释放资源 if (joystick) { joystick->Unacquire(); joystick->Release(); } if (di) { di->Release(); } return 0; }
编译运行说明
- VS2019创建空的C++控制台项目,将上述代码粘贴到.cpp文件即可直接编译
- 项目不需要额外配置链接器,代码已内置库引用指令
- 运行前请确认目标摇杆已接入电脑
内容的提问来源于stack exchange,提问作者Tom Hu
相关产品推荐
相关产品推荐

