C# 多输入设备识别 扫码枪键盘区分RAWINPUT原始输入简易开发笔记
工作需要写了一个小程序,结合cefsharp利用erp软件的api进行二次开发,实现定制功能。软件会用到扫码枪扫描快递单号发货。会用到蓝牙电子称称重。还会遇到快递拆单快速新建订单并打印订单,也需要用到扫码枪扫描快递单,然后新建指定数量新的快递单。开发中遇到很多问题。比如,扫码枪会认定为keyboard。蓝牙接收器也会认定为keyboard。加上自身的键盘。这么多的输入设备,如果遇到2个或者2个以上输入设备同时输入就会产生干扰。不能同时输入。针对这个问题,查了很多资料发现rawinput原始输入才是解决这个问题的关键。接下来会结合开发代码进行记录。
一、我遇到的第一个问题如何识别是扫码枪输入的快递单号。还是键盘输入的信息。
网上有人的给出的解决方案是利用键盘钩子,记录键盘的按键keypress事件或者keydown事件的时间间隔。一般设置50毫秒。会认为是扫码枪的输入。因为人工输入达不到这么快的速度。虽然我也这样做了,也可以区分是扫描枪还是键盘输入了。但实际使用的时候还是会产生冲突的情况。遇到了rawinput之后这个问题就非常简单了。原始输入得到的信息里包括了输入设备的信息,这样通过识别设备的信息来区分不同的设备。
private const int RIDEV_INPUTSINK = 0x100;
private const int RIDEV_NoLegacy = 0x30;
private const int WM_INPUT = 0x00FF;
private const uint RID_INPUT = 0x10000003;
private const uint RIDI_DEVICENAME = 0x20000007;
private const string HoneywellScanning = @"HID#VID_0C2E&PID_0204";
StringBuilder 创建订单扫码枪 = new StringBuilder();
[StructLayout(LayoutKind.Sequential)]
internal struct RAWINPUTDEVICE
{
[MarshalAs(UnmanagedType.U2)]
public ushort usUsagePage;
[MarshalAs(UnmanagedType.U2)]
public ushort usUsage;
[MarshalAs(UnmanagedType.U4)]
public int dwFlags;
public IntPtr hwndTarget;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RAWINPUTHEADER
{
[MarshalAs(UnmanagedType.U4)]
public int dwType;
[MarshalAs(UnmanagedType.U4)]
public int dwSize;
public IntPtr hDevice;
[MarshalAs(UnmanagedType.U4)]
public int wParam;
}
[StructLayout(LayoutKind.Explicit)]
internal struct RAWINPUT
{
[FieldOffset(0)]
public RAWINPUTHEADER header;
[FieldOffset(16)]
public RAWMOUSE mouse;
[FieldOffset(16)]
public RAWKEYBOARD keyboard;
[FieldOffset(16)]
public RAWHID hid;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RAWKEYBOARD
{
[MarshalAs(UnmanagedType.U2)]
public ushort MakeCode;
[MarshalAs(UnmanagedType.U2)]
public ushort Flags;
[MarshalAs(UnmanagedType.U2)]
public ushort Reserved;
[MarshalAs(UnmanagedType.U2)]
public ushort VKey;
[MarshalAs(UnmanagedType.U4)]
public uint Message;
[MarshalAs(UnmanagedType.U4)]
public uint ExtraInformation;
}
[StructLayout(LayoutKind.Explicit)]
internal struct RAWMOUSE
{
[MarshalAs(UnmanagedType.U2)]
[FieldOffset(0)]
public ushort usFlags;
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(4)]
public uint ulButtons;
[FieldOffset(4)]
public BUTTONSSTR buttonsStr;
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(8)]
public uint ulRawButtons;
[FieldOffset(12)]
public int lLastX;
[FieldOffset(16)]
public int lLastY;
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(20)]
public uint ulExtraInformation;
}
[StructLayout(LayoutKind.Sequential)]
internal struct BUTTONSSTR
{
[MarshalAs(UnmanagedType.U2)]
public ushort usButtonFlags;
[MarshalAs(UnmanagedType.U2)]
public ushort usButtonData;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RAWHID
{
[MarshalAs(UnmanagedType.U4)]
public int dwSizHid;
[MarshalAs(UnmanagedType.U4)]
public int dwCount;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RID_DEVICE_INFO_HID
{
[MarshalAs(UnmanagedType.U4)]
public int dwVendorId;
[MarshalAs(UnmanagedType.U4)]
public int dwProductId;
[MarshalAs(UnmanagedType.U4)]
public int dwVersionNumber;
[MarshalAs(UnmanagedType.U2)]
public ushort usUsagePage;
[MarshalAs(UnmanagedType.U2)]
public ushort usUsage;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RID_DEVICE_INFO_KEYBOARD
{
[MarshalAs(UnmanagedType.U4)]
public int dwType;
[MarshalAs(UnmanagedType.U4)]
public int dwSubType;
[MarshalAs(UnmanagedType.U4)]
public int dwKeyboardMode;
[MarshalAs(UnmanagedType.U4)]
public int dwNumberOfFunctionKeys;
[MarshalAs(UnmanagedType.U4)]
public int dwNumberOfIndicators;
[MarshalAs(UnmanagedType.U4)]
public int dwNumberOfKeysTotal;
}
[StructLayout(LayoutKind.Explicit)]
internal struct RID_DEVICE_INFO
{
[FieldOffset(0)]
public int cbSize;
[FieldOffset(4)]
public int dwType;
[FieldOffset(8)]
public RID_DEVICE_INFO_MOUSE mouse;
[FieldOffset(8)]
public RID_DEVICE_INFO_KEYBOARD keyboard;
[FieldOffset(8)]
public RID_DEVICE_INFO_HID hid;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RID_DEVICE_INFO_MOUSE
{
[MarshalAs(UnmanagedType.U4)]
public int dwId;
[MarshalAs(UnmanagedType.U4)]
public int dwNumberOfButtons;
[MarshalAs(UnmanagedType.U4)]
public int dwSampleRate;
[MarshalAs(UnmanagedType.U4)]
public int fHasHorizontalWheel;
}
[DllImport("User32.dll")]
extern static uint GetRawInputData(IntPtr hRawInput, uint uiCommand, IntPtr pData, ref uint pcbSize, uint cbSizeHeader);
[DllImport("User32.dll")]
extern static bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevice, uint uiNumDevices, uint cbSize);
[DllImport("User32.dll")]
extern static uint GetRawInputDeviceInfo(IntPtr hDevice, uint uiCommand, IntPtr pData, ref uint pcbSize);
private void 订阅WM_INPUT信息()
{
RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[1];
rid[0].usUsagePage = 0x01;
rid[0].usUsage = 0x06;
rid[0].dwFlags = RIDEV_INPUTSINK | RIDEV_NoLegacy;
rid[0].hwndTarget = this.Handle;
RegisterRawInputDevices(rid, (uint)rid.Length, (uint)Marshal.SizeOf(rid[0]));
}
我写了一个方法 订阅WM_INPUT信息,用来注册设备。这里RAWINPUTDEVICE是一个结构体。微软官网有详细介绍。usUsagePage=0x01,usUsage = 0x06 这两个选项代表注册的是键盘设备。
dwFlags是一个控制标志,RIDEV_INPUTSINK代表不论程序在焦点状态或者失去焦点状态都会得到WM_INPUT信息。RIDEV_NoLegacy代表你会接收到WM_INPUT信息,但是不会把信息给到winform里。比如你可以接收到按下键盘数字“1”,但是winform里textbox不会有任何变化,相当于屏蔽了键盘的任何输入信息到winform里。两者可以同时写。我根据自身实际情况选择了两者同时写。微软官网还有更多的控制指令。RAWINPUTDEVICE (winuser.h) - Win32 apps | Microsoft Docs这个是介绍文档。很遗憾是英文版。最后是hwndTarget 我设置给了当前winform的handle。
最后一句就是调用winapi注册设备了。注册之后会收到WM_INPUT类型信息。接下来是重写WndProc方法处理WM_INPUT信息。
protected override void WndProc(ref Message message)
{
if (message.Msg == WM_INPUT)
processMessage(message);
base.WndProc(ref message);
}
如果Msg的类型是WM_INPUT 调用processMessage这个方法来处理message。
private void processMessage(Message message)
{
uint dwSize = 0;
GetRawInputData(message.LParam, RID_INPUT, IntPtr.Zero, ref dwSize, (uint)Marshal.SizeOf(typeof(RAWINPUTHEADER)));
IntPtr buffer = Marshal.AllocHGlobal((int)dwSize);
GetRawInputData(message.LParam, RID_INPUT, buffer, ref dwSize, (uint)Marshal.SizeOf(typeof(RAWINPUTHEADER)));
RAWINPUT raw = (RAWINPUT)Marshal.PtrToStructure(buffer, typeof(RAWINPUT));
uint size = (uint)Marshal.SizeOf(typeof(RID_DEVICE_INFO));
GetRawInputDeviceInfo(raw.header.hDevice, RIDI_DEVICENAME, IntPtr.Zero, ref size);
IntPtr pData = Marshal.AllocHGlobal((int)size);
GetRawInputDeviceInfo(raw.header.hDevice, RIDI_DEVICENAME, pData, ref size);
string deviceName = (string)Marshal.PtrToStringAnsi(pData);
//判断deviceName来自哪个设备。做对应的处理
//doSomething..
//下面两句是释放了缓存
Marshal.FreeHGlobal(buffer);
Marshal.FreeHGlobal(pData);
}
两次 调用GetRawInputData 这个winapi。第一次调用为了得到dwSize这个长度,然后定义了dwsize长度的缓存buffer来保存返回的数据。数据是RAWINPUT类型的。然后通过raw.keyboard.Message得到按键值。翻译工作这里不做介绍了。每一个按键会得到2次信息。对应的就是keydown,keyup完成一次按键敲击。有精力的朋友可以继续深入研究。我有这个信息就足够了没在研究下去了。
已经知道了按键的数据。现在还需要知道是哪个设备发送过来的。就到了下面的GetRawInputDeviceInfo这个winapi,同样是2次。第一次得到size,第二次得到数据。我这里需要的是设备名字。所以用的RIDI_DEVICENAME这个控制指令。官网有其他指令的介绍。
现在就可以通过设备名称来识别是哪个设备来的信息。分别保存到各自的StringBuilder中。
//doSomething..
举例。。
if (deviceName.Contains(HoneywellScanning))
{
switch (raw.keyboard.Message)
{
case (uint)21:
创建订单扫码枪.Append('Y');
break;
case (uint)20:
创建订单扫码枪.Append('T');
break;
case (uint)11:
创建订单扫码枪.Append('0');
break;
case (uint)10:
创建订单扫码枪.Append('9');
break;
case (uint)9:
创建订单扫码枪.Append('8');
break;
case (uint)8:
创建订单扫码枪.Append('7');
break;
case (uint)7:
创建订单扫码枪.Append('6');
break;
case (uint)6:
创建订单扫码枪.Append('5');
break;
case (uint)5:
创建订单扫码枪.Append('4');
break;
case (uint)4:
创建订单扫码枪.Append('3');
break;
case (uint)3:
创建订单扫码枪.Append('2');
break;
case (uint)2:
创建订单扫码枪.Append('1');
break;
//(uint)28 是回车
case (uint)28:
try
{
string 快递单号 = 创建订单扫码枪.ToString();
创建订单扫码枪.Clear();
//创建订单(快递单号);
}
catch (Exception ex) { MessageBox.Show(ex.ToString()); }
break;
}
}
这样我有2个扫描枪。1个蓝牙称。他们可以互不干扰的输入着各自的数据。也不会因为同时输入而收到干扰。也不会因为winform的textbox得到了焦点被莫名扫码枪输入一串数字。也不会被扫码枪的自带回车键给提交了表单避免了错误操作。
结束语:这个是我写的第一个文章。方便自己今后阅读,也希望能够帮助到需要帮助的人.本人文化水平有限。代码纯属个人爱好。高手勿喷。直接绕路。qq281781648需要源码可以联系本人
C# 多输入设备识别 扫码枪键盘区分RAWINPUT原始输入简易开发笔记相关推荐
- 重新想象 Windows 8 Store Apps (49) - 输入: 获取输入设备信息, 虚拟键盘, Tab 导航, Pointer, Tap, Drag, Drop...
重新想象 Windows 8 Store Apps (49) - 输入: 获取输入设备信息, 虚拟键盘, Tab 导航, Pointer, Tap, Drag, Drop 原文:重新想象 Window ...
- 计算机键盘在线识别,usb键盘,教您电脑usb键盘无法识别解决方法
在现在,电脑已成了我们生活中必不可少的实用工具.不管在生活中的聊天.娱乐,还是在学习上的查阅都可以应用的到,我么你知道,当我们在使用电脑,都是要用到usb键盘的,但是如果遇到无法识别时,该怎么办呢?为 ...
- 计算机键盘在线识别,usb键盘,详细教您电脑usb键盘无法识别解决方法
键盘是最常用也是最主要的输入设备,通过键盘,可以将英文字母.数字.标点符号等输入到计算机中,从而向计算机发出命令.输入数据等.不过有用户在使用usb键盘的时候,就遇到了usb键盘无法识别的情况,下面, ...
- 电脑无法识别扫码枪怎么办?看4点解决方法就知道
近来有很多朋友问远景达(RAKINDA)的技术同事,为什么电脑不能识别扫码枪呢?随着自动识别技术的设备应用于我们生活的多种领域上,同时也给我们带来便捷.但是扫码枪作为由光学.电子.机械.软件应用技术紧 ...
- QT 版本号识别 不同系统区分
QT 版本号识别 不同系统区分 版本号的识别 获取使用QT的版本号 qt版本号的比较 qt 版本小版本的区别 不同系统使用区分 版本号的识别 获取使用QT的版本号 1.$$[QT_VERSION] 在 ...
- 电脑不识别扫码枪怎么解决?
近来有很多朋友问远景达的技术同事,为什么电脑不能识别扫码枪呢?随着自动识别技术的设备应用于我们生活的多种领域上,同时也给我们带来便捷.但是扫码枪作为由光学.电子.机械.软件应用技术紧密结合的电子设备, ...
- Win7系统不识别usb键盘
Win7系统不识别usb键盘 打开win7设备管理器,找到usb键盘并直接打开,发现如下usb键盘中的驱动中是有带感叹号的. 第1步:在桌面找到计算机图标,并在此图标上右键选择管理,打开计算机管理程序 ...
- 计算机识别不成USB键盘,如果无法识别计算机键盘该怎么办?提示无法识别的USB设备...
1. 如果无法识别PS / 2接口键盘该怎么办? 对于台式计算机上最常见的PS / 2接口键盘,如果无法识别,则可能是由于键盘故障或计算机主板上的PS / 2接口引起的. 通常,主板上的PS / 2接 ...
- 痞子衡嵌入式:超级下载算法(RT-UFL)开发笔记(2) - 识别当前i.MXRT型号
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是超级下载算法开发笔记(2)之识别当前i.MXRT型号. 文接上篇 <超级下载算法(RT-UFL)开发笔记(1) - 执行在不同CM ...
最新文章
- [16] 螺旋面(Spire)图形的生成算法
- 如何用OKR搞垮一个团队?
- mongodb高可用性架构---Replica Set
- 第七周项目三-用多文件组织多个类的程序
- Wasserstein距离在生成模型中的应用
- 什么是真正的APM?
- java连接摄像头_Java实现 海康摄像头抓拍图像(示例代码)
- aws cognito_AWS Cognito的用户管理—(2/3)核心功能
- H264中I、B、P帧编码原理及pts和dts分析
- Go Web编程--使用bcrpyt哈希用户密码
- Gibbs 采样定理的若干证明
- Altium Designer(六):Make Library
- [蛋蛋涂鸦]蜡笔蛋&某蛋真面目(表被吓着)
- RNN神经网络- 吴恩达Andrew Ng 循环神经网络 NLP Transformers Week4 知识总结
- Xcode证书错误 Provisioning profile does not support the Associated Domains capability
- 低版本 android 软件下载,纳米盒旧版本下载-纳米盒旧版下载4.1安卓版-西西软件下载...
- 《数字图像处理 第三版》(冈萨雷斯)——第五章 图像复原与重建
- 1的阶乘加到100的阶乘
- Macbook Tools
- 【java】英语单词对战小游戏