Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Intercept messages from USB auxiliary device
#1
Hello,
I am attempting to intercept and re-configure buttons on a USB device.
My understanding is that I need to use WM_INPUT to intercept messages.
From reading online, the device first needs to be registered: https://learn.microsoft.com/en-us/window...putdevices
Then get get handle of device.
Can you please help guide me in the right direction, to first capture the button and then re-assign it my own code?
Thank so much in advance
#2
Note: If a button already does some action, the raw input API cannot block that action. You'll get `WM_INPUT` and can then execute code, but cannot block the default action. If there is no default action, then no problem.
 
Code:
Copy      Help
// script "Raw input.cs"
//Registers a raw input device to receive WM_INPUT messages. On message reads data.
//Run this script. To stop, click the End button on the toolbar.
//Raw input docs: https://learn.microsoft.com/en-us/windows/win32/inputdev/raw-input


var w = WndUtil.CreateMessageOnlyWindow("#32770");

var rid = new api.RAWINPUTDEVICE {
    usUsagePage = 1, usUsage = 6, //TODO: set correct values. It depends on device. The 0 6 if for keyboards. Run script "Raw input device info.cs" to get the values. See https://learn.microsoft.com/en-us/windows-hardware/drivers/hid/hid-usages
    dwFlags = api.RIDEV_INPUTSINK,
    hwndTarget = w
};
if (!api.RegisterRawInputDevices(rid, 1, Marshal.SizeOf<api.RAWINPUTDEVICE>())) throw new AuException(lastError.message);

while (api.GetMessage(out var m)) {
    if (m.message == api.WM_INPUT) _WmInput(m.wParam, m.lParam);
    else api.DispatchMessage(m);
}


unsafe void _WmInput(nint wParam, nint lParam) {
    print.it("WM_INPUT", wParam, lParam);
    
    int size = 0;
    if (api.GetRawInputData(lParam, api.RID_INPUT, null, ref size, Marshal.SizeOf<api.RAWINPUTHEADER>()) == -1) return;
    var b = stackalloc byte[size];
    if (api.GetRawInputData(lParam, api.RID_INPUT, b, ref size, Marshal.SizeOf<api.RAWINPUTHEADER>()) != size) return;
    ref api.RAWINPUT raw = ref *(api.RAWINPUT*)b;
    if (raw.header.hDevice == 0) return;
    
    //print.it(raw.header.dwType);
    //print.it(raw.data.hid.dwCount);
    
    //but I don't know how to read the data for non-keyboard/mouse devices. It depends on device. Ask AI.

}

#pragma warning disable 649, 169 //field never assigned/used
unsafe class api : NativeApi {
    [
DllImport("user32.dll", SetLastError = true)]
    internal static extern bool RegisterRawInputDevices(in RAWINPUTDEVICE pRawInputDevices, uint uiNumDevices, int cbSize);
    
    internal struct RAWINPUTDEVICE {
        public ushort usUsagePage;
        public ushort usUsage;
        public uint dwFlags;
        public wnd hwndTarget;
    }

    
    internal const uint RIDEV_INPUTSINK = 0x100;
    
    [
DllImport("user32.dll", EntryPoint = "GetMessageW", SetLastError = true)]
    internal static extern bool GetMessage(out MSG lpMsg, wnd hWnd = default, uint wMsgFilterMin = 0, uint wMsgFilterMax = 0);
    
    [
DllImport("user32.dll", EntryPoint = "DispatchMessageW")]
    internal static extern nint DispatchMessage(in MSG lpMsg);
    
    internal const ushort WM_INPUT = 0xFF;
    
    [
DllImport("user32.dll")]
    internal static extern int GetRawInputData(nint hRawInput, uint uiCommand, void* pData, ref int pcbSize, int cbSizeHeader);
    
    internal const uint RID_INPUT = 0x10000003;
    
    internal struct RAWINPUTHEADER {
        public uint dwType;
        public uint dwSize;
        public nint hDevice;
        public nint wParam;
    }

    
    internal struct RAWINPUT {
        public RAWINPUTHEADER header;
        public _data_e__Union data;
        
        [
StructLayout(LayoutKind.Explicit)]
        public struct _data_e__Union {
            [
FieldOffset(0)] public RAWMOUSE mouse;
            [
FieldOffset(0)] public RAWKEYBOARD keyboard;
            [
FieldOffset(0)] public RAWHID hid;
        }
    }

    
    internal struct RAWHID {
        public uint dwSizeHid;
        public uint dwCount;
        public FlexibleArray<byte> bRawData;
    }

    
    internal struct RAWKEYBOARD {
        public ushort MakeCode;
        public ushort Flags;
        public ushort Reserved;
        public ushort VKey;
        public uint Message;
        public uint ExtraInformation;
    }

    
    internal struct RAWMOUSE {
        public ushort usFlags;
        public _Anonymous_e__Union u_;
        public uint ulRawButtons;
        public int lLastX;
        public int lLastY;
        public uint ulExtraInformation;
        
        [
StructLayout(LayoutKind.Explicit)]
        public struct _Anonymous_e__Union {
            [
FieldOffset(0)] public uint ulButtons;
            [
FieldOffset(0)] public _Anonymous_e__Struct n_;
            
            public struct _Anonymous_e__Struct {
                public ushort usButtonFlags;
                public ushort usButtonData;
            }
        }
    }
}

#pragma warning restore 649, 169 //field never assigned/used
Code:
Copy      Help
// script "Raw input device info.cs"
//Prints some info of raw input devices other than keyboard and mouse.

print.clear();

unsafe {
    int nDev = 0, bSize = 0;
    api.GetRawInputDeviceList(null, ref nDev, sizeof(api.RAWINPUTDEVICELIST));
    var a = new api.RAWINPUTDEVICELIST[nDev];
    nDev = api.GetRawInputDeviceList(a, ref nDev, sizeof(api.RAWINPUTDEVICELIST));
    foreach (var d in a) {
        //print.it(d.dwType, d.hDevice);
        if (d.dwType != api.RIM_TYPEHID) continue;
        
        //get name
        api.GetRawInputDeviceInfo(d.hDevice, api.RIDI_DEVICENAME, null, ref bSize);
        var name = string.Create(bSize - 1, 0, (span, _) => {
            fixed(char* p = span) api.GetRawInputDeviceInfo(d.hDevice, api.RIDI_DEVICENAME, p, ref bSize);
        });

        print.it(name);
        
        //get other info
        var di = new api.RID_DEVICE_INFO { cbSize = sizeof(api.RID_DEVICE_INFO) };
        api.GetRawInputDeviceInfo(d.hDevice, api.RIDI_DEVICEINFO, &di, ref di.cbSize);
        print.it($"Usage page: {di.u_.hid.usUsagePage},  usage: {di.u_.hid.usUsage}");
    }
    
}



#pragma warning disable 649, 169 //field never assigned/used
unsafe class api : NativeApi {
    [
DllImport("user32.dll", SetLastError = true)]
    internal static extern int GetRawInputDeviceList([Out] RAWINPUTDEVICELIST[] pRawInputDeviceList, ref int puiNumDevices, int cbSize);
    
    internal struct RAWINPUTDEVICELIST {
        public nint hDevice;
        public uint dwType;
    }


internal const uint RIM_TYPEHID = 0x2;

[
DllImport("user32.dll", EntryPoint = "GetRawInputDeviceInfoW", SetLastError = true)]
internal static extern int GetRawInputDeviceInfo(nint hDevice, uint uiCommand, void* pData, ref int pcbSize);

internal const uint RIDI_DEVICENAME = 0x20000007;
internal const uint RIDI_DEVICEINFO = 0x2000000B;

internal struct RID_DEVICE_INFO {
    public int cbSize;
    public uint dwType;
    public _Anonymous_e__Union u_;

    [
StructLayout(LayoutKind.Explicit)]
    public struct _Anonymous_e__Union {
        [
FieldOffset(0)] public RID_DEVICE_INFO_MOUSE mouse;
        [
FieldOffset(0)] public RID_DEVICE_INFO_KEYBOARD keyboard;
        [
FieldOffset(0)] public RID_DEVICE_INFO_HID hid;
    }
}


internal struct RID_DEVICE_INFO_HID {
    public uint dwVendorId;
    public uint dwProductId;
    public uint dwVersionNumber;
    public ushort usUsagePage;
    public ushort usUsage;
}


internal struct RID_DEVICE_INFO_KEYBOARD {
    public uint dwType;
    public uint dwSubType;
    public uint dwKeyboardMode;
    public uint dwNumberOfFunctionKeys;
    public uint dwNumberOfIndicators;
    public uint dwNumberOfKeysTotal;
}


internal struct RID_DEVICE_INFO_MOUSE {
    public uint dwId;
    public uint dwNumberOfButtons;
    public uint dwSampleRate;
    public BOOL fHasHorizontalWheel;
}
}

#pragma warning restore 649, 169 //field never assigned/used
#3
Thank you very much Gintaras! I will check it out
#4
Thanks Again Gintaras,
When I run Raw input device info, I get the following output:

\\?\HID#VID_0554&PID_1001&MI_03#8&22bd9473&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
Usage page: 1,  usage: 0

When I input these values   Usage page: 1,  usage: 0    into Raw input.cs , I get the error:

Au.Types.AuException: The parameter is incorrect (87).
   at line 12 in Raw input.cs
   >>

pointing to the line:

if (!api.RegisterRawInputDevices(rid, 1, Marshal.SizeOf<api.RAWINPUTDEVICE>())) throw new AuException(lastError.message);
#5
I'm not familiar with usage pages, therefore I asked AI. It recommended to try `usUsage` = 1, 2, 3 ... until maybe it will work.

See also `RAWINPUTDEVICE` documentation. Try flag `RIDEV_PAGEONLY` (0x20).
C# code:
    usUsagePage = 1, usUsage = 0,
    dwFlags = api.RIDEV_INPUTSINK | 0x20,
But then, if usUsagePage = 1, you'll receive WM_INPUT for all keyboards, mice, etc, and will need to filter out unwanted notifications in _WmInput.
#6
Thank you, flag `RIDEV_PAGEONLY did the trick. I am able to listen to messages from the device. However, I am getting random output. For example, this is what I get after I keep pressing the same button on the device. I tried using AI, so far to no avail,

WM_INPUT, 1, 392956119
WM_INPUT, 1, 493684811
WM_INPUT, 1, 193792377
WM_INPUT, 1, 31263161
WM_INPUT, 1, 86903265
WM_INPUT, 1, 193857913
WM_INPUT, 1, 462750461
WM_INPUT, 1, 57606997
WM_INPUT, 1, 194185593
WM_INPUT, 1, 394135767
WM_INPUT, 1, 877789405
WM_INPUT, 1, 194251129
WM_INPUT, 1, 466420477
WM_INPUT, 1, 86968801
WM_INPUT, 1, 59900757
WM_INPUT, 1, 466616651
#7
This `_WmInput` function prints raw data. Tested with a joystick.
 
Code:
Copy      Help
unsafe void _WmInput(nint wParam, nint lParam) {
    //print.it("WM_INPUT");
    
    //get header

    
    int headerSize = sizeof(api.RAWINPUTHEADER), size = headerSize;
    api.RAWINPUTHEADER rih = default;
    if (api.GetRawInputData(lParam, api.RID_HEADER, &rih, ref size, headerSize) <= 0) return;
    //print.it(rih.dwType); //0 mouse, 1 keyboard, 2 other
    if (rih.dwType != 2) return;
    
    //get data
    
    var b = stackalloc byte[size = rih.dwSize];
    if (api.GetRawInputData(lParam, api.RID_INPUT, b, ref size, headerSize) <= 0) return;
    ref api.RAWINPUT raw = ref *(api.RAWINPUT*)b;
    
    //but I don't know how to read the data for non-keyboard/mouse devices. It depends on device. Ask AI.
    
    //print raw data

    
    print.it(raw.data.hid.dwCount, raw.data.hid.dwSizeHid);
    var data = raw.data.hid.bRawData.AsSpan(raw.data.hid.dwSizeHid);
    print.it(data, 8);
    print.it($"Joystick button: {data[49]}. Values of axis X and Y: {BitConverter.ToUInt32(data[1..])} {BitConverter.ToUInt32(data[5..])}.");
}

Add this line to the `api` class: internal const uint RID_HEADER = 0x10000005;

Example output:
 
Code:
Copy      Help
1, 65
01 00 40 00 00 00 40 00     ..@...@.
00 00 40 00 00 01 00 00     ..@.....
00 00 00 00 00 00 00 00     ........
00 00 00 00 00 00 00 00     ........
00 00 00 00 00 00 00 00     ........
00 68 30 F9 40 06 02 00     .h0.@...
00 00 00 00 00 00 00 00     ........
00 00 00 00 00 00 00 00     ........
00                          .

The data depends on device and probably is undocumented, unless it is a well-known standard device (but I don't know where it's documented). For example, when testing with a joystick, some bytes are different depending on button and axis values.
#8
You are incredible!


Forum Jump:


Users browsing this thread: 1 Guest(s)