【问题标题】:How to distinguish between multiple input devices in C#C#中如何区分多个输入设备
【发布时间】:2009-02-25 21:04:44
【问题描述】:

我有一个条形码扫描仪(它就像一个键盘),当然我还有一个连接到计算机的键盘。该软件正在接受来自扫描仪和键盘的输入。我只需要接受扫描仪的输入。代码是用 C# 编写的。有没有办法“禁用”键盘输入,只接受来自扫描仪的输入?

注意: 键盘是笔记本电脑的一部分……所以它不能拔掉。另外,我尝试输入以下代码 protected override Boolean ProcessDialogKey(System.Windows.Forms.Keys keyData) { 返回真; } 但是随着忽略键盘的击键,条形码扫描仪的输入也被忽略了。

我不能让扫描仪发送标记字符,因为扫描仪正被其他应用程序使用,添加标记字符流意味着修改其他代码。

另外,我不能使用计时方法来确定输入是否来自条形码扫描仪(如果它是一堆字符,然后是暂停),因为扫描的条形码可能是单字符条形码。

是的,我正在从流中读取数据。

我正在尝试阅读文章:将条码扫描仪与 WinForms 中的键盘区分开来。但是我有以下问题:

  1. 我收到一个错误 NativeMethods 由于其保护级别而无法访问。好像我需要导入一个dll;它是否正确?如果是这样,我该怎么做?
  2. 我应该使用哪个protected override void WndProc(ref Message m) 定义,文章中有两种实现方式?
  3. 收到与 [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] 相关的错误错误 CS0246:找不到类型或命名空间名称“SecurityPermission”(您是否缺少 using 指令或程序集引用? )。如何解决此错误?
  4. 在包含以下内容的行中还有一个错误:if ((from hardwareIds in hardwareIds where deviceName.Contains(hardwareId) select hardwareId).Count() > 0) Error is error CS1026: ) expected.
  5. 我是否应该将文章中的所有代码放在一个名为 BarcodeScannerListener.cs 的 .cs 文件中?

Nicholas Piasecki 在http://nicholas.piasecki.name/blog/2009/02/distinguishing-barcode-scanners-from-the-keyboard-in-winforms/ 上发布的有关 C# 解决方案源代码的后续问题:

  1. 我无法在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_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-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_0536&Pid_01c1 有 2 个条目;这会导致扫描无法工作吗?

好吧,看来我必须想办法不依赖扫描仪发送的 ASCII 0x04 字符……因为我的扫描仪不发送该字符。之后,触发条形码扫描事件并显示带有条形码的弹出窗口。所以感谢尼古拉斯的帮助。

【问题讨论】:

  • 我在文章底部添加了一个示例代码发布。祝你好运!
  • @NicholasPiasecki 已经 6 年了,但仍然 - 为什么是 HTTP 410?
  • 如果您的扫描仪支持 USB HID,您最好使用 Windows 10 中提供的新 API,禁用键盘模拟:github.com/Microsoft/Windows-universal-samples/tree/master/… ... 在旧版本上,您可以使用 SetupDi .. . 如果你必须使用键盘emu,你可以在archive.org找到它:web.archive.org/web/20150212020144/http://… ... HTTP 410意味着很多东西,包括“生命短暂”、“媒介已经过时”和“世界已满”混蛋”
  • @NicholasPiasecki,您或任何人能否分享 Nicholas Piasecki 的源代码。他的博客不工作了。我正在申请适用于 windows xp 的条码扫描器
  • @Qeeet,我也在寻找 Nicholas Piasecki 源代码。你找到了吗?

标签: c# barcode-scanner


【解决方案1】:

You could use the Raw Input API to distinguish between the keyboard and the scanner like I did recently. 不管你连接了多少键盘或类似键盘的设备;在击键映射到通常在 KeyDown 事件中看到的与设备无关的虚拟键之前,您将看到 WM_INPUT

更容易的是按照其他人的建议进行操作,并将扫描仪配置为在条形码之前和之后发送标记字符。 (您通常通过扫描扫描仪用户手册背面的特殊条形码来做到这一点。)然后,您的主窗体的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 的条形码扫描仪,而不仅仅是键盘楔。如果您的扫描仪只是一个键盘楔子,我建议您从 eBay 上以 25 美元的价格购买二手霍尼韦尔 4600G 之类的东西。相信我,你的理智是值得的。

【讨论】:

  • 我正在尝试跟随这篇文章。但是,我收到一个错误 NativeMethods 由于其保护级别而无法访问。好像我需要导入一个dll;它是否正确?如果是这样,我该怎么做?另外,我应该使用哪个受保护的覆盖 void WndProc(ref Message m) 定义?
  • 也收到与 [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] 相关的错误错误 CS0246:找不到类型或命名空间名称“SecurityPermission”(您是否缺少使用指令还是程序集引用?)
  • 在包含以下内容的行中出现错误:if ((from hardwareId in hardwareIds where deviceName.Contains(hardwareId) select hardwareId).Count() > 0) Error is error CS1026: ) 预期跨度>
  • @jaredbaszler MSDN 说,相同的 API 支持支持“SPP-SSI”msdn.microsoft.com/en-us/library/windows/apps/mt426649.aspx 的蓝牙扫描仪,并且 Scanned 事件确实发送了它来自 msdn.microsoft.com/en-us/library/windows/apps/… 的设备,因此理论上您可以将 DeviceIds 映射到用户告诉它来自哪里
  • @NicholasPiasecki - 我想知道上面的代码在什么框架中。我正在尝试将Windows.Devices.PointOfService 库合并到当前的Winforms 项目中,但运气不佳。我可以将库添加到我的项目中,但由于缺少对似乎是 Win10 SDK 其他部分的引用,上述代码片段无法编译。
【解决方案2】:

我在类似情况下所做的是通过查看输入速度来区分扫描和用户打字。

很多字符非常靠近,然后暂停就是扫描。其他都是键盘输入。

我不确切地知道你的要求,所以也许这对你没有用,但这是我所拥有的最好的:)

【讨论】:

  • 是的,直接从设备中查找和读取并非易事。您应该能够将扫描仪设置为也提供启动和停止序列 - 这些序列永远不会出现在键盘上。
  • 您能否提供一些代码示例来说明您是如何实现这一目标的?我还使用输入速度找到了解决方案。但是,这不是一个好的选择。有时击键之间的空白非常大。例如:15 15 16 180 13 15 15 15(以毫秒为单位)
【解决方案3】:

这取决于您与设备交互的方式。无论如何,它不会是 C# 解决方案,而是其他一些库。您是否正在从流中读取数据?如果您只是敲击键盘,您可能无能为力。

【讨论】:

    【解决方案4】:

    我知道这是一个老线程,在WIN10中搜索条形码扫描找到它。 只需几条笔记以防有人需要。

    霍尼韦尔的这些扫描仪具有多个 USB 接口。 一种是键盘+Hid Point of sale(复合设备)。 还有 CDC-ACM(ComPort 仿真)和 Hid 销售点(单独)等等。

    默认情况下,扫描仪会公开一个序列号,因此主机可以区分许多设备(我曾经连接过 +20)。不过有一个命令可以禁用序列号!

    较新的模型在这方面表现相同。 如果你想现场观看,试试我的终端程序 yat3(在my site 上免费)。 它可以打开上述所有接口,并且是为此类设备量身定制的。

    使用键盘界面的一句话:
    仅将它们用作最后​​的手段。当涉及到异国情调的角色时,它们很慢,不太可靠。唯一的好用处是如果您想将数据输入到现有应用程序中。如果你仍然编码,那么从 ComPort/HidPos-Device 读取会更容易。

    【讨论】:

      【解决方案5】:

      看看这个:http://nate.dynalias.net/dev/keyboardredirector.railsNOT ANYMORE)效果很好!

      指定您要阻止的键盘和键,它就像一个魅力!

      也看看这个:http://www.oblita.com/interception.html 你可以为它创建一个 C# 包装器——它也像一个魅力一样工作..

      【讨论】:

        【解决方案6】:

        我认为您可以通过 DirectX API 区分多个键盘,或者如果这不起作用,则通过原始输入 API。

        【讨论】:

        • (注意:条码扫描器对 Windows 来说只是另一个键盘)
        【解决方案7】:

        我已经成功地完成了你们在这里寻找的东西。我有一个从 Honeywell/Metrologic 条码扫描仪接收所有条码字符数据的应用程序。系统上没有其他应用程序接收到来自扫描仪的数据,键盘继续正常工作。

        我的应用程序结合使用原始输入和可怕的低级键盘挂钩系统。与这里写的相反,我发现在调用键盘钩子函数之前收到了 wm_input 消息。我处理 wm_input 消息的代码基本上设置了一个布尔变量来指定接收到的字符是否来自扫描仪。处理 wm_input 后​​立即调用的键盘挂钩函数会吞下扫描仪的伪键盘数据,从而防止数据被其他应用程序接收。

        键盘钩子函数必须放在一个 dll 中,因为您想拦截所有系统键盘消息。此外,wm_input 处理代码必须使用内存映射文件与 dll 进行通信。

        【讨论】:

        • 这有什么新的吗? - 似乎这家伙已经在不同的页面上发布了几个关于这个问题的 cmets..
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-25
        • 1970-01-01
        • 2019-06-13
        • 1970-01-01
        • 2022-01-24
        相关资源
        最近更新 更多