网上大神的方法是无焦点接收USB扫码枪数据,我的需求是当前窗口接收就行,没有必要设置全局键盘钩子。
自己琢磨了一个类,没有API调用:
/// <summary> /// 条码解析类 /// </summary> public class Cl_BarCodeReader { #region 属性 /// <summary> /// 发布器委托 /// </summary> /// <param name="Barcode">有效码值字符串</param> /// <param name="cl">条码种类</param> public delegate void ScanerDelegate(string Barcode, Barcode_Class cl); /// <summary> /// 接收到有效条码的事件发布器 /// </summary> public event ScanerDelegate ScanerReceived; /// <summary> /// 条码信息字符串 /// </summary> public string BarCode { get; set; } /// <summary> /// 按键数组 /// </summary> public List<Keys> MyKeys { get; set; } /// <summary> /// 初始按键时间 /// </summary> public long MyTime { get; set; } #endregion #region 构造 /// <summary> /// 空白构造函数 /// </summary> public Cl_BarCodeReader() { BarCode = ""; MyKeys = new List<Keys>(); MyTime = 0; }#endregion #region 编辑 /// <summary> /// 处理一个捕获的按键键值,如果扫码完毕,触发接收事件ScanerReceived /// 传递包含的码值字符串和条码种类枚举 /// 编码规则:nn...ED,n是数字,末尾两个字母ED /// 前两位数字表示预先规定的条码的种类/来源,例如: /// 01 - 执行跟踪号 /// 02 - 计划跟踪号 /// ... /// </summary> /// <param name="key">按键键值</param> public void Add(Keys key) { //忽略扫码枪传来的Shift键值 if (key == Keys.ShiftKey) return; var tick = DateTime.Now.Ticks; //初次接收 if (MyKeys.Count == 0) { //如果不是数字,不处理 if (!char.IsDigit(Convert.ToChar(key))) return; MyKeys.Add(key); MyTime = tick; return; } //计算和第一个键值的时间差 var tsp = tick - MyTime; /* //在即时窗口输出时间差、键值,调试用 var tk = new Task(() => Debug.WriteLine(tsp.ToString() + " " + Convert.ToChar(key) + " " + key.ToString())); tk.Start(); */ //时间差大于50毫秒时,不是扫码枪按键,重新捕获 if (tsp > 10000 * 50) { MyKeys.Clear(); MyTime = 0; Add(key); } else { //检查第二位是否为数字,此处需根据条码的编码规则修改 if (MyKeys.Count == 1) { if (!char.IsDigit(Convert.ToChar(key))) { MyKeys.Clear(); MyTime = 0; return; } } MyKeys.Add(key); //当存储的键值超过4个时,检查是否结束 if (MyKeys.Count > 4) { //末两位是ED if (MyKeys[MyKeys.Count - 2] == Keys.E && MyKeys[MyKeys.Count - 1] == Keys.D) { BarCode = ""; for (int i = 0; i < MyKeys.Count; i++) { BarCode += Convert.ToChar(MyKeys[i]); } MyKeys.Clear(); MyTime = 0; var cd = Code(); //触发事件 if (!string.IsNullOrEmpty(cd)) ScanerReceived(cd, BarCodeClass()); } } } } #endregion #region 判断 /// <summary> /// 判断当前的条码字符串是否有效 /// </summary> /// <returns></returns> public bool IsEffect() { return IsEffect(BarCode); } /// <summary> /// 判断指定的条码字符串是否是有效条码 /// </summary> /// <param name="barcode"></param> /// <returns></returns> public static bool IsEffect(string barcode) {
//长度检查 if (barcode.Length < 5) return false;
//前两位数字检查 if (!char.IsDigit(barcode, 0) || !char.IsDigit(barcode, 1)) return false;
//末两位是否是ED if (barcode.Substring(barcode.Length - 2, 2) != "ED") return false;
//条码种类检查 if (BarCodeClass(barcode.Substring(0, 2)) == Barcode_Class.Unkown) return false; return true; } #endregion #region 信息 /// <summary> /// 返回当前条码字符串中的信息部分 /// 无效返回空字符串 /// </summary> /// <returns></returns> public string Code() { return Code(BarCode); } /// <summary> /// 返回条码字符串中的信息部分 /// </summary> /// <param name="barcode"></param> /// <returns></returns> public static string Code(string barcode) { if (IsEffect(barcode)) {
//去除前两位和末两位 return barcode.Substring(2, barcode.Length - 4); } else return ""; } /// <summary> /// 返回当前条码种类枚举 /// </summary> /// <returns></returns> public Barcode_Class BarCodeClass() { if (BarCode.Length > 4) return BarCodeClass(BarCode.Substring(0, 2)); else return Barcode_Class.Unkown; } /// <summary> /// 返回当前条码种类名称 /// </summary> /// <returns></returns> public string BarCodeClassName() { var bclass = BarCodeClass();
//GetEnumDescription是一个利用反射获取枚举自定义特性的方法,具体实现可以百度 return Cl_General.GetEnumDescription(bclass); } /// <summary> /// 依据条码前缀,返回条码种类枚举 /// </summary> /// <param name="codepre"></param> public static Barcode_Class BarCodeClass(string codepre) { switch (codepre) { case "01": return Barcode_Class.CarryTrackCode; case "02": return Barcode_Class.PlanTrackCode; default: return Barcode_Class.Unkown; } } /// <summary> /// 依据条码前缀,返回条码种类名称 /// </summary> /// <param name="codepre"></param> /// <returns></returns> public static string BarCodeClassName(string codepre) { var bclass = BarCodeClass(codepre);
//GetEnumDescription是一个利用反射获取枚举自定义特性的方法,具体实现可以百度 return Cl_General.GetEnumDescription(bclass); } #endregion } /// <summary> /// 条码种类枚举 /// </summary> public enum Barcode_Class { [Description("未知")] /// <summary> /// 未知 /// </summary> Unkown, [Description("执行跟踪号")] /// <summary> /// 执行跟踪号 /// </summary> CarryTrackCode, [Description("计划跟踪号")] /// <summary> /// 计划跟踪号 /// </summary> PlanTrackCode }
我把这个类放在了我的基窗口里,继承窗口只需要override ScanerHandler来实现具体的收到条码方法,窗体Load时调用一次SetBarCodeReader即可:
public partial class fm_BaseForm : Form { protected Cl_BarCodeReader BReader = null; /// <summary> /// 窗体键抬起事件处理过程,用于截获扫码枪消息 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected virtual void Form_KeyUp(object sender, KeyEventArgs e) { BReader.Add(e.KeyCode); } /// <summary> /// 启用扫码枪监视对象 /// </summary> protected virtual void SetBarCodeReader(bool setkeypre = true) { if (setkeypre) { this.KeyPreview = true; this.KeyUp += Form_KeyUp; } if (BReader == null) { BReader = new Cl_BarCodeReader(); BReader.ScanerReceived += ScanerHandler; } } /// <summary> /// 停止扫码枪监视对象 /// </summary> protected virtual void StopBarCodeReader(bool setkeypre = true) { if (setkeypre) { this.KeyUp -= Form_KeyUp; this.KeyPreview = false; } if (BReader != null) { BReader.ScanerReceived -= ScanerHandler; BReader = null; } } /// <summary> /// 收到有效条码时的处理过程 /// </summary> /// <param name="bcode"></param> protected virtual void ScanerHandler(string bcode, Barcode_Class cl) { //收到有效条码时的详细过程... } }