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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 13:27:00