Именно на этот вопрос я не нашел готового ответа. Считаю, что в десктопном режиме со сканером штрих-кодов (считывателем магнитных карт), необходимо работать исключительно напрямую. Windows Raw Input позволяет легко отследить нажатие клавиш, включая идентификацию с какой именно клавиатуры было нажатие, но не может “проглотить” ввод с определенной клавиатуры. Это можно сделать при помощи SetWindowsHookEx, но в нем нельзя определить с какой клавиатуры пришел символ.
У Атол решение на базе анализа всего трафика нажатий клавиш. Если “драйвер” увидел стартовый перфикс (F7 например), то он начинает все съедать до окончания штрих-кода по суффиксу или таймауту ввода. На мой взгляд это костыль.
Единственное вменяемое решение использование драйвера уровня ядра для прямого доступа к клавиатуре. Именно HID клавиатуре.
Для решения этой проблемы использован libusb – кроссплатформенная библиотека для работы с HID устройствами USB. Сценарий прост:
- Создаем пакет драйвера для HID устройства, работающего в режиме клавиатуры (inf-wizard.exe);
- Устанавливаем полученный драйвер для HID устройства и драйвер libusb;
- Начинаем прямую работу с HID устройством (например, используя LibUsbDotNet как я);
Придется освоить работу с USB и протокол работы клавиатуры, включая преобразование кодов клавиш в символы. Бонусом – перехват подключения/отключения любого USB оборудования, его быстрая готовность после подключения, кроссплатформенное решение и возможность использовать более дешевое оборудование (китайские сканеры от 15USD).
Теперь практика.
- Подключаемся к устройству и иницируем поток считывания:
1 2 3 4 5 6 7 8 9 10 11 12 |
Dim c_usbdev As UsbDevice = <Найденное/подключенное устройство> c_usbdevwhole = TryCast(c_usbdev, IUsbDevice) If Not c_usbdevwhole.SetConfiguration(1) Then Throw New Exception("can't set configuration") End If If Not c_usbdevwhole.ClaimInterface(0) Then Throw New Exception("can't set interface") End If Dim th As New Threading.Thread(AddressOf DataLoop) With {.IsBackground = True, .Name = "HID Data loop: " & dec.Info.SerialString} th.Start() |
2. Поток считывания данных будет выглядеть так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
Private Sub DataLoop() Dim reader As UsbEndpointReader = c_usbdev.OpenEndpointReader(&H81) Dim readBuffer(7) As Byte Dim ec As ErrorCode = ErrorCode.None Dim buffer As String = "" While True Dim bytesRead As Integer ec = reader.Read(readBuffer, 2000, bytesRead) If ec = ErrorCode.Win32Error Or c_stop Then Exit While End If If bytesRead > 0 Then If readBuffer(2) > 0 Then Dim c As String = GetKeyData(readBuffer) If c = vbCr Then OnDataReceived(Me, buffer) buffer = Nothing Else buffer &= c End If End If End If End While End Sub |
При получении символа возврата каретки (Enter) буфер передается в события OnDataReceived(data) до этого символы копятся в буфере нажатых клавиш (buffer).
3. Пример функции распознавания клавиш (делал под свои нужды, так что не полное):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Private Function GetKeyData(buff() As Byte) As String Dim res As String = "" Dim code As Byte = buff(2) If code >= &H04 And code <= &H1D Then res = Chr(97 + code - &H04) ElseIf code = &H27 Then res = "0" ElseIf code >= &H1E And code <= &H27 Then res = Chr(49 + code - &H1E) ElseIf code = &H28 Then res = vbCr ElseIf code = &H2D Then res = "-" Else res = "(" & Hex(code).PadLeft(2, "0") & ")" End If Return res.ToUpper End Function |
Вполне достойная замена эмуляции COM порта. Все работает безупречно.