5

Ordinarily XInput controllers are identified simply using an index corresponding to the player number of the controller. Is there a way to obtain more information about a controller with a specific index, such as its vendor ID, product ID, or device name?

Even better would be a identifier that corresponds uniquely and consistently to just that controller so that it can be distinguished from all other XInput devices regardless of its index, including another controller that's an identical model (i.e. same product and vendor ID), similar to the instance GUID available using DirectInput.

Can this be accomplished using XInput or another Microsoft API? I'm also open to using undocumented functions if need be.

Bri Bri
  • 2,169
  • 3
  • 19
  • 44
  • What happened to the identical question you asked just days ago? – IInspectable Oct 08 '20 at 10:00
  • 1
    @IInspectable It was voted to be closed as looking for a suggestion for software or a library, which as near as I can tell was done by people who didn't understand the question. I'm trying again and trying to make it very explicit I'm looking for a solution to a specific problem. – Bri Bri Oct 08 '20 at 11:42

2 Answers2

6

There are a few undocumented functions inside the XInput1_4.dll. You can get the Vendor ID and Product ID like this:

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <Xinput.h>
#include <stdio.h>

struct XINPUT_CAPABILITIES_EX
{
    XINPUT_CAPABILITIES Capabilities;
    WORD vendorId;
    WORD productId;
    WORD revisionId;
    DWORD a4; //unknown
};

typedef DWORD(_stdcall* _XInputGetCapabilitiesEx)(DWORD a1, DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES_EX* pCapabilities);
_XInputGetCapabilitiesEx XInputGetCapabilitiesEx;

void main()
{
    HMODULE moduleHandle = LoadLibrary(TEXT("XInput1_4.dll"));
    XInputGetCapabilitiesEx = (_XInputGetCapabilitiesEx)GetProcAddress(moduleHandle, (char*)108);

    for (int i = 0; i < 4; ++i)
    {
        printf("Gamepad %d ", i);

        XINPUT_CAPABILITIES_EX capsEx;
        if (XInputGetCapabilitiesEx(1, i, 0, &capsEx) == ERROR_SUCCESS)
        {
            printf("connected, vid = 0x%04X pid = 0x%04X\n", (int)capsEx.vendorId, (int)capsEx.productId);
        }
        else
        {
            printf("not connected\n");
        }
    }
}
Toni Georgiev
  • 131
  • 1
  • 4
  • Your answer really helped me a lot, thanks!!! – Victor Mar 04 '22 at 08:37
  • This seems to be the answer I'm looking for but I get build errors when using this code. fatal error LNK1169: one or more multiply defined symbols found. My fix was to make the varaible static: static _XInputGetCapabilitiesEx XInputGetCapabilitiesEx; – tzg Mar 23 '22 at 17:10
1

What XInput internally does is open a device, then call DeviceIoControl on it every time it reads the joypad. (control code 0x8000e00c)

You need to hook these functions imported by "XInput1_4.dll":

  • CreateFileW from "api-ms-win-core-file-l1-1-0.dll"
  • DuplicateHandle from "api-ms-win-core-handle-l1-1-0.dll"
  • CloseHandle from "api-ms-win-core-handle-l1-1-0.dll"
  • DeviceIoControl from "api-ms-win-core-io-l1-1-0.dll"

Using the hooks for CreateFileW, DuplicateHandle and CloseHandle, you can keep track of what filename is associated with a handle.

Then when you see a call to DeviceIoControl with control code 0x8000e00c, you will know what filename is being read.


The first time you call XInputGetState, it will open multiple devices, and call DeviceIoControl multiple times, regardless of what player number you have asked for. You are only interested in the last filename seen by DeviceIoControl before XInputGetState returns. And if XInputGetState indicates the controller is not plugged in, disregard the filename you have collected for that controller number.


Examples of filenames I have seen on my own computer:

  • \\?\hid#{00001124-0000-1000-8000-00805f9b34fb}&vid_045e&pid_02e0&ig_00#8&7074921&2&0000#{ec87f1e3-c13b-4100-b5f7-8b84d54260cb}
  • \\?\usb#vid_045e&pid_028e#1&1a590e2c&1&01#{ec87f1e3-c13b-4100-b5f7-8b84d54260cb}

edit:

One more hook is required as well.

  • CoCreateInstance from "api-ms-win-core-com-l1-1-0.dll", to hook creating the undocumented IDeviceBroker COM object. If it can successfully create an IDeviceBroker COM object, it will use that instead of the call to CreateFileW. Parameters will be: CLSID_DeviceBroker = {acc56a05-e277-4b1e-a43e-7a73e3cd6e6c}, IID_IDeviceBroker = {8604b268-34a6-4b1a-a59f-cdbd8379fd98}. The method OpenDeviceFromInterfacePath will be called instead of CreateFileW. Alternatively, you can make creating the IDeviceBroker object simply fail, and it will proceed to use CreateFileW as usual.
Dwedit
  • 618
  • 5
  • 11