Posts: 77
Threads: 23
Joined: Aug 2019
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
Posts: 12,272
Threads: 144
Joined: Dec 2002
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.
// 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
// 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
Posts: 77
Threads: 23
Joined: Aug 2019
Thank you very much Gintaras! I will check it out
Posts: 77
Threads: 23
Joined: Aug 2019
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);
Posts: 12,272
Threads: 144
Joined: Dec 2002
02-26-2026, 02:44 PM
(This post was last modified: 02-26-2026, 02:59 PM by Gintaras.)
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).
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.
Posts: 77
Threads: 23
Joined: Aug 2019
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
Posts: 12,272
Threads: 144
Joined: Dec 2002
02-27-2026, 09:19 AM
(This post was last modified: 02-27-2026, 09:22 AM by Gintaras.)
This `_WmInput` function prints raw data. Tested with a joystick.
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:
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.
Posts: 77
Threads: 23
Joined: Aug 2019
|