Use RawImport to separate a USB HID barcode scanner and keyboard events

I’m really beginning to dislike the WordPress code window.  One of those nut fuckers updated it again.  It worked, now it works like shit.  If it ain’t broke, fix it until it is.

I have a Honeywell 7580 Genisis barcode scanner configured as a USB keyboard.  The challenge was to separate the keyboard from the barcode scanners events.  I found a partial solution on CodeProject: Using Raw Input from C# to handle multiple keyboards. I’m NOT going to post Raw Inputs source, so download the latest version. Yeah, yeah, It’s been rewritten to something new and shiny but it bogs my old box down.

After digging up the Honeywell 7580 hardware ID from the device manager, I added a simple device enumerated filter to Raw Input.

Replace RawKeyboards EnumeratedDevices method.
RawKeyboard.cs

public void EnumerateDevices(string filterByDeviceID)
{
lock (_padLock)
{
_deviceList.Clear();

var keyboardNumber = 0;

var globalDevice = new DeviceKeyPressEvent
{
DeviceName = "Global Keyboard",
DeviceHandle = IntPtr.Zero,
DeviceType = Win32.GetDeviceType(DeviceType.RimTypekeyboard),
Name = "Fake Keyboard. Some keys (ZOOM, MUTE, VOLUMEUP, VOLUMEDOWN) are sent to rawinput with a handle of zero.",
Source = keyboardNumber++.ToString(CultureInfo.InvariantCulture)
};

//4-Sept-2021 Ya don't need to add this fake device.
// _deviceList.Add(globalDevice.DeviceHandle, globalDevice);

var numberOfDevices = 0;
uint deviceCount = 0;
var dwSize = (Marshal.SizeOf(typeof(Rawinputdevicelist)));

if (Win32.GetRawInputDeviceList(IntPtr.Zero, ref deviceCount, (uint)dwSize) == 0)
{
var pRawInputDeviceList = Marshal.AllocHGlobal((int)(dwSize * deviceCount));
Win32.GetRawInputDeviceList(pRawInputDeviceList, ref deviceCount, (uint)dwSize);

for (var i = 0; i < deviceCount; i++) 
{ 
uint pcbSize = 0; 
// On Window 8 64bit when compiling against .Net > 3.5 using .ToInt32 you will generate an arithmetic overflow. Leave as it is for 32bit/64bit applications
var rid = (Rawinputdevicelist)Marshal.PtrToStructure(new IntPtr((pRawInputDeviceList.ToInt64() + (dwSize * i))), typeof(Rawinputdevicelist));

Win32.GetRawInputDeviceInfo(rid.hDevice, RawInputDeviceInfo.RIDI_DEVICENAME, IntPtr.Zero, ref pcbSize);

if (pcbSize <= 0) continue;

var pData = Marshal.AllocHGlobal((int)pcbSize);
Win32.GetRawInputDeviceInfo(rid.hDevice, RawInputDeviceInfo.RIDI_DEVICENAME, pData, ref pcbSize);
var deviceName = Marshal.PtrToStringAnsi(pData);

//4-Sept-2021 Added filterByDeviceID to only register select devices like specific barcode scanners.
if (filterByDeviceID==null || deviceName.Contains(filterByDeviceID) || string.IsNullOrEmpty(filterByDeviceID))
{
if (rid.dwType == DeviceType.RimTypekeyboard || rid.dwType == DeviceType.RimTypeHid)
{
var deviceDesc = Win32.GetDeviceDescription(deviceName);

var dInfo = new DeviceKeyPressEvent
{
DeviceName = Marshal.PtrToStringAnsi(pData),
DeviceHandle = rid.hDevice,
DeviceType = Win32.GetDeviceType(rid.dwType),
Name = deviceDesc,
Source = keyboardNumber++.ToString(CultureInfo.InvariantCulture)
};

if (!_deviceList.ContainsKey(rid.hDevice))
{
numberOfDevices++;
_deviceList.Add(rid.hDevice, dInfo);
}
}
}
Marshal.FreeHGlobal(pData);
}

Marshal.FreeHGlobal(pRawInputDeviceList);

NumberOfKeyboards = numberOfDevices;
Debug.WriteLine("EnumerateDevices() found {0} Keyboard(s)", NumberOfKeyboards);
return;
}
}

throw new Win32Exception(Marshal.GetLastWin32Error());
}

 

Remove RawInput constructor and replace it with these two constructors.

RawInput.cs

public RawInput(IntPtr parentHandle, bool captureOnlyInForeground)
{
AssignHandle(parentHandle);

_keyboardDriver = new RawKeyboard(parentHandle, captureOnlyInForeground);
//4-Sept-2021  pass a null paramater - get all devices
_keyboardDriver.EnumerateDevices(null);
_devNotifyHandle = RegisterForDeviceNotifications(parentHandle);
}

//4-Sept-2021  constructor with a device id to filter by
public RawInput(IntPtr parentHandle, bool captureOnlyInForeground, string filterByDeviceID)
{
AssignHandle(parentHandle);

_keyboardDriver = new RawKeyboard(parentHandle, captureOnlyInForeground);
_keyboardDriver.EnumerateDevices(filterByDeviceID);
_devNotifyHandle = RegisterForDeviceNotifications(parentHandle);
}

 

RawInput absorbs all KeyDown events.  While the barcode scanner KeyPress events work, the keyboard is now dead.  Replace PreFilterMessage with this:

PreMessageFilter.cs

public bool PreFilterMessage(ref Message m)
{

if (m.Msg != Win32.WM_INPUT)
{
//4-Sept-2021  Allow any non WM_INPUT message to pass-through
return false;
}

return m.Msg == Win32.WM_KEYDOWN;
}

 

I still call it with the usual:

private readonly RawInput _rawinput;
const bool CaptureOnlyInForeground = true;
StringBuilder rawInputSb = new StringBuilder();
bool rawInputBounce = false;

public SomeForm()
{
_rawinput = new RawInput(Handle, CaptureOnlyInForeground , @"HID#VID_0C2E&PID_0206");
_rawinput.AddMessageFilter();
_rawinput.DeviceKeyPressed += OnKeyPressed;
}

private void OnKeyPressed(object sender, RawInputEventArg e)
{
//4-Sept-2021  rawInput sends TWO characters, not one.  
//4-Sept-2021  Added this simple debounce to stop it.  Might further look into it.
if (!rawInputBounce)
{

//4-Sept-2021  My scanner is configured to send a CR after a scan.
if (e.KeyPressEvent.VKeyName.Equals("ENTER") && rawInputSb.Length > 0)
{
//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
//However you want to handle the barcode scanner input is up to you
//-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
string doWithMeAsYouPlease = rawInputSb.ToString();

//4-Sept-2021 Clear the buffer
rawInputSb.Clear();
}
else
{
//4-Sept-2021 Save the barcode character to an SB
rawInputSb.Append(e.KeyPressEvent.VKey.ToString(CultureInfo.InvariantCulture));
}

}
//4-Sept-2021 Debouncy
rawInputBounce=!rawInputBounce;
}

 

Leave a Reply