Handling USB Barcode Scanner acting as a keyboard,

2019-09-06 06:20发布

问题:

In a winforms app, I need to read input from a standard USB barcode scanner, one which makes iteself appears as a USB keyboard to windows. This has to work without giving focus to a particular control (ie, I can't say "click on this textbox then scan the barcode"). The scanner is configured to output a header and trailer to every codes it scans.

I'd rather don't go the "raw" way, ie, hooking directly into the USB input or Windows events (WM_INPUT and such).

I can of course trap the keystrokes in ProcessCmdKey, but then I don't seem to be able to properly identify the keys for the header/trailer (^~{ and }~^ respectively).

Any idea how this can be done properly in managed code?

回答1:

This works, but it's kinda ugly:

    [DllImportAttribute("User32.dll")]
    public static extern int ToAscii(int uVirtKey, int uScanCode, byte[] lpbKeyState, byte[] lpChar, int uFlags);

    [DllImportAttribute("User32.dll")]
    public static extern int GetKeyboardState(byte[] pbKeyState);

    public static char GetAsciiCharacter(int uVirtKey, int uScanCode)
    {
        byte[] lpKeyState = new byte[256];
        GetKeyboardState(lpKeyState);
        byte[] lpChar = new byte[2];
        if (ToAscii(uVirtKey, uScanCode, lpKeyState, lpChar, 0) == 1)
            return (char)lpChar[0];
        else
            return new char();
    }

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
        if(keyData == Keys.ShiftKey || keyData == Keys.Shift)
            return base.ProcessCmdKey(ref msg, keyData);

        char keyChar = GetAsciiCharacter((int) (keyData & Keys.KeyCode), (((int) msg.LParam) & 0x1000000));

        if(keyChar == '\0')
            return base.ProcessCmdKey(ref msg, keyData);

        _currentSequence.Add(keyChar);

        if (_currentSequence.ToString() == "^~{")
        {
            _handlingInputFromScanner = true;
            _scannerBuffer.Clear();
            return true;
        }

        if (_currentSequence.ToString() == "}~^")
        {
            _handlingInputFromScanner = false;
            OnScannerRead.Invoke(this, new ScannerReadEventArgs { ScannerData = _scannerBuffer.ToString() });
            _scannerBuffer.Clear();
            return true;
        }

        if (keyChar == '}' || keyChar == '{' || keyChar == '~' || keyChar == '^')
        {
            return true;
        }

        if (_handlingInputFromScanner)
        {
            _scannerBuffer.Append(keyChar);
            return true;
        }

        return base.ProcessCmdKey(ref msg, keyData);
    }


回答2:

ProcessCmdKey is the right place to do it.



回答3:

maybe a textbox hidden by a panel, that gets focus when the barcode is to be scanned, and then use the keydown event, that should get you the raw ascii values of the characters sent by the scanner