Distinguishing Barcode Scanners from the Keyboard in WinForms

Like any business, small or otherwise, we use barcodes for lots of things–for picking orders, for looking up totes, for processing returns, and for checking in inventory, to name but a few–and so our internal Windows Forms-based application asks for barcodes a lot:

To scan a barcode, just focus that textbox and use the scan gun. Easy!

To scan a barcode, just focus that textbox and use the scan gun. Easy!

We have three different types of barcode scan guns at work, but they all work in more or less the same way: they show up as a USB HID device in Windows, and when you scan a barcode, Windows and WinForms applications simply see it as if someone were typing a bunch of numbers on the keyboard really quickly. This makes integration easy: wherever you have a Textbox, you can click on it and scan a barcode and it just works.

By scanning barcodes everywhere, we reduce miscounts and improve our accuracy. But there is one part of this select-the-textbox-and-scan model that is incredibly irritating.

I was first alerted to the problem by my highly irritated coworker storming into my office. He was annoyed that he had been checking in inventory for a particularly large shipment and hadn’t noticed until the end of his work that he or the scan gun’s cord had jiggled the keyboard and caused the textbox to lose focus. The scan gun beeped happily when it read a barcode, but the warehouse computers lack sound and so there was no obvious way to alert him that, um, some of those last 100 barcode scans had been sent to an hwnd that doesn’t give a flying fart about them. The result? He had to start over.

The typical programmer response was “Well, make sure that the textbox actually has focus.” And this policy worked for a short while until I was the one checking in a particularly large shipment one day and happened to do the exact same thing. I was irritated, to say the least. Time to solve the problem.

Developing a strategy

So how would I like this to work? Well, I envision creating a class called BarcodeScannerListener. This class would expose a BarcodeScanned event that would, you know, be fired when a barcode was scanned no matter which control or form currently had focus; the event arguments would be something like BarcodeScannedEventArgs that contained the complete scanned barcode and, if we wanted to get real fancy, perhaps some additional information about the device. Then, all I have to do is pass this instance of BarcodeScannerListener around my application to interested parties (through dependency injection or some nonsense, or by making it a singleton), and I’d have cut out a lot of textbox-related scanning code in one fell swoop.

So we have three hurdles to overcome:

  1. We need to be able to listen to keyboard strokes no matter which form or control currently has focus.
  2. Since we’re listening to this cacophony of keyboard strokes from all controls in the application, we need to be able to distinguish between strokes that come from the keyboard and strokes that come from the barcode scan gun.
  3. Since it’s conceivable that a control that can’t receive input or shouldn’t receive input might be focused when a barcode is scanned, we need to suppress the Windows messages related to the keyboard strokes generated by the scan gun. (How annoying would it be to hear 12 Windows “ding” sounds upon scanning a barcode, or how annoying would it be to have a UPC show up in that e-mail reply you were typing to a customer when you scanned it look up inventory?)

Our ideal high-level overview would something like this:

Easier said than done.

Easier said than done.

So let’s take a stab at this.

Serial emulation mode?

Some barcode scanners, in addition to supporting USB HID information mode in which they appear as a barcode scanner, also support a serial port emulation mode. In this case, we would communicate with the scanner by sending and receiving commands through the SerialPort class as if it were any other serial device, and you usually tell the scanner to go into this mode by scanning a special “configuration barcode” that its firmware recognizes, found in the owner’s manual. This would be great if

  • the scanner supported such a serial emulation mode;
  • the scanner was meant to be used by our application and our application alone; and
  • we didn’t want the scanner to generate keystrokes anywhere in the operating system.

Unfortunately, not all of our scanners at work support this mode, and even if they did, the scanner is intended to be used by multiple applications (such as FedEx Ship Manager, which requires it to operate as a keyboard wedge). So it seems like the easy solution is out.

Two steps forward, one step back

The first thing that we need to do is to figure out how to get access to the keystroke information no matter which form or control has focus. There are two ways to go about doing this: one is to use the Raw Input API and the other is to use low-level keyboard hook.

But since we also need to know which device the keyboard stroke came from, it looks like the Raw Input API will be the winner; the KBDLLHOOKSTRUCT structure that’s provided by SetWindowsHookEx() doesn’t tell us any device information. So let’s put keyboard hooks on the back burner for now and learn about the Raw Input API.

Diddling around with the Raw Input API

By default, no applications in Windows receive raw input. You have to register for it. And when you’ve registered an hwnd to receive raw input, that hwnd will start getting WM_INPUT messages pumped to it. The WM_INPUT message contains a RAWINPUTHEADER structure that can be passed to various functions in the Raw Input API that can tell you scads of information about the input event that just occurred. What’s important to note is that, with the appropriate flags, these WM_INPUT messages get sent to the hwnd that you register with the API for every input event regardless of whether or not your hwnd currently has focus. This satisfies our first requirement of needing to be able to see incoming keyboard stroke events regardless of whether or not some stupid textbox has focus. The regular device-independent input events like WM_CHAR and WM_KEYDOWN will still get generated for the hwnd; all you’re saying is that “hey, I want to hear about WM_INPUT and get all the gory details, too.”

Using the Raw Input API itself is kind of weird, and the mystical incantations of P/Invoke only make it weirder. This article on CodeProject helped me a lot in this regard, but I still had to drudge through the MSDN documentation and, of course, pinvoke.net.

Time to get started on BarcodeScannerListener. Since we’ll be piggybacking on an hwnd to receive the WM_INPUT messages, we might as well use one that’s going to be around for the lifetime of our application. In this case, this means my main form. But it’d be nice to design this as an independent class so that any consumers don’t need to know about the MainForm. What do you do when you need to override a WndProc without touching the class itself? You got it, it’s NativeWindow to the rescue again.

    public class BarcodeScannerListener : NativeWindow
    {
        /// <summary>
        /// Initializes a new instance of the BarcodeScannerListener
        /// class. The raw input devices that this class will listen to are
        /// registered with the given window handle.
        /// </summary>
        /// <param name="form">the form that should listen for
        /// barcode scans</param>
        /// <exception cref="ArgumentNullException">if the form is null</exception>
        /// <exception cref="ApplicationException">if we are unable to register
        /// for raw input devices</exception>
        /// <exception cref="ConfigurationErrorsException">if an error occurs
        /// during configuration</exception>
        public BarcodeScannerListener(Form form)
        {
            IntPtr hwnd;
 
            if (form == null)
            {
                throw new ArgumentNullException("form");
            }
 
            hwnd = form.Handle;
 
            HookRawInput(hwnd); // We'll take a look at this in a minute
            this.HookHandleEvents(form);
 
            this.AssignHandle(hwnd);
        }
 
        /// <summary>
        /// Hooks into the form's HandleCreated and HandleDestoryed events
        /// to ensure that we start and stop listening at appropriate times.
        /// </summary>
        /// <param name="form">the form to listen to</param>
        private void HookHandleEvents(Form form)
        {
            form.HandleCreated += this.OnHandleCreated;
            form.HandleDestroyed += this.OnHandleDestroyed;
        }
 
        /// <summary>
        /// When the form's handle is created, let's hook into it so we can see
        /// the WM_INPUT event.
        /// </summary>
        /// <param name="sender">the form whose handle was created</param>
        /// <param name="e">the event arguments</param>
        private void OnHandleCreated(object sender, EventArgs e)
        {
            this.AssignHandle(((Form)sender).Handle);
        }
 
        /// <summary>
        /// When the form's handle is destroyed, let's unhook from it so we stop
        /// listening and allow the OS to free up its resources.
        /// </summary>
        /// <param name="sender">the form whose handle was destroyed</param>
        /// <param name="e">the event arguments</param>
        private void OnHandleDestroyed(object sender, EventArgs e)
        {
            this.ReleaseHandle();
        }
    }

Most of the code above is just the kind of bullcrap that we need to do to make sure that we release resources properly when the form’s hwnd gets destroyed. The real meat we want to figure out is that HookRawInput() function in the constructor. This is what does the dirty work of registering our hwnd with the Raw Input API:

/// <summary>
/// Registers ourselves to listen to raw input from keyboard-like devices.
/// </summary>
/// <param name="hwnd">the handle of the form that will receive the raw
/// input messages</param>
/// <exception cref="InvalidOperationException">if the call to register with the
/// raw input API fails for some reason</exception>
private static void HookRawInput(IntPtr hwnd)
{
	NativeMethods.RAWINPUTDEVICE[] rid;
 
	rid = new NativeMethods.RAWINPUTDEVICE[1];
 
	rid[0].usUsagePage = 0x01;      // USB HID Generic Desktop Page
	rid[0].usUsage = 0x06;          // Keyboard Usage ID
	rid[0].dwFlags = NativeMethods.RawInputDeviceFlags.RIDEV_INPUTSINK;
	rid[0].hwndTarget = hwnd;
 
	if (!NativeMethods.RegisterRawInputDevices(rid, (uint)rid.Length, (uint)Marshal.SizeOf(rid[0])))
	{
		InvalidOperationException e;
 
		e = new InvalidOperationException(
			"The barcode scanner listener could not register for raw input devices.",
			new Win32Exception());
		throw e;
	}
}

Let’s stop for a minute and take a look at what the hell this is doing. The goal here is to P/Invoke the Win32 method called RegisterRawInputDevices(). This takes a variable length array of RAWINPUTDEVICE structures as a parameter. These structures tell us what classes of devices that we want to receive WM_INPUT messages for. Note that we can’t ask for a particular device, and this is what makes things so interestingly complicated–we’re going to get all of the notifications for all keyboard-like devices and we’re going to have to filter out the ones from the barcode scan gun ourselves. But that comes much later.

So what we do here is register ourselves with one class of devices, those devices that are keyboard-like. (Unfortunately, there is no HID usage page for barcode scan guns yet.) The parameters in usUsagePage and usUsage are magic numbers. There is a whole slew of these magic numbers on the USB consortium’s Web site. Since all of desktop scan guns act as keyboard wedges, I went with the usage page and usage (really, could they have chosen terms that were any more confusingly similar?) for a keyboard device and it just worked.

The RIDEV_INPUTSINK flag is what tells Windows to send us WM_INPUT messages regardless of the hwnd that they’re being directed to. This means that even if our main form is minimized and the user has, say, Notepad focused and scans a barcode, we’ll still see the event. I’m not sure if that’s really cool or really spooky.

The actual extern declaration is sitting in a class called NativeMethods. The declaration really isn’t all that interesting and if you want to look at it, you can take a look at the sample code.

Great! So now we’re all set up to get WM_INPUT messages. Let’s check this by actually implementing our version of WndProc in our BarcodeScannerListener class:

/// <summary>
/// Hook into the form's WndProc message. We listen for WM_INPUT and do
/// special processing on the raw data.
/// </summary>
/// <param name="m">the message</param>
[SecurityPermission(
   SecurityAction.LinkDemand,
   Flags = SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref Message m)
{
	switch (m.Msg)
	{
		case NativeMethods.WM_INPUT:
			this.ProcessRawInputMessage(m.LParam);
			// There's more work to do here, but we'll get to that in a minute
			break;
	}
 
	base.WndProc(ref m);
}

So, this is pretty simple. If we get a WM_INPUT, we call our ProcessRawInputMessage() function on it. Otherwise, we don’t really care about the message. So let’s take a look at that beast of a function. Get your scroll wheel finger ready because it’s a doozy:

/// <summary>
/// Process the given WM_INPUT message.
/// </summary>
/// <param name="rawInputHeader">the rawInputHeader of the message</param>
/// <returns>whether or not the keystroke was handled</returns>
private bool ProcessRawInputMessage(IntPtr rawInputHeader)
{
	bool handled;
	uint size;
 
	handled = false;
	size = 0;
 
	// First we call GetRawInputData() to set the value of size, which
	// we will the nuse to allocate the appropriate amount of memory in
	// the buffer.
	if (NativeMethods.GetRawInputData(
			rawInputHeader,
			NativeMethods.RawInputCommandFlag.RID_INPUT,
			IntPtr.Zero,
			ref size,
			(uint)Marshal.SizeOf(typeof(NativeMethods.RAWINPUTHEADER))) == 0)
	{
		IntPtr buffer;
		BarcodeScannerDeviceInfo deviceInfo; // I'll get to this later
		NativeMethods.RAWINPUT raw;
 
		buffer = Marshal.AllocHGlobal((int)size);
 
		try
		{
			if (NativeMethods.GetRawInputData(
					rawInputHeader,
					NativeMethods.RawInputCommandFlag.RID_INPUT,
					buffer,
					ref size,
					(uint)Marshal.SizeOf(typeof(NativeMethods.RAWINPUTHEADER))) == size)
			{
				raw = (NativeMethods.RAWINPUT)Marshal.PtrToStructure(buffer, typeof(NativeMethods.RAWINPUT));
 
				// This is what filters out for the right device we're looking for.
				// How'd I get that hDevice magic number? Keep reading.
				if (this.devices.TryGetValue(raw.header.hDevice, out deviceInfo))
				{
					handled = true;
 
					if (raw.header.dwType == NativeMethods.RawInputType.RIM_TYPEKEYBOARD)
					{
						if (raw.keyboard.Message == NativeMethods.WM_KEYDOWN)
						{
							StringBuilder localBuffer;
							byte[] state;
 
							localBuffer = new StringBuilder();
							state = new byte[256];
 
							if (NativeMethods.GetKeyboardState(state))
							{
								if (NativeMethods.ToUnicode(
										raw.keyboard.VKey,
										raw.keyboard.MakeCode,
										state,
										localBuffer,
										64,
										0) > 0)
								{
									if (localBuffer.Length == 1 && localBuffer[0] == 0x4)
									{
										this.FireBarcodeScanned(deviceInfo);
									}
									else
									{
										this.keystrokeBuffer.Append(localBuffer.ToString());
									}
								}
							}
						}
					}
				}
			}
		}
		finally
		{
			Marshal.FreeHGlobal(buffer);
		}
	}
 
	return handled;
}

After working with Java and C# for a few years, this kind of API is a reminder of how boring and tedious manual memory management can be. To get details about the WM_INPUT message, including important things like the device it’s coming from, we need to call a method in the Win32 API called GetRawInputHeader(). Since the size of the data can vary, we have to call the method once passing a null reference to a buffer (IntPtr.Zero) and it will populate the size parameter for us. Then we can allocate memory for a buffer of that size and call the function again. Then we marshal the data in that buffer to a RAWINPUT struct, which is the bad boy that tells us what device the keyboard stroke is originating from.

The rest of the method is now jumping ahead: if the device handle (hDevice) is one that we’re watching for (namely, the device handle of the barcode scan gun) and the message is a WM_KEYDOWN event, then we get the current keyboard state and call ToUnicode() to convert the current “keyboard” state into an actual usable character. If a character is returned, we’ll add it to a StringBuilder called keystrokeBuffer. Since all of our scan guns are configured to send an EOT character (ASCII 0×4) at the end of a barcode, we’ll watch for that and if we see it we’ll clear out our buffer and fire the BarcodeScanned event.

Why go to the trouble of GetKeyboardState() and ToUnicode()? Well, remember that the barcode scan gun really is just simulating a keyboard. So when it tries to send our EOT character at the end of the barcode, it doesn’t actually press an EOT key–there’s no such key on your keyboard. Instead it presses Control and then it presses D. These two keyboard strokes get sent in two different WM_INPUTs and two different calls to this method. If we’d been just looking at the virtual keycode on each method call, we’d end up with a lot of bogus characters. Let’s let ToUnicode() do the hard work of figuring out whether a letter is lowercase or uppercase, or if it’s trying to output a $ instead of a 3.

Blocking the keyboard strokes

So our third requirement was to block the keyboard strokes from actually reaching the control if they originated from our scan gun. We might be tempted to revisit our old friend the low-level keyboard hook for this. Indeed, we might spend three hours pursuing this option only to realize something quite unfortunate: the low-level keyboard hook, which provides no device-specific information, runs before any WM_INPUT messages get sent to us by the Raw Input API. That means that by the time that we have enough information to decide whether or not we want to swallow the keyboard stroke, it’s already too late–we’ve missed our chance to swallow it because the keyboard hook callback is long gone.

A less elegant, possibly race-condition prone condition is to just zap all of the WM_KEYDOWN events in the WndProc method if we handled the keyboard stroke:

protected override void WndProc(ref Message m)
{
	switch (m.Msg)
	{
		case NativeMethods.WM_INPUT:
			if (this.ProcessRawInputMessage(m.LParam))
			{
				// This is the new stuff
				NativeMethods.MSG message;
				NativeMethods.PeekMessage(
					out message,
					IntPtr.Zero,
					NativeMethods.WM_KEYDOWN,
					NativeMethods.WM_KEYDOWN,
					NativeMethods.PeekMessageRemoveFlag.PM_REMOVE);
			}
 
			break;
	}
 
	base.WndProc(ref m);
}

We P/Invoke a call to PeekMessage() with a parameter of PM_REMOVE to do this. By passing in a null pointer as the hwnd parameter, we will zap all of the WM_KEYDOWN messages for handles that are running on the current thread. Since all of app’s UI is running in a single-thread, we can be sure we’re zapping the events regardless of which of our controls had focus. This seems to work well enough, especially as I can’t think of a scenario where a user would be typing on the keyboard and scanning a barcode at the same time. But I can envision a scenario where we try to clear the WM_KEYDOWN but it hasn’t be posted yet, allowing a stray character to get through. I haven’t seen it in practice, but it’s something to keep in mind.

Figuring out the device handle

So the last little bit is to figure out the device handle of a barcode scan gun. This is so that we can compare it against the hDevice handle that comes back in the RAWINPUT structure in the Raw Input API. To our constructor we’ll add a method invocation for InitializeBarcodeScannerDeviceHandles() and define that function thusly:

/// <summary>
/// Enumerates devices provided by GetRawInputDeviceList. We'll only listen
/// to these devices.
/// </summary>
/// <exception cref="ConfigurationErrorsException">if an error occurs
/// during configuration</exception>
private void InitializeBarcodeScannerDeviceHandles()
{
	BarcodeScannerListenerConfigurationSection config;
	BarcodeScannerListenerConfigurationElementCollection hardwareIdsConfig;
	List<string> hardwareIds;
	uint numDevices;
	uint size;
 
	config = BarcodeScannerListenerConfigurationSection.GetConfiguration();
	hardwareIdsConfig = config.HardwareIds;
	hardwareIds = new List<string>();
	numDevices = 0;
	size = (uint)Marshal.SizeOf(typeof(NativeMethods.RAWINPUTDEVICELIST));
 
	foreach (BarcodeScannerListenerConfigurationElement hardwareId in hardwareIdsConfig)
	{
		hardwareIds.Add(hardwareId.Id);
	}
 
	// First, we get the number of raw input devices in the list by passing
	// in IntPtr.Zero. Then we allocate sufficient memory and retrieve the
	// entire list.
	if (NativeMethods.GetRawInputDeviceList(IntPtr.Zero, ref numDevices, size) == 0)
	{
		IntPtr rawInputDeviceList;
 
		rawInputDeviceList = Marshal.AllocHGlobal((int)(size * numDevices));
		if (NativeMethods.GetRawInputDeviceList(
			rawInputDeviceList,
			ref numDevices,
			size) != uint.MaxValue)
		{
			// Next, we iterate through the list, discarding undesired items
			// and retrieving further information on the barcode scanner devices
			for (int i = 0; i < numDevices; ++i)
			{
				uint pcbSize;
				NativeMethods.RAWINPUTDEVICELIST rid;
 
				pcbSize = 0;
				rid = (NativeMethods.RAWINPUTDEVICELIST)Marshal.PtrToStructure(
					new IntPtr((rawInputDeviceList.ToInt32() + (size * i))),
					typeof(NativeMethods.RAWINPUTDEVICELIST));
 
				if (NativeMethods.GetRawInputDeviceInfo(
					rid.hDevice,
					NativeMethods.RawInputDeviceInfoCommand.RIDI_DEVICENAME,
					IntPtr.Zero,
					ref pcbSize) >= 0)
				{
					if (pcbSize > 0)
					{
						string deviceName;
						string friendlyName;
						BarcodeScannerDeviceInfo info;
						IntPtr data;
 
						data = Marshal.AllocHGlobal((int)pcbSize);
						if (NativeMethods.GetRawInputDeviceInfo(
							rid.hDevice,
							NativeMethods.RawInputDeviceInfoCommand.RIDI_DEVICENAME,
							data,
							ref pcbSize) >= 0)
						{
							deviceName = (string)Marshal.PtrToStringAnsi(data);
 
							if ((from hardwareId in hardwareIds
								 where deviceName.Contains(hardwareId)
								 select hardwareId).Count() > 0)
							{
								friendlyName = GetDeviceFriendlyName(deviceName);
 
								info = new BarcodeScannerDeviceInfo(
									deviceName,
									GetBarcodeScannerDeviceType(rid.dwType),
									rid.hDevice,
									friendlyName);
 
								this.devices.Add(rid.hDevice, info);
							}
						}
 
						Marshal.FreeHGlobal(data);
					}
				}
			}
		}
 
		Marshal.FreeHGlobal(rawInputDeviceList);
	}
}

This mess of interop code reads our configuration file for a list of hardware IDs to look for. For example, one of the strings I’m looking for in the device name is HID#Vid_05e0&Pid_028a, which I got from Device Manager, and this is enough to identify our Hand Held scan gun. Then we call GetRawInputDeviceList() (twice, once to get the size of the buffer that we need to allocate, and once again to actually fill that buffer once we’ve allocated it) to get a list of the devices that we’re actually listening to. For each device, a call to GetRawInputDeviceInfo() gets us the device name, which is this really horrific looking string, but my hardware ID is in there somewhere. So if I have a match, I grab some information about the device, wrap it up in a simple BarcodeScannerDeviceInfo object, and add the device handle and this struct to a dictionary called devices. Our ProcessRawInputMessage() function can now look at this dictionary to determine which WM_INPUT messages to listen to and which ones to ignore.

Now, you may be thinking, why do all that to get the device handle? Why not just store that in your config file? Well, the problem with USB devices is that device handle changes every time you unplug and replug the barcode scan gun. The hardware ID is the only reliable identifier. I don’t understand what most of the huge device name string actually contains, but at this point, I’m not sure if I really care to become a USB expert–right now, things Simply Work, and I’m inclined to leave them that way.

Putting it all together

With all this in place, I can run the app, scan the UPC symbol of the ridiculously American-sized Diet Mountain Dew sitting on my desk, and squeal for joy as (A) the textbox does NOT show any characters, (B) the status bar listens to the BarcodeScanned event and displays the UPC data, and (C) I type on the keyboard and data is correctly passed through to the textbox and ignored by the BarcodeScannerListener. If I scan the barcode in another application, my application still sees it, but the application still gets the text passed through–that’s not a big deal for me. (The reason for this is our call to PeekMessage() only zaps messages for my current application thread, not for the entire OS.)

I’ll clean up the code and see if I can post something up soon. And if you know of a way where this could have been done in three lines, by all means, let me know.

At the very least, it’s nice to scan away on those huge inventory shipments and not worry about some stupid textbox having focus.

Update: I’ve put together a bare-bones version of the code. Sample Application Code

Update 5/15/2010: One Year Later

So it’s been about 14 months since I put the above code into production in my application. Thanks to some of the helpful comments passers-by have left, and thanks to some rare crash reports that I was able to debug, I’ve made some changes to the code that I use in production and would like to make them known, to save you some headaches.

PeekMessage() was a bad idea

The first is that I removed the call to PeekMessage() and instead used an IMessageFilter implementation. The reason that PeekMessage() was problematic is that it removed all of the WM_KEYDOWN messages in the window’s message queue.

Under normal conditions, this worked just fine, since there was normally only one WM_INPUT (the one being processed) and one WM_KEYDOWN (the one that corresponds to the WM_INPUT message) in the queue. But if the system was “busy” (say, for example, you start up FedEx Ship Manager in the background and scan something), then some messages–particularly, the SHIFT key, as noted in the comments below–would be “missed.” That’s because the tax on OS resources would slow down the rate at which the application could process messages while the barcode scanner continues to dish out messages at a constant rate.

So what would happen is that, say, three WM_KEYDOWN messages would appear on the queue: let’s use “b”, SHIFT, and “c” as an example set. By the time the application got around to processing the first WM_INPUT (the one for the “b” keystroke), it would call PeekMessage() and inadvertently zap all three WM_KEYDOWN messages.

And normally even that wouldn’t be a big deal, because we’re looking at WM_INPUT messages, after all, not WM_KEYDOWN messages, to determine what to put in our StringBuilder. But note that we call GetKeyboardState() to determine the state of the keyboard and pass that byte array into the ToUnicode() call to get the actual character back. It turns out that GetKeyboardState() works by examining the WM_KEYDOWN messages that are sitting in the message queue–and guess what, we zapped the SHIFT keypress right out of the queue by calling PeekMessage(). As a result, we’ll fill our StringBuilder with “bc” instead of “bC”, and we might be left scratching our heads as to why the SHIFT, CTRL, or ALT keys aren’t always “making it through.”

So my workaround is simple:

    /// <summary>
    /// Filters WM_KEYDOWN messages.
    /// </summary>
    public class BarcodeScannerKeyDownMessageFilter : IMessageFilter
    {
        /// <summary>
        /// Gets or sets a value indicating whether or not the next keydown message
        /// should be filtered.
        /// </summary>
        public bool FilterNext
        {
            get;
            set;
        }
 
        /// <summary>
        /// Filters out a message before it is dispatched.
        /// </summary>
        /// <param name="m">The message to be dispatched. You cannot modify
        /// this message.</param>
        /// <returns>
        /// true to filter the message and stop it from being dispatched; false
        /// to allow the message to continue to the next filter or control.
        /// </returns>
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        public bool PreFilterMessage(ref Message m)
        {
            bool filter = false;
 
            if (this.FilterNext && m.Msg == NativeMethods.WM_KEYDOWN)
            {
                filter = true;
                this.FilterNext = false;
            }
 
            return filter;
        }
    }

If the FilterNext property is set, then we filter out the next WM_KEYDOWN that we see and then set the property back to false. Otherwise, we let the message through.

The BarcodeScannerListener now instances the BarcodeScannerKeyDownMessageFilter (what a mouthful) in its constructor and registers it with the Windows Forms-provided Application class:

            this.filter = new BarcodeScannerKeyDownMessageFilter();
            Application.AddMessageFilter(this.filter);

And the WndProc message sets the FilterNext property of the filter as it sees appropriate:

        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case NativeMethods.WM_INPUT:
                    if (this.ProcessRawInputMessage(m.LParam))
                    {
                        this.filter.FilterNext = true;
                    }
 
                    break;
            }
 
            base.WndProc(ref m);
        }

This has proven to be much more reliable in practice than the old PeekMessage() method. I haven’t had problems with “missed keystrokes” since.

GetKeyboardState() only works as expected when you have focus

By design, Windows keyboard messages go straight to the window that has focus. When I originally wrote the article, I had the HookRawInput() method of the BarcodeScannerListener specify RIDEV_INPUTSINK. This allowed me to receive WM_INPUT messages even when my application did not have focus.

Well, that meant I still received the WM_INPUT messages, but I didn’t receive the WM_KEYDOWN messages. And normally I wouldn’t care about that, but that also means that I wasn’t receiving WM_KEYDOWN messages for things like SHIFT, CTRL and ALT. The net result? Barcodes would be scanned with intended case if the application had focus, but they would be scanned as all lowercase if the application did not have focus. That’s because GetKeyboardState() assesses the keyboard state by looking at the WM_KEY* messages in the message queue, as I mentioned above in the discussion about the problems with the PeekMessage() call.

I didn’t come up with a workaround for this other than to say that I now only support scans when the application has focus. As a result, I used 0 instead of specifying any flags in HookRawInput().

(Thanks to commenter Matt for pointing this out.)

A rare memory corruption bug caused great hair loss

If there’s one thing that I hate, it’s bugs that I fix but never truly understood the original cause. And on this one I’m not alone.

Every once in 10,000 or so scans, the application would crash with a memory corruption error in msvcr80.dll, and it always occurred when marshalling data into the RAWINPUT structure in the ProcessRawInputMessage() function of the BarcodeScannerListener:

raw = (NativeMethods.RAWINPUT)Marshal.PtrToStructure(buffer, typeof(NativeMethods.RAWINPUT));

(DrkNess, one of the commenters on this article, also encountered the error.)

Now, as we know with access violations, the memory corruption could have occurred at some other point in the program: this is just an unmanaged call that gets called exceptionally frequently and so is likely to stumble across the corrupted memory boundary.

I read all up on pinning memory with the garbage collector (shouldn’t have applied), double- and triple-checked the P/Invoke declarations (they seemed to be correct), and even found several references to a similar problem in the article on CodeProject that became an inspiration for this one, but damned if I couldn’t find a reason for the error. Obviously I had screwed something up in a subtle way that is fantastically hard to reproduce.

So I ripped out all of the P/Invoke declarations and created a C++/CLI assembly that contained one class, the help-me-I’m-running-out-of-names-for-these-things BarcodeScannerListenerInteropHelper. Its sole purpose is to be used by the BarcodeScannerListener and bridge the gap between the managed world and the unmanaged Windows platform calls without the need for P/Invoke. With P/Invoke out of the way, so too has the memory corruption bug disappeared. A nice side effect is that the BarcodeScannerListener class has become much smaller and easier to understand.

For the really hardcore, write an upper device driver filter

Throughout this process I also learned that one sure-fire way to implement all of this barcode scanning functionality is to write an upper device driver filter. But I still stick to my method because (a) it works well enough, (b) doesn’t require special configuration on the client machine, and (c) is a hell of a lot easier than mucking with drivers.

Such a filter would enable us to receive input regardless of which application had focus, and it would enable us to control which applications received the input, but it does look like it would be a lot of work.

New Conclusions and Delusions

The code has held up pretty well, and I’m glad that I finally found the time to write a post-mortem analysis of the issues that I’ve encountered.

Here’s an updated version of the sample application that incorporates the changes that I’ve made. Good luck!