首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用HidD_GetInputReport(..)检索XBOX ONEs按钮状态

使用HidD_GetInputReport(..)检索XBOX ONEs按钮状态
EN

Stack Overflow用户
提问于 2020-11-07 21:29:41
回答 1查看 1.1K关注 0票数 1

我试图通过Microsoft 与XBOX控制器对话,而不使用 XINPUT。我目前能够控制所有隆隆电机(包括力反馈触发器)发送数据包使用HidD_SetOutputReport(HANDLE, VOID*, ULONG)。但是,我无法使用HidD_GetInputReport(HANDLE, VOID*, ULONG)ReadFile() / ReadFileEx()读取按钮值,不管是否使用FILE_FLAG_OVERLAPPED创建句柄,以及使用OVERLAPPED和Windows。

我已经反向设计了URB协议,并借助下面的文章https://github.com/quantus/xbox-one-controller-protocol。主要目标是克服XINPUT开销,并编写一个灵活的框架,以便我也可以集成其他游戏垫。

这就是我所完成的:

  1. 我已经通过USB连接了游戏板和我的电脑(这样我就可以读取从设备发送和接收到的所有USB包)。
  2. 我使用SetupDiGetClassDevs(...)SetupDiEnumDeviceInfo(...)SetupDiEnumDeviceInterfaces(...)SetupDiGetDeviceInterfaceDetail(...)找到了控制器的路径
  3. 我使用HANDLE gamePad = CreateFile(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL)为设备创建了一个句柄
  4. 使用HidP_GetCaps(HANDLE, HIDP_CAPS*)似乎不会返回有效数据,因为它报告的OutputReportByteLength为0,但我能够发送大小为5(打开)和9(设置隆隆马达)的输出报告。
  5. 所有的输入和输出数据(至少按钮和隆隆马达)似乎遵循以下模式
代码语言:javascript
运行
复制
byte 0: Package type
byte 1: was always 0x00
byte 2: Package number (always incrementing every package)
byte 3: Size of following data
byte 4+: <Data>
  1. 有了这些信息,我就能让发动机和触发装置如我所愿地隆隆作响。

例如:我的两个输出隆隆包看起来像这样(用脉冲长度脏地打开和关闭马达):

这开启了所有发动机的隆隆电机速度在0xFF和触发器的速度0xF0。我就是这样做的:

代码语言:javascript
运行
复制
struct RumbleContinous{
    BYTE mode;
    BYTE mask; // Motor Mask 0b 0 0 0 0 LT RT L R
    BYTE lTForce;
    BYTE rTForce;
    BYTE lForce;
    BYTE rForce;
    BYTE pulseLength;
    BYTE offTime;
    BYTE terminator; // Terminator / Dummy / ?? (XINPUT sends that as 0xEB!) / Changing seems to not make any changes
};

RumbleContinous rc = {0x00, 0x0F, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF 0x00, 0xEB};

HidD_SetOutputReport(gamePad, (PVOID)&rc, sizeof(RumbleContinous ));

to my problem

查看来自控制器的输入包,看起来您需要创建一个大小为0x0E = 14的缓冲区,ZeroMemory it (或者像MSDN建议的那样将第一个字节写入0),只需调用HidD_GetInputReport(HANDLE, buffer, 14)

因此,我所做的就是调用HidD_FlushQueue(),以确保下一个包是输入包。然后,我插入一个小延迟,以便我能够改变一些控制器的值。之后,我尝试用HidD_GetInputReport(HANDLE, cmd_in, 14)读取字节数组,但是函数在GetLastError() == 0x00000057 // ERROR_INVALID_PARAMETER中总是失败。

由于HID能够过滤包,所以可能需要分配比预期大一个字节的缓冲区,并将所需的报告id传递到位置0的缓冲区。我就是这样做的:

代码语言:javascript
运行
复制
BYTE cmd_in[15];
ZeroMemory(cmd_in, 15);
cmd_in[0] = 0x20;
HidD_GetInputReport(gamePad, cmd_in, 15);

还是没有成功。由于HidP_GetCaps(...)函数报告了16的输入报告(但我不相信这一点,因为它已经愚弄了我,输出报告大小为0),所以我尝试扫过许多缓冲区大小:

代码语言:javascript
运行
复制
BYTE cmd_in[30];
for (UINT bs = 0; bs < 30; bs++) {
    ZeroMemory(cmd_in, 30);

    HidD_FlushQueue(gamePad); // Flushing works
    Sleep(500);
        
    if (HidD_GetInputReport(gamePad, cmd_in, bs)) {
        // Output result (ommited)
    }
    else {
        // Print error (ommited)
    }
}

代码语言:javascript
运行
复制
BYTE cmd_in[30];
for (UINT bs = 0; bs < 30; bs++) {
    ZeroMemory(cmd_in, 30);
    cmd_in[0] = 0x20;

    HidD_FlushQueue(gamePad); // Flushing works
    Sleep(500);
        
    if (HidD_GetInputReport(gamePad, cmd_in, bs)) {
        // Output result (ommited)
    }
    else {
        // Print error (ommited)
    }
}

还是没有成功。根据特殊要求的输出格式和错误的HidP_GetCaps(...)读数,我怀疑XBOX游戏垫驱动程序需要一个已经在输入缓冲区中的特殊头(据我所知,HidD_GetInputReport(.)只需调用用户/内核模式驱动程序回调;因此驱动程序可以执行检查并拒绝发送给它的所有数据)

可能有人知道怎么打电话给HidD_GetInputReport(.)对于XBOX One控制器

我知道可以检索输入数据,因为SimpleHIDWrite能够看到按钮状态。甚至通过格式是完全不同的(例如,这两个触发器是在一个字节中组合的)。在USB封装中,每个触发器都有自己的字节):

我还应该提到,HIDWrite没有按任何按钮就能看到数据!查看来自SimpleHIDWrite的日志,它看起来像是在从00 15字节的数据中读取RD,在00时有一个16字节的数组和0元素(在我的应用程序中没有工作)。还是直接转储所有输入的数据。如果是的话,这怎么可能?这对我来说也是个选择!

EN

回答 1

Stack Overflow用户

发布于 2020-11-08 15:12:36

我查看了XINPUT在执行以下代码时所做的工作:

代码语言:javascript
运行
复制
XINPUT_STATE s;
XInputGetState(0, &s);

原来XINPUT做的事情和我做的一样,直到从控制器读取数据为止。偷盗HidD_GetInputReport(...) XINPUT正在调用DeviceIoControl(...)。因此,我所做的是一个快速的google服务器"DeviceIoControl xbox“和tada在这里它是不需要计算出我自己的内存布局:在不需要输入的情况下获取xbox控制器输入

编辑:即使游戏垫是通过蓝牙连接的,使用DeviceIoControl(...)也能工作,而当游戏垫通过蓝牙连接时,HidD_SetOutputReport(...)就不能工作。我记得通过蓝牙读取DeviceIoControl(...)需要在输出缓冲区中显示一个加法参数。但我目前正在设法通过DeviceIoControl(...)控制隆隆电机。如果你有任何建议,请随时发表评论!以上链接中的文章只激活两个隆隆电机,而不是触发器!

编辑2:我尝试从j0x0到0xFFFFFF和I0x1到0x3F对DeviceIoControl(HANDLE, j, CHAR*, i, NULL, 0, DWORD*, NULL)进行扫描。它起了作用..。一开始..。但是经过几个j值之后,我得到了一个蓝屏:WDF_Violation (至少我知道如何使计算机崩溃;)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64732631

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档