RiaanDP
RiaanDP

Reputation: 1212

Blocking input from keyboard HID

I am in the process of integrating support for USB barcode scanners into a WPF application, but I have hit a snag.

Currently I am successfully identifying and capturing input with the help of this article.

The issue that I am facing is that although I am successfully identifying and routing input from the scanner device, I am unable to block the input events for further processing (e.g. if a textbox control on my application has input focus, the input from the scanner will be written to it).

I have tried two methods of capturing input:

var wndSourceHandle = HwndSource.FromHwnd(new WindowInteropHelper(
                                          _wndEventSource = value).Handle);
wndSourceHandle.AddHook(this.WndProc);

WndProc definition:

private IntPtr WndProc(IntPtr hwnd, int iMessage, IntPtr wParam, IntPtr lParam, ref  bool bisHandled)
    {
        if (iMessage == Win32.WM_INPUT)
        {
            var result = ProcessRawInput(lParam);
            bisHandled = result != null && result.Value;
            return IntPtr.Zero;
        }

        return IntPtr.Zero;
    }

As well as:

ComponentDispatcher.ThreadFilterMessage += (ref MSG msg, ref bool handled) =>
                {
                    if (msg.message == Win32.WM_INPUT)
                    {
                        var result = ProcessRawInput(msg.lParam);
                        handled = result != null && result.Value;
                        return;
                    }

                    handled = false;
                };

The ProcessRawInput method returns true if the source of the input is the barcode scanner, false otherwise.

Upvotes: 2

Views: 4047

Answers (2)

RiaanDP
RiaanDP

Reputation: 1212

After a bit more research I found a solution applicable to WinForms here. I was able to modify it for WPF as follows:

ComponentDispatcher.ThreadFilterMessage += (ref MSG msg, ref bool handled) =>
                {
                    if (msg.message == Win32.WM_INPUT)
                    {
                        var result = ProcessRawInput(msg.lParam);
                        this.m_bIgnoreNextKeyDownMessage = result != null && result.Value;
                        return;
                    }
                    if (msg.message == Win32.WM_KEYDOWN && this.m_bIgnoreNextKeyDownMessage)
                    {
                        handled = true;
                    }

                    this.m_bIgnoreNextKeyDownMessage = false;
                };

This solution basically marks the first WM_KEYDOWN message after a barcode WM_INPUT message as "handled". I am not sure if this is the only/best/safest solution, but it looks like it does the trick.

Update:

With the above solution I still found that every now and again one random character from the scanned barcode would slip through to a focussed textbox - I am not sure why this is the case - could be a timing issue with the keyboard events as they are passed through the message handler. Another solution for checking whether the WM_KEYDOWN message should be ignored:

if (msg.message == Win32.WM_KEYDOWN && !String.IsNullOrEmpty(this.m_strKeyInput))
{
    handled = true;
}

The buffer m_strKeyInput contains the current scanned barcode value - this buffer is empty when no barcode is available, built up one character at a time as the barcode scanner pushes down the barcode - and then emptied once a custom BarcodeScanned event is generated. The only drawback of this solution that I can think of is that all keyboards will stop functioning for the few milliseconds the barcode is being pushed from the scanner - which is acceptable for my scenario.

Upvotes: 3

A.R.
A.R.

Reputation: 15685

It sounds like since you are routing the input from the scanner (treating it as a keyboard) and into a textbox you could simply use one of the Preview* events on that textbox to perform your additional processing. For example you could override PreviewTextInput

private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
  // MyBarcodeScanner would be a reference to the IInput device that represents the scanner:
  if (e.Device == MyBarcodeScanner)
  {
    // Process the text, rejecting it in this case.
    // Marking the event as handled will prevent the 'TextChanged' event 
    // from firing, so the characters will not appear in the text box.
    e.Handled = true;
  }
  else
  {
    // This is some other keyboard, by not handling the event,
    // the users will be allowed to enter text normally.
  }
}

You may have to do a little trial and error to figure out how to identify the barcode scanner that is identified by 'e.Device' but that should be relatively easy. Sorry I can't be more complete, but I don't have a barcode scanner of my own to experiment with.

Upvotes: 0

Related Questions