02-25-2026, 05:25 PM
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