網上大神的方法是無焦點接收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) { //收到有效條碼時的詳細過程... } }