Windows 11下XINPUT库无法使用,求游戏手柄开发替代库
问题描述
我基于XINPUT库(复制自开源项目)开发了Xbox 360手柄应用,代码如下。尝试执行生成的.exe文件时,报错“This app can't run on your pc”;在VS Code中运行时,提示“Error exists after running PrelaunchTask”,点击“Debug anyway”后显示“Launch: Program 'C:/C++\Gamepad.exe' does not exist”。查阅XInput版本文档得知XInput 1.4随Windows 10发布,用于UWP应用,我使用的是Windows 11 64位系统,误以为无法使用XINPUT库,现咨询可替代的游戏手柄应用开发库,若我的理解有误请指正。
Gamepad.cpp
#include "Gamepad.h" #include <algorithm> #include <climits> float normalize(float input, float min, float max); Gamepad::Gamepad(UINT id) : controllerID(id), deadzoneX(XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE), deadzoneY(XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { ZeroMemory(&state, sizeof(XINPUT_STATE)); ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION)); } Gamepad::Gamepad(UINT id, float deadzoneX, float deadzoneY) : deadzoneX(deadzoneX), deadzoneY(deadzoneY) { ZeroMemory(&state, sizeof(XINPUT_STATE)); ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION)); } UINT Gamepad::getControllerID() const { return controllerID; } XINPUT_GAMEPAD* Gamepad::getGamepad() { return &state.Gamepad; } /*XINPUT_BATTERY_INFORMATION* Gamepad::getBatteryInfo() { XInputGetBatteryInformation(controllerID, XINPUT_DEVTYPE_GAMEPAD, &battery); return &battery; }*/ bool Gamepad::isConnected() { if (XInputGetState(controllerID - 1, &state) == ERROR_SUCCESS) { return true; } else { return false; } } float normalize(float input, float min, float max) { float average = (min + max) / 2; float range = (max - min) / 2; return (input - average) / range; } float Gamepad::ApplyDeadzone(float value, float maxValue, float deadzone) { if (value < -deadzone) { value += deadzone; //increase neg vals to remove deadzone discontinuity } else if (value > deadzone) { value -= deadzone; //decrease pos vals to remove deadzone discontinuity } else { return 0; //hey values are zero for once } float normValue = (float)value / (float)(maxValue - deadzone); return std::max(-1.0f, std::min(normValue, 1.0f)); } bool Gamepad::Update() { if (!isConnected()) return false; float normLX = normalize(static_cast<float>(state.Gamepad.sThumbLX), -32767, 32767); float normLY = normalize(static_cast<float>(state.Gamepad.sThumbLY), -32767, 32767); float normRX = normalize(static_cast<float>(state.Gamepad.sThumbRX), -32767, 32767); float normRY = normalize(static_cast<float>(state.Gamepad.sThumbRY), -32767, 32767); if (deadzoneX <= 1.0f || deadzoneY <= 1.0f) { leftStickX = ApplyDeadzone(normLX, maxValue, deadzoneX); leftStickY = ApplyDeadzone(normLY, maxValue, deadzoneY); rightStickX = ApplyDeadzone(normRX, maxValue, deadzoneX); rightStickY = ApplyDeadzone(normRY, maxValue, deadzoneY); } else { leftStickX = ApplyDeadzone(normLX, maxValue, normalize(deadzoneX, SHRT_MIN, SHRT_MAX)); leftStickY = ApplyDeadzone(normLY, maxValue, normalize(deadzoneY, SHRT_MIN, SHRT_MAX)); rightStickX = ApplyDeadzone(normRX, maxValue, normalize(deadzoneX, SHRT_MIN, SHRT_MAX)); rightStickY = ApplyDeadzone(normRY, maxValue, normalize(deadzoneY, SHRT_MIN, SHRT_MAX)); } leftTrigger = static_cast<float>(state.Gamepad.bLeftTrigger) / 255.0f;//normalize input rightTrigger = static_cast<float>(state.Gamepad.bRightTrigger) / 255.0f; return true; } void Gamepad::Vibrate(USHORT leftSpeed, USHORT rightSpeed) { vibration.wLeftMotorSpeed = leftSpeed; vibration.wRightMotorSpeed = rightSpeed; XInputSetState(controllerID - 1, &vibration); } void Gamepad::Vibrate(USHORT speed) { vibration.wLeftMotorSpeed = speed; vibration.wRightMotorSpeed = speed; XInputSetState(controllerID - 1, &vibration); } void Gamepad::resetVibration() { vibration.wLeftMotorSpeed = 0; vibration.wRightMotorSpeed = 0; XInputSetState(controllerID - 1, &vibration); } bool Gamepad::isButtonPressed(UINT button) const { return (state.Gamepad.wButtons & button) != 0; }
Main.cpp
#include <iostream> #include <chrono> #include <thread> #include <iomanip> #include "Gamepad.h" #define NOMINMAX #include <windows.h> int main() { Gamepad gamepad(1); bool aPressed = false; if (!gamepad.isConnected()) { std::cout << "Controller not connected" << std::endl; return -1; } while (true) { if (gamepad.Update()) { system("cls"); std::cout << std::fixed << std::setprecision(2) << "Left Trigger: " << gamepad.leftTrigger << ", Right Trigger: " << gamepad.rightTrigger << "n" << "Left Stick " << "X: " << gamepad.leftStickX << ", Y: " << gamepad.leftStickY << "n" << "Right Stick " << "X: " << gamepad.rightStickX << ", Y: " << gamepad.rightStickY << std::endl; if (gamepad.isButtonPressed(XINPUT_GAMEPAD_A)) { aPressed = true; } else { aPressed = false; } if (aPressed == true) { gamepad.Vibrate(20000, 20000); } else { gamepad.resetVibration(); } if (gamepad.isButtonPressed(XINPUT_GAMEPAD_BACK)) break; } std::this_thread::sleep_for(std::chrono::milliseconds(10)); } return 0; }
Gamepad.h
#pragma once #include <Windows.h> #include <Xinput.h> class Gamepad { private: UINT controllerID; XINPUT_STATE state; XINPUT_VIBRATION vibration; //XINPUT_BATTERY_INFORMATION battery;//not used in v1.4 const float maxValue = 1.0f; float deadzoneX; float deadzoneY; float ApplyDeadzone(float value, float maxValue, float deadzone); public: Gamepad(UINT id); Gamepad(UINT id, float deadzoneX, float deadzoneY); inline UINT getControllerID() const; XINPUT_GAMEPAD* getGamepad(); //XInputGetBatteryInformation is not supported for v1.4 //XINPUT_BATTERY_INFORMATION* getBatteryInfo(); bool isConnected(); bool Update(); void Vibrate(USHORT leftSpeed, USHORT rightSpeed); void Vibrate(USHORT speed); void resetVibration(); bool isButtonPressed(UINT button) const; float leftStickX, leftStickY; float rightStickX, rightStickY; float leftTrigger, rightTrigger; };
解答
XInput版本误解纠正
Windows 11完全支持XInput库,XInput 1.4并非仅用于UWP应用,桌面Win32程序同样可以调用。此外,Windows系统还兼容更早的XInput版本(如9.1.0、1.3),这些版本对Xbox 360手柄的支持同样完善。你的代码基于XInput是可行的,无需因系统版本放弃XInput。
解决当前运行错误
1. “This app can't run on your pc”错误
大概率是编译架构不匹配:
- 若编译的是32位程序,而系统是64位,或反之,可能出现此错误。检查编译目标架构,设置为与系统匹配的x64或x86。
- 另一种可能是程序未成功编译,生成的exe文件损坏或不完整。
2. VS Code中的编译运行错误
- “Error exists after running PrelaunchTask”说明编译任务执行失败,检查
.vscode/tasks.json中的编译配置,确保链接了XInput库(添加xinput.lib或xinput1_4.lib到链接参数),且头文件路径正确。 - “Program does not exist”是因为编译未生成目标exe文件,先解决编译错误,确保编译成功后再调试。
- 代码小问题:Main.cpp的输出字符串中使用了
"n",应改为"\n",否则会导致输出混乱,甚至影响程序运行逻辑。
替代游戏手柄开发库推荐
若确实需要替代方案,以下几个库值得考虑:
- SDL2:跨平台手柄支持库,兼容Windows、Linux、macOS等系统,能识别绝大多数主流手柄,API简洁易用,同时提供图形、音频等多媒体功能,适合开发跨平台游戏或应用。
- GLFW:轻量型跨平台库,常用于OpenGL/Vulkan图形应用,内置手柄输入支持,API简单,无多余依赖。
- DirectInput:微软老牌输入库,支持更多类型的旧手柄,但对Xbox手柄的优化不如XInput,适合需要兼容多种非Xbox手柄的场景。
- SFML:多媒体开发库,包含手柄输入模块,跨平台支持,API设计直观,适合快速开发小型多媒体应用。
内容的提问来源于stack exchange,提问作者Tenny_J




