简体   繁体   English

C#中如何区分多个输入设备

[英]How to distinguish between multiple input devices in C#

I have a barcode scanner (which acts like a keyboard) and of course I have a keyboard too hooked up to a computer.我有一个条形码扫描仪(它就像一个键盘),当然我的键盘也连接到了电脑上。 The software is accepting input from both the scanner and the keyboard.该软件正在接受来自扫描仪和键盘的输入。 I need to accept only the scanner's input.我只需要接受扫描仪的输入。 The code is written in C#.代码写在 C# 中。 Is there a way to "disable" input from the keyboard and only accept input from the scanner?有没有办法“禁用”键盘输入并且只接受来自扫描仪的输入?

Note: Keyboard is part of a laptop...so it cannot be unplugged.注意:键盘是笔记本电脑的一部分……因此无法拔下。 Also, I tried putting the following code protected override Boolean ProcessDialogKey(System.Windows.Forms.Keys keyData) { return true;另外,我尝试将以下代码保护覆盖 Boolean ProcessDialogKey(System.Windows.Forms.Keys keyData) { return true; } But then along with ignoring the keystrokes from the keyboard, the barcode scanner input is also ignored.但是随着忽略键盘的击键,条形码扫描仪的输入也被忽略了。

I cannot have the scanner send sentinal characters as, the scanner is being used by other applications and adding a sentinal character stream would mean modifying other code.我不能让扫描仪发送标记字符,因为扫描仪正被其他应用程序使用,添加标记字符 stream 意味着修改其他代码。

Also, I cannot use the timing method of determining if the input came from a barcode scanner (if its a bunch of characters followed by a pause) since the barcodes scanned could potentially be single character barcodes.此外,我不能使用计时方法来确定输入是否来自条形码扫描仪(如果它是一堆字符,然后是暂停),因为扫描的条形码可能是单字符条形码。

Yes, I am reading data from a stream.是的,我正在从 stream 读取数据。

I am trying to follow along with the article: Distinguishing Barcode Scanners from the Keyboard in WinForms.我正在尝试阅读文章:在 WinForms 中将条码扫描仪与键盘区分开来。 However I have the following questions:但是我有以下问题:

  1. I get an error NativeMethods is inaccessible due to its protection level.我收到一个错误 NativeMethods 由于其保护级别而无法访问。 It seems as though I need to import a dll;好像我需要导入一个 dll; is this correct?这个对吗? If so, how do I do it?如果是这样,我该怎么做?
  2. Which protected override void WndProc(ref Message m) definition should I use, there are two implementations in the article?我应该使用哪个受保护的覆盖 void WndProc(ref Message m) 定义,文章中有两种实现?
  3. Am getting an error related to [SecurityPermission( SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] error CS0246: The type or namespace name 'SecurityPermission' could not be found (are you missing a using directive or an assembly reference?).收到与 [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] 相关的错误错误 CS0246:找不到类型或命名空间名称“SecurityPermission”(您是否缺少 using 指令或程序集引用?)。 How do I resolve this error?如何解决此错误?
  4. There is also an error on the line containing: if ((from hardwareId in hardwareIds where deviceName.Contains(hardwareId) select hardwareId).Count() > 0) Error is error CS1026: ) expected.包含以下内容的行上还有一个错误:if ((from hardwareId in hardwareIds where deviceName.Contains(hardwareId) select hardwareId).Count() > 0) Error is error CS1026: ) 预期。
  5. Should I be placing all the code in the article in one.cs file called BarcodeScannerListener.cs?我应该将文章中的所有代码放在名为 BarcodeScannerListener.cs 的 one.cs 文件中吗?

Followup questions about C# solution source code posted by Nicholas Piasecki on http://nicholas.piasecki.name/blog/2009/02/distinguishing-barcode-scanners-from-the-keyboard-in-winforms/ : Nicholas Piasecki 在http://nicholas.piasecki.name/blog/2009/02/distinguishing-barcode-scanners-from-the-keyboard-in-winforms/上发布的关于 C# 解决方案源代码的后续问题:

  1. I was not able to open the solution in VS 2005, so I downloaded Visual C# 2008 Express Edition, and the code ran.我在VS 2005中无法打开解决方案,所以我下载了Visual C# 2008 Express Edition,代码运行了。 However, after hooking up my barcode scanner and scanning a barcode, the program did not recognize the scan.但是,在连接我的条形码扫描仪并扫描条形码后,程序无法识别扫描。 I put a break point in OnBarcodeScanned method but it never got hit.我在 OnBarcodeScanned 方法中设置了一个断点,但它从未被击中。 I did change the App.config with the id of my Barcode scanner obtained using Device Manager.我确实使用设备管理器获得的条形码扫描仪的 ID 更改了 App.config。 There seems to be 2 deviceNames with HID#Vid_0536&Pid_01c1 (which is obtained from Device Manager when the scanner is hooked up).似乎有 2 个带有 HID#Vid_0536&Pid_01c1 的设备名称(连接扫描仪时从设备管理器中获得)。 I don't know if this is causing the scanning not to work.我不知道这是否导致扫描不起作用。 When iterating over the deviceNames, here is the list of devices I found (using the debugger):遍历 deviceNames 时,这里是我找到的设备列表(使用调试器):

"\??\HID#Vid_0536&Pid_01c1&MI_01#9&25ca5370&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}" “\??\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_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_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}" “\??\HID#Vid_413c&Pid_3012#7&960fae0&0&0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd}”
"\??\Root#RDP_KBD#0000#{884b96c3-56ef-11d1-bc8c-00a0c91405dd}" "\??\ACPI#PNP0303#4&2f94427b&0#{884b96c3-56ef-11d1-bc8c-00a0c91405dd}" "\??\Root#RDP_MOU#0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd}" "\??\ACPI#PNP0F13#4&2f94427b&0#{378de44c-56ef-11d1-bc8c-00a0c91405dd}" "\??\Root#RDP_KBD#0000#{884b96c3-56ef-11d1-bc8c-00a0c91405dd}" "\??\ACPI#PNP0303#4&2f94427b&0#{884b96c3-56ef-11d1-bc8c-00a0c91405dd}" "\?? \Root#RDP_MOU#0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd}" "\??\ACPI#PNP0F13#4&2f94427b&0#{378de44c-56ef-11d1-bc8c-00a0c91405dd}"

So there are 2 entries for HID#Vid_0536&Pid_01c1;所以 HID#Vid_0536&Pid_01c1 有 2 个条目; could that be causing the scanning not to work?这会导致扫描不起作用吗?

OK so it seems that I had to figure out a way to not depend on the ASCII 0x04 character being sent by the scanner...since my scanner does not send that character.好的,看来我必须想办法不依赖扫描仪发送的 ASCII 0x04 字符……因为我的扫描仪不发送该字符。 After that, the barcode scanned event is fired and the popup with the barcode is shown.之后,触发条形码扫描事件并显示带有条形码的弹出窗口。 So thanks Nicholas for your help.所以感谢尼古拉斯的帮助。

You could use the Raw Input API to distinguish between the keyboard and the scanner like I did recently. 您可以使用原始输入 API 来区分键盘和扫描仪,就像我最近所做的那样。 It doesn't matter how many keyboard or keyboard-like devices you have hooked up;您连接了多少键盘或类似键盘的设备并不重要。 you will see a WM_INPUT before the keystroke is mapped to a device-independent virtual key that you typically see in a KeyDown event.在击键映射到通常在KeyDown事件中看到的与设备无关的虚拟键之前,您将看到WM_INPUT

Far easier is to do what others have recommended and configure the scanner to send sentinel characters before and after the barcode.更容易的是按照其他人的建议进行操作,并将扫描仪配置为在条形码之前和之后发送标记字符。 (You usually do this by scanning special barcodes in the back of the scanner's user manual.) Then, your main form's KeyPreview event can watch those roll end and swallow the key events for any child control if it's in the middle of a barcode read. (您通常通过扫描扫描仪用户手册背面的特殊条形码来做到这一点。)然后,如果它处于条形码读取的中间,您的主窗体的KeyPreview事件可以观察那些滚动结束并吞下任何子控件的键事件。 Or, if you wanted to be fancier, you could use a low-level keyboard hook with SetWindowsHookEx() to watch for those sentinels and swallow them there (advantage of this is you could still get the event even if your app didn't have focus).或者,如果您想变得更高级,可以使用带有SetWindowsHookEx()的低级键盘钩子来监视那些哨兵并将它们吞下(这样做的好处是即使您的应用程序没有,您仍然可以获得事件重点)。

I couldn't change the sentinel values on our barcode scanners among other things so I had to go the complicated route.我无法更改条码扫描仪上的标记值,所以我不得不 go 复杂的路线。 Was definitely painful.绝对是痛苦的。 Keep it simple if you can!如果可以的话,保持简单!

-- --

Your update, seven years later: If your use case is reading from a USB barcode scanner, Windows 10 has a nice, friendly API for this built-in in Windows.Devices.PointOfService.BarcodeScanner . Your update, seven years later: If your use case is reading from a USB barcode scanner, Windows 10 has a nice, friendly API for this built-in in Windows.Devices.PointOfService.BarcodeScanner . It's a UWP/WinRT API, but you can use it from a regular desktop app as well;它是 UWP/WinRT API,但您也可以从常规桌面应用程序中使用它; that's what I'm doing now.这就是我现在正在做的事情。 Here's some example code for it, straight from my app, to give you the gist:下面是一些示例代码,直接来自我的应用程序,为您提供要点:

{
    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;
            }
        }
    }
}

Granted, you'll need a barcode scanner that supports the USB HID POS and isn't just a keyboard wedge.当然,您需要一个支持 USB HID POS 的条形码扫描仪,而不仅仅是键盘楔。 If your scanner is just a keyboard wedge, I recommend picking up something like a used Honeywell 4600G off eBay for like $25.如果您的扫描仪只是一个键盘楔子,我建议您从 eBay 上以 25 美元的价格购买二手霍尼韦尔 4600G 之类的东西。 Trust me, your sanity will be worth it.相信我,你的理智是值得的。

What I did in a similar situation is distinguish between a scan and a user typing by looking at the speed of the input.我在类似情况下所做的是通过查看输入速度来区分扫描和用户打字。

Lots of characters very close together then a pause is a scan.许多字符非常靠近,然后暂停是扫描。 Anything else is keyboard input.其他任何东西都是键盘输入。

I don't know exactly your requirements, so maybe that won't do for you, but it's the best I've got:)我不确切知道您的要求,所以也许这对您不起作用,但这是我所拥有的最好的:)

look at this: http://nate.dynalias.net/dev/keyboardredirector.rails ( NOT AVAILABLE ANYMORE ) works great!看看这个: http://nate.dynalias.net/dev/keyboardredirector.rails 不再可用)效果很好!

Specify the keyboard and the keys you want to block, and it works like a charm!指定要阻止的键盘和键,它就像一个魅力!

Also take a look at this: http://www.oblita.com/interception.html You can create a C# wrapper for it - it also works like a charm..也看看这个: http://www.oblita.com/interception.html你可以为它创建一个 C# 包装器。

I know this is an old thread, found it by searching barcode scanning in WIN10.我知道这是一个旧线程,通过在WIN10中搜索条形码扫描找到它。 Just a few notes in case someone needs it.只是一些笔记,以防有人需要它。

These scanners from Honeywell have several USB interfaces.霍尼韦尔的这些扫描仪具有多个 USB 接口。 One is a keyboard + Hid Point of sales (composite device).一种是键盘+Hid Point of sale(复合设备)。 Also there are CDC-ACM (ComPort emulation) and Hid Point of sales (alone) + more.还有 CDC-ACM(ComPort 仿真)和 Hid 销售点(单独)+ 更多。

By default the scanners expose a serial number, so the host can distinguish between many devices (I had once +20 connected).默认情况下,扫描仪会公开一个序列号,因此主机可以区分许多设备(我曾经连接过 +20)。 There is a command to disable the serial number though!有一个命令可以禁用序列号!

The newer models behave the same in this regard.较新的模型在这方面表现相同。 If you want to see it live, try my terminal program yat3 (free on my site ).如果你想现场观看,试试我的终端程序 yat3(在我的网站上免费)。 It can open all the interfaces mentioned above and is tailored for such devices.它可以打开上面提到的所有接口,并且是为此类设备量身定制的。

A word to use keyboard interfaces:使用键盘界面的一句话:
Only use them as a last resort.仅将它们用作最后的手段。 They are slow, less reliable when it comes to exotic characters.当涉及到异国情调的角色时,它们很慢,不太可靠。 The only good use is if you want to enter data into existing applications.唯一的好用处是如果您想将数据输入到现有应用程序中。 If you code anyway, then reading from ComPort/HidPos-Device is easier.如果您仍然编码,那么从 ComPort/HidPos-Device 读取会更容易。

It depends on the way you are interacting with the device.这取决于您与设备交互的方式。 Anyway it wont be a C# solution, it will be some other library.无论如何,它不会是 C# 解决方案,它将是其他一些库。 Are you reading data from a stream?您是否正在从 stream 读取数据? If you are just taking keystrokes, there may be nothing you can do about it.如果您只是敲击键盘,您可能无能为力。

I think you might be able to distinguish multiple keyboards through DirectX API, or if that doesn't work, through raw input API.我认为您可以通过 DirectX API 来区分多个键盘,或者如果这不起作用,则可以通过原始输入 API 来区分。

I have successfully accomplished what you folks are looking for here.我已经成功地完成了你们在这里寻找的东西。 I have an application that receives all barcode character data from a Honeywell/Metrologic barcode scanner.我有一个从 Honeywell/Metrologic 条码扫描仪接收所有条码字符数据的应用程序。 No other application on the system receives the data from the scanner, and the keyboard continues to function normally.系统上没有其他应用程序接收到扫描仪的数据,键盘正常继续function。

My application uses a combination of raw input and the dreaded low-level keyboard hook system.我的应用程序结合使用原始输入和可怕的低级键盘挂钩系统。 Contrary to what is written here, I found that the wm_input message is received before the keyboard hook function is called.与这里写的相反,我发现在调用键盘钩子 function 之前收到了 wm_input 消息。 My code to process the wm_input message basically sets a boolean variable to specify whether or not the received character is from the scanner.我处理 wm_input 消息的代码基本上设置了一个 boolean 变量来指定接收到的字符是否来自扫描仪。 The keyboard hook function, called immediately after the wm_input is processed, swallows the scanner's pseudo-keyboard data, preventing the data from being received by other applications.在处理 wm_input 后立即调用的键盘钩子 function 会吞下扫描仪的伪键盘数据,从而防止数据被其他应用程序接收。

The keyboard hook function has to be placed in an dll since you want to intercept all system keyboard messages.键盘钩子 function 必须放在 dll 中,因为您要拦截所有系统键盘消息。 Also, a memory mapped file has to be used for the wm_input processing code to communicate with the dll.此外,一个 memory 映射文件必须用于 wm_input 处理代码与 dll 通信。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM