Как различать несколько устройств ввода в C#

У меня есть сканер штрих-кода (который действует как клавиатура) и, конечно же, клавиатура тоже подключена к компьютеру. Программное обеспечение принимает ввод как со сканера, так и с клавиатуры. Мне нужно принять только ввод сканера. Код написан на C#. Есть ли способ «отключить» ввод с клавиатуры и принимать ввод только со сканера?

Примечание. Клавиатура является частью портативного компьютера ... поэтому ее нельзя отключить. Кроме того, я попытался поместить следующий код, защищенный переопределением Boolean ProcessDialogKey (System.Windows.Forms.Keys keyData) {return true; } Но тогда, наряду с игнорированием нажатий клавиш с клавиатуры, также игнорируется ввод сканера штрих-кода.

Я не могу заставить сканер отправлять сигнальные символы, поскольку сканер используется другими приложениями, и добавление потока сигнальных символов будет означать изменение другого кода.

Кроме того, я не могу использовать метод синхронизации для определения того, поступил ли ввод со сканера штрих-кода (если это набор символов, за которыми следует пауза), поскольку отсканированные штрих-коды потенциально могут быть односимвольными штрих-кодами.

Да, я читаю данные из потока.

Я пытаюсь следовать статье: Отличие сканеров штрих-кода от клавиатуры в WinForms. Однако у меня есть следующие вопросы:

  1. Я получаю сообщение об ошибке NativeMethods недоступен из-за уровня защиты. Кажется, мне нужно импортировать dll; это верно? Если да, то как мне это сделать?
  2. Какое определение защищенного переопределения void WndProc (ref Message m) я должен использовать, в статье есть две реализации?
  3. Я получаю сообщение об ошибке, связанной с [SecurityPermission (SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] ошибкой CS0246: не удалось найти тип или имя пространства имен «SecurityPermission» (вам не хватает директивы using или ссылки на сборку?). Как мне исправить эту ошибку?
  4. Также есть ошибка в строке, содержащей: if ((из hardwareId в hardwareIds, где deviceName.Contains (hardwareId) select hardwareId) .Count ()> 0) Ожидается ошибка CS1026:).
  5. Следует ли мне поместить весь код статьи в один файл .cs под названием BarcodeScannerListener.cs?

Дополнительные вопросы об исходном коде решения C#, опубликованные Николасом Пясецки на http://nicholas.piasecki.name/blog/2009/02/distinguishing-barcode-scanners-from-the-keyboard-in-winforms/ :

  1. Мне не удалось открыть решение в VS 2005, поэтому я загрузил Visual C# 2008 Express Edition, и код запустился. Однако после подключения сканера штрих-кода и сканирования штрих-кода программа не распознала сканирование. Я поставил точку останова в методе OnBarcodeScanned, но он так и не сработал. Я изменил App.config на идентификатор своего сканера штрих-кода, полученный с помощью диспетчера устройств. Кажется, есть 2 deviceNames с HID # Vid_0536 & Pid_01c1 (которые получаются из диспетчера устройств при подключении сканера). Я не знаю, вызывает ли это сканирование. При итерации по имени устройства вот список устройств, которые я нашел (с помощью отладчика):

"\ ?? \ HID # Vid_0536 & Pid_01c1 & MI_01 # 9 & 25ca5370 & 0 & 0000 # {4d1e55b2-f16f-11cf-88cb-001111000030}"

"\ ?? \ HID # Vid_0536 & Pid_01c1 & MI_00 # 9 & 38e10b9 & 0 & 0000 # {884b96c3-56ef-11d1-bc8c-00a0c91405dd}"

"\ ?? \ HID # Vid_413c & Pid_2101 & MI_00 # 8 & 1966e83d & 0 & 0000 # {884b96c3-56ef-11d1-bc8c-00a0c91405dd}"

"\ ?? \ HID # Vid_413c & Pid_3012 # 7 & 960fae0 & 0 & 0000 # {378de44c-56ef-11d1-bc8c-00a0c91405dd}"
"\ ?? \ Root # RDP_KBD # 0000 # {884b96c3-56ef-11d1-bc8c" \ ACPI # PNP0303 # 4 & 2f94427b & 0 # {884b96c3-56ef-11d1-bc8c-00a0c91405dd} "" \ ?? \ Root # RDP_MOU # 0000 # {378de44c-56ef-11d1-bc8c-00a0c9140 "PNF13140" PNF135dd \ ACF0C9140 "? # 4 & 2f94427b & 0 # {378de44c-56ef-11d1-bc8c-00a0c91405dd} "

Итак, есть 2 записи для HID # Vid_0536 & Pid_01c1; может это быть причиной того, что сканирование не работает?

Хорошо, похоже, мне пришлось найти способ не зависеть от символа ASCII 0x04, отправляемого сканером ... поскольку мой сканер не отправляет этот символ. После этого запускается событие сканирования штрих-кода и отображается всплывающее окно со штрих-кодом. Так что спасибо Николасу за вашу помощь.

Ответов (7)

Решение

Вы можете использовать API сырого ввода, чтобы различать клавиатуру и сканер, как я недавно. Неважно, сколько клавиатур или подобных им устройств вы подключили; вы увидите WM_INPUT перед тем, как нажатие клавиши будет сопоставлено с виртуальным ключом, не зависящим от устройства, который вы обычно видите в KeyDown событии.

Гораздо проще сделать то, что рекомендуют другие, и настроить сканер на отправку контрольных символов до и после штрих-кода. (Обычно это делается путем сканирования специальных штрих-кодов в конце руководства пользователя сканера.) Затем KeyPreview событие вашей основной формы может наблюдать за этим концом поворота и проглатывать ключевые события для любого дочернего элемента управления, если он находится в середине считываемого штрих-кода. Или, если вы хотите быть более интересным, вы можете использовать низкоуровневую клавиатуру, SetWindowsHookEx() чтобы наблюдать за этими стражами и проглатывать их там (преимущество в том, что вы все равно можете получить событие, даже если ваше приложение не было в фокусе).

Среди прочего, я не мог изменить контрольные значения на наших сканерах штрих-кода, поэтому мне пришлось пойти по сложному пути. Было определенно больно. Будьте проще, если можете!

-

Ваше обновление, семь лет спустя: если в вашем сценарии использования считывается со сканера штрих-кода USB, в Windows 10 есть приятный и удобный API для этой встроенной функции Windows.Devices.PointOfService.BarcodeScanner . Это UWP / WinRT API, но вы также можете использовать его из обычного настольного приложения; вот что я делаю сейчас. Вот пример кода прямо из моего приложения, чтобы дать вам суть:

{
    using System;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Windows;
    using Windows.Devices.Enumeration;
    using Windows.Devices.PointOfService;
    using Windows.Storage.Streams;
    using PosBarcodeScanner = Windows.Devices.PointOfService.BarcodeScanner;

    public class BarcodeScanner : IBarcodeScanner, IDisposable
    {
        private ClaimedBarcodeScanner scanner;

        public event EventHandler<BarcodeScannedEventArgs> BarcodeScanned;

        ~BarcodeScanner()
        {
            this.Dispose(false);
        }

        public bool Exists
        {
            get
            {
                return this.scanner != null;
            }
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        public async Task StartAsync()
        {
            if (this.scanner == null)
            {
                var collection = await DeviceInformation.FindAllAsync(PosBarcodeScanner.GetDeviceSelector());
                if (collection != null && collection.Count > 0)
                {
                    var identity = collection.First().Id;
                    var device = await PosBarcodeScanner.FromIdAsync(identity);
                    if (device != null)
                    {
                        this.scanner = await device.ClaimScannerAsync();
                        if (this.scanner != null)
                        {
                            this.scanner.IsDecodeDataEnabled = true;
                            this.scanner.ReleaseDeviceRequested += WhenScannerReleaseDeviceRequested;
                            this.scanner.DataReceived += WhenScannerDataReceived;

                            await this.scanner.EnableAsync();
                        }
                    }
                }
            }
        }

        private void WhenScannerDataReceived(object sender, BarcodeScannerDataReceivedEventArgs args)
        {
            var data = args.Report.ScanDataLabel;

            using (var reader = DataReader.FromBuffer(data))
            {
                var text = reader.ReadString(data.Length);
                var bsea = new BarcodeScannedEventArgs(text);
                this.BarcodeScanned?.Invoke(this, bsea);
            }
        }

        private void WhenScannerReleaseDeviceRequested(object sender, ClaimedBarcodeScanner args)
        {
            args.RetainDevice();
        }

        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.scanner = null;
            }
        }
    }
}

Конечно, вам понадобится сканер штрих-кода, который поддерживает USB HID POS, а не просто клавиатурный клин. Если ваш сканер представляет собой просто клавиатуру, я рекомендую купить что-нибудь вроде бывшего в употреблении Honeywell 4600G на eBay примерно за 25 долларов. Поверьте, ваше здравомыслие того стоит.

посмотрите на это: http://nate.dynalias.net/dev/keyboardredirector.rails ( БОЛЬШЕ НЕТ ДОСТУПНЫХ ) отлично работает!

Укажите клавиатуру и клавиши, которые вы хотите заблокировать, и это работает как шарм!

Также взгляните на это: http://www.oblita.com/interception.html Вы можете создать для него оболочку C# - она ​​также работает как шарм ..

В аналогичной ситуации я различал сканирование и ввод текста пользователем, глядя на скорость ввода.

Много символов очень близко друг к другу, тогда пауза - это сканирование. Все остальное вводится с клавиатуры.

Я точно не знаю ваших требований, так что, возможно, вам это не подойдет, но это лучшее, что у меня есть :)

Это зависит от того, как вы взаимодействуете с устройством. В любом случае это не будет решение C#, это будет какая-то другая библиотека. Вы читаете данные из потока? Если вы просто нажимаете клавиши, возможно, вы ничего не сможете с этим поделать.

Я думаю, вы сможете различать несколько клавиатур через DirectX API или, если это не сработает, через API сырого ввода.

Я знаю, что это старая ветка, я нашел ее, выполнив сканирование штрих-кода в WIN10. Всего несколько заметок на случай, если это кому-то понадобится.

Эти сканеры от Honeywell имеют несколько интерфейсов USB. Один - клавиатура + Hid Point of sales (составное устройство). Также есть CDC-ACM (эмуляция ComPort) и Hid Point of sales (отдельно) + еще.

По умолчанию сканеры показывают серийный номер, поэтому хост может различать многие устройства (у меня однажды было подключено +20). Но есть команда для отключения серийного номера!

В этом отношении более новые модели ведут себя так же. Если вы хотите увидеть это вживую, попробуйте мою терминальную программу yat3 (бесплатно на моем сайте ). Он может открывать все упомянутые выше интерфейсы и адаптирован для таких устройств.

Слово для использования интерфейсов клавиатуры:
используйте их только в крайнем случае. Они медлительны, менее надежны, когда дело касается экзотических персонажей. Единственное хорошее применение - это если вы хотите вводить данные в существующие приложения. Если вы все равно кодируете, то читать из ComPort / HidPos-Device проще.

Я успешно выполнил то, что вы, ребята, здесь ищете. У меня есть приложение, которое получает все символьные данные штрих-кода от сканера штрих-кода Honeywell / Metrologic. Никакое другое приложение в системе не получает данные от сканера, и клавиатура продолжает нормально работать.

В моем приложении используется комбинация необработанного ввода и ужасной низкоуровневой системы подключения клавиатуры. Вопреки тому, что здесь написано, я обнаружил, что сообщение wm_input получено до вызова функции перехвата клавиатуры. Мой код для обработки сообщения wm_input в основном устанавливает логическую переменную, чтобы указать, был ли получен символ от сканера. Функция перехвата клавиатуры, вызываемая сразу после обработки wm_input, поглощает данные псевдоклавиатуры сканера, предотвращая получение данных другими приложениями.

Функция перехвата клавиатуры должна быть помещена в dll, поскольку вы хотите перехватывать все сообщения системной клавиатуры. Кроме того, файл с отображением памяти должен использоваться для кода обработки wm_input для связи с dll.