C#中如何区分多个输入设备
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/587840/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to distinguish between multiple input devices in C#
提问by Amar Patel
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#. Is there a way to "disable" input from the keyboard and only accept input from the scanner?
我有一个条码扫描器(它就像一个键盘),当然我的键盘也连接到了电脑上。该软件正在接受来自扫描仪和键盘的输入。我只需要接受扫描仪的输入。代码是用 C# 编写的。有没有办法“禁用”来自键盘的输入并只接受来自扫描仪的输入?
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; } But then along with ignoring the keystrokes from the keyboard, the barcode scanner input is also ignored.
注意:键盘是笔记本电脑的一部分......所以它不能被拔掉。另外,我尝试将以下代码保护覆盖 Boolean ProcessDialogKey(System.Windows.Forms.Keys keyData) { return true; 但是随着忽略键盘的击键,条码扫描仪输入也被忽略。
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.
我不能让扫描器发送标记字符,因为其他应用程序正在使用扫描器,添加标记字符流意味着修改其他代码。
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.
是的,我正在从流中读取数据。
I am trying to follow along with the article: Distinguishing Barcode Scanners from the Keyboard in WinForms. However I have the following questions:
我正在尝试跟随文章:区分条码扫描仪与 WinForms 中的键盘。但是我有以下问题:
- I get an error NativeMethods is inaccessible due to its protection level. It seems as though I need to import a dll; is this correct? If so, how do I do it?
- Which protected override void WndProc(ref Message m) definition should I use, there are two implementations in the article?
- 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?). How do I resolve this error?
- 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.
- Should I be placing all the code in the article in one .cs file called BarcodeScannerListener.cs?
- 我收到错误 NativeMethods 由于其保护级别而无法访问。好像我需要导入一个dll;这样对吗?如果是这样,我该怎么做?
- 我应该使用哪个 protected override void WndProc(ref Message m) 定义,文章中有两个实现?
- 收到与 [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] 错误 CS0246 相关的错误:找不到类型或命名空间名称“SecurityPermission”(您是否缺少 using 指令或程序集引用?)。如何解决此错误?
- 行上还有一个错误,其中包含: if ((from hardwareId in hardwareIds where deviceName.Contains(hardwareId) select hardwareId).Count() > 0) Error is error CS1026: ) expected。
- 我应该将文章中的所有代码放在一个名为 BarcodeScannerListener.cs 的 .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# 解决方案源代码的后续问题:
- I was not able to open the solution in VS 2005, so I downloaded Visual C# 2008 Express Edition, and the code ran. 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. I did change the App.config with the id of my Barcode scanner obtained using Device Manager. There seems to be 2 deviceNames with HID#Vid_0536&Pid_01c1 (which is obtained from Device Manager when the scanner is hooked up). 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):
- 我在 VS 2005 中无法打开解决方案,所以我下载了 Visual C# 2008 Express Edition,并且代码运行了。但是,在连接我的条码扫描器并扫描条码后,程序无法识别扫描。我在 OnBarcodeScanned 方法中放置了一个断点,但它从未被击中。我确实使用设备管理器获得的条形码扫描仪的 ID 更改了 App.config。似乎有 2 个带有 HID#Vid_0536&Pid_01c1 的设备名称(连接扫描仪时从设备管理器获得)。我不知道这是否导致扫描不起作用。在遍历 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}"
"\??\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}"
"\??\HID#Vid_413c&Pid_3012#7&960fae0&0&0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd}"
"\??\Root#RDP_KBD#0000#6ef-c0dc5c5c0d-c5c0d-c5c0dc0000#10000#{378de44c-56ef-11d1-bc8c-00a0c91405dd} \ACPI#PNP0303#4&2f94427b&0#{884b96c3-56ef-11d1-bc8c-00a0c91405dd}" "\??\Root#RDP_MOU#0000#{378de44c-56ef-180c}-10bc0p10dc0p10dc0dc10bc10bc10bc10dc10bc10dc10bc10dc10bc10dc10bc10dc10b #4&2f94427b&0#{378de44c-56ef-11d1-bc8c-00a0c91405dd}"
So there are 2 entries for HID#Vid_0536&Pid_01c1; could that be causing the scanning not to work?
因此,HID#Vid_0536&Pid_01c1 有 2 个条目;这会导致扫描不起作用吗?
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. After that, the barcode scanned event is fired and the popup with the barcode is shown. So thanks Nicholas for your help.
好吧,看来我必须想出一种方法来不依赖于扫描仪发送的 ASCII 0x04 字符......因为我的扫描仪不发送该字符。之后,条形码扫描事件被触发并显示带有条形码的弹出窗口。所以感谢尼古拉斯的帮助。
采纳答案by Nicholas Piasecki
You could use the Raw Input API to distinguish between the keyboard and the scanner like I did recently.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.
您可以像我最近所做的那样使用原始输入 API 来区分键盘和扫描仪。您连接了多少个键盘或类似键盘的设备并不重要;您将WM_INPUT
在击键映射到您通常在KeyDown
事件中看到的与设备无关的虚拟键之前看到。
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. 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).
更容易的是按照其他人的建议并配置扫描仪在条形码之前和之后发送标记字符。(您通常通过扫描扫描仪用户手册背面的特殊条形码来完成此操作。)然后,您的主窗体的KeyPreview
事件可以观察这些滚动结束并吞下任何处于条形码读取中间的子控件的关键事件。或者,如果你想变得更高级,你可以使用一个低级的键盘钩子SetWindowsHookEx()
来监视那些哨兵并将它们吞到那里(这样做的好处是即使你的应用程序没有焦点,你仍然可以获得事件)。
I couldn't change the sentinel values on our barcode scanners among other things so I had to go the complicated route. 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
. It's a UWP/WinRT API, but you can use it from a regular desktop app as well; that's what I'm doing now. Here's some example code for it, straight from my app, to give you the gist:
七年后您的更新:如果您的用例是从 USB 条码扫描器读取数据,Windows 10 为这个内置的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;
}
}
}
}
Granted, you'll need a barcode scanner that supports the USB HID POS and isn't just a keyboard wedge. If your scanner is just a keyboard wedge, I recommend picking up something like a used Honeywell 4600G off eBay for like $25. Trust me, your sanity will be worth it.
当然,您需要一个支持 USB HID POS 的条码扫描器,而不仅仅是一个键盘楔。如果您的扫描仪只是一个键盘楔子,我建议您以 25 美元的价格从 eBay 购买二手霍尼韦尔 4600G 之类的东西。相信我,你的理智是值得的。
回答by Karl
It depends on the way you are interacting with the device. Anyway it wont be a C# solution, it will be some other library. Are you reading data from a stream? If you are just taking keystrokes, there may be nothing you can do about it.
这取决于您与设备交互的方式。无论如何,它不会是 C# 解决方案,而是其他一些库。您是否正在从流中读取数据?如果您只是敲击键盘,则可能无能为力。
回答by Blorgbeard is out
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 :)
我不确切知道您的要求,所以也许这对您不起作用,但这是我所拥有的最好的 :)
回答by Blorgbeard is out
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。
回答by Greg Bergmann
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. No other application on the system receives the data from the scanner, and the keyboard continues to function normally.
我已经成功地完成了你们在这里寻找的东西。我有一个应用程序,它接收来自 Honeywell/Metrologic 条码扫描器的所有条码字符数据。系统上没有其他应用程序从扫描仪接收数据,键盘继续正常工作。
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. 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. 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 消息是在调用键盘钩子函数之前收到的。我处理 wm_input 消息的代码基本上设置了一个布尔变量来指定接收到的字符是否来自扫描仪。处理 wm_input 后立即调用的键盘钩子函数会吞下扫描仪的伪键盘数据,防止数据被其他应用程序接收。
The keyboard hook function has to be placed in an dll since you want to intercept all system keyboard messages. Also, a memory mapped file has to be used for the wm_input processing code to communicate with the dll.
由于您要拦截所有系统键盘消息,因此必须将键盘挂钩函数放在 dll 中。此外,内存映射文件必须用于 wm_input 处理代码以与 dll 通信。
回答by MMM
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.htmlYou can create a C# wrapper for it - it also works like a charm..
也看看这个:http: //www.oblita.com/interception.html你可以为它创建一个 C# 包装器 - 它也像一个魅力..
回答by DieterF
I know this is an old thread, found it by searching barcode scanning in WIN10. Just a few notes in case someone needs it.
我知道这是一个旧线程,通过在 WIN10 中搜索条码扫描找到它。只是一些笔记,以防有人需要它。
These scanners from Honeywell have several USB interfaces. One is a keyboard + Hid Point of sales (composite device). Also there are CDC-ACM (ComPort emulation) and Hid Point of sales (alone) + more.
霍尼韦尔的这些扫描仪具有多个 USB 接口。一种是键盘+Hid Point of sales(复合设备)。还有 CDC-ACM(ComPort 仿真)和 Hid Point of sales(单独)+更多。
By default the scanners expose a serial number, so the host can distinguish between many devices (I had once +20 connected). There is a command to disable the serial number though!
默认情况下,扫描仪会公开一个序列号,因此主机可以区分许多设备(我曾经连接过 +20)。但是有一个命令可以禁用序列号!
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). It can open all the interfaces mentioned above and is tailored for such devices.
较新的模型在这方面表现相同。如果您想现场观看,请尝试我的终端程序 yat3(在我的网站上免费)。它可以打开上面提到的所有接口,并且是为此类设备量身定制的。
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 读取会更容易。