C#獲取掃碼槍掃描資料並處理
阿新 • • 發佈:2022-12-02
開發原因:工廠產品需要頻繁掃描產品SN進行產品踢轉處理不良以及工單結多產品
直接上程式碼:
using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; namespace WindowsFormsApplication6 { internal class ScanerHook { public delegate void ScanerDelegate(ScanerCodes codes); public event ScanerDelegate ScanerEvent; //private const int WM_KEYDOWN = 0x100;//KEYDOWN //private const int WM_KEYUP = 0x101;//KEYUP //private const int WM_SYSKEYDOWN = 0x104;//SYSKEYDOWN //private const int WM_SYSKEYUP = 0x105;//SYSKEYUP //private static int HookProc(int nCode, Int32 wParam, IntPtr lParam); private int hKeyboardHook = 0;//宣告鍵盤鉤子處理的初始值 private ScanerCodes codes = new ScanerCodes();//13為鍵盤鉤子 //定義成靜態,這樣不會丟擲回收異常 private static HookProc hookproc; delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] //設定鉤子 private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId); [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] //解除安裝鉤子 private static extern bool UnhookWindowsHookEx(int idHook); [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] //繼續下個鉤子 private static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam); [DllImport("user32", EntryPoint = "GetKeyNameText")] private static extern int GetKeyNameText(int IParam, StringBuilder lpBuffer, int nSize); [DllImport("user32", EntryPoint = "GetKeyboardState")] //獲取按鍵的狀態 private static extern int GetKeyboardState(byte[] pbKeyState); [DllImport("user32", EntryPoint = "ToAscii")] //ToAscii職能的轉換指定的虛擬鍵碼和鍵盤狀態的相應字元或字元 private static extern bool ToAscii(int VirtualKey, int ScanCode, byte[] lpKeySate, ref uint lpChar, int uFlags); //int VirtualKey //[in] 指定虛擬關鍵程式碼進行翻譯。 //int uScanCode, // [in] 指定的硬體掃描碼的關鍵須翻譯成英文。高階位的這個值設定的關鍵,如果是(不壓) //byte[] lpbKeyState, // [in] 指標,以256位元組陣列,包含當前鍵盤的狀態。每個元素(位元組)的陣列包含狀態的一個關鍵。如果高階位的位元組是一套,關鍵是下跌(按下)。在低位元,如/果設定表明,關鍵是對切換。在此功能,只有肘位的CAPS LOCK鍵是相關的。在切換狀態的NUM個鎖和滾動鎖定鍵被忽略。 //byte[] lpwTransKey, // [out] 指標的緩衝區收到翻譯字元或字元。 //uint fuState); // [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise. [DllImport("kernel32.dll")] //使用WINDOWS API函式代替獲取當前例項的函式,防止鉤子失效 public static extern IntPtr GetModuleHandle(string name); public ScanerHook() { } public bool Start() { if (hKeyboardHook == 0) { hookproc = new HookProc(KeyboardHookProc); //GetModuleHandle 函式 替代 Marshal.GetHINSTANCE //防止在 framework4.0中 註冊鉤子不成功 IntPtr modulePtr = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName); //WH_KEYBOARD_LL=13 //全域性鉤子 WH_KEYBOARD_LL // hKeyboardHook = SetWindowsHookEx(13, hookproc, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0); hKeyboardHook = SetWindowsHookEx(13, hookproc, modulePtr, 0); } return (hKeyboardHook != 0); } public bool Stop() { if (hKeyboardHook != 0) { bool retKeyboard = UnhookWindowsHookEx(hKeyboardHook); hKeyboardHook = 0; return retKeyboard; } return true; } private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam) { EventMsg msg = (EventMsg)Marshal.PtrToStructure(lParam, typeof(EventMsg)); codes.Add(msg); if (ScanerEvent != null && msg.message == 13 && msg.paramH > 0 && !string.IsNullOrEmpty(codes.Result)) { ScanerEvent(codes); } return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam); } public class ScanerCodes { private int ts = 300; // 指定輸入間隔為300毫秒以內時為連續輸入 private List<List<EventMsg>> _keys = new List<List<EventMsg>>(); private List<int> _keydown = new List<int>(); // 儲存組合鍵狀態 private List<string> _result = new List<string>(); // 返回結果集 private DateTime _last = DateTime.Now; private byte[] _state = new byte[256]; private string _key = string.Empty; private string _cur = string.Empty; public EventMsg Event { get { if (_keys.Count == 0) { return new EventMsg(); } else { return _keys[_keys.Count - 1][_keys[_keys.Count - 1].Count - 1]; } } } public List<int> KeyDowns { get { return _keydown; } } public DateTime LastInput { get { return _last; } } public byte[] KeyboardState { get { return _state; } } public int KeyDownCount { get { return _keydown.Count; } } public string Result { get { if (_result.Count > 0) { return _result[_result.Count - 1].Trim(); } else { return null; } } } public string CurrentKey { get { return _key; } } public string CurrentChar { get { return _cur; } } public bool isShift { get { return _keydown.Contains(160); } } public void Add(EventMsg msg) { #region 記錄按鍵資訊 // 首次按下按鍵 if (_keys.Count == 0) { _keys.Add(new List<EventMsg>()); _keys[0].Add(msg); _result.Add(string.Empty); } // 未釋放其他按鍵時按下按鍵 else if (_keydown.Count > 0) { _keys[_keys.Count - 1].Add(msg); } // 單位時間內按下按鍵 else if (((TimeSpan)(DateTime.Now - _last)).TotalMilliseconds < ts) { _keys[_keys.Count - 1].Add(msg); } // 從新記錄輸入內容 else { _keys.Add(new List<EventMsg>()); _keys[_keys.Count - 1].Add(msg); _result.Add(string.Empty); } #endregion _last = DateTime.Now; #region 獲取鍵盤狀態 // 記錄正在按下的按鍵 if (msg.paramH == 0 && !_keydown.Contains(msg.message)) { _keydown.Add(msg.message); } // 清除已鬆開的按鍵 if (msg.paramH > 0 && _keydown.Contains(msg.message)) { _keydown.Remove(msg.message); } #endregion #region 計算按鍵資訊 int v = msg.message & 0xff; int c = msg.paramL & 0xff; StringBuilder strKeyName = new StringBuilder(500); if (GetKeyNameText(c * 65536, strKeyName, 255) > 0) { _key = strKeyName.ToString().Trim(new char[] { ' ', '\0' }); GetKeyboardState(_state); if (_key.Length == 1 && msg.paramH == 0)// && msg.paramH == 0 { // 根據鍵盤狀態和shift快取判斷輸出字元 _cur = ShiftChar(_key, isShift, _state).ToString(); _result[_result.Count - 1] += _cur; } // 備選 else { _cur = string.Empty; } } #endregion } private char ShiftChar(string k, bool isShiftDown, byte[] state) { bool capslock = state[0x14] == 1; bool numlock = state[0x90] == 1; bool scrolllock = state[0x91] == 1; bool shiftdown = state[0xa0] == 1; char chr = (capslock ? k.ToUpper() : k.ToLower()).ToCharArray()[0]; if (isShiftDown) { if (chr >= 'a' && chr <= 'z') { chr = (char)((int)chr - 32); } else if (chr >= 'A' && chr <= 'Z') { if (chr == 'Z') { string s = ""; } chr = (char)((int)chr + 32); } else { string s = "`1234567890-=[];',./"; string u = "~!@#$%^&*()_+{}:\"<>?"; if (s.IndexOf(chr) >= 0) { return (u.ToCharArray())[s.IndexOf(chr)]; } } } return chr; } } public struct EventMsg { public int message; public int paramL; public int paramH; public int Time; public int hwnd; } } }
上面是獲取掃碼槍掃描資料的具體程式碼,掃描過快的話會導致條碼粘連,不過因為條碼長度都一樣,所以可以獲取資料後再進行加工處理。下面是獲取資料後並處理的過程
using System; using System.Windows.Forms; using System.Runtime.InteropServices; using System.Collections; using System.Collections.Generic; namespace WindowsFormsApplication6 { public partial class Form1 : Form { private ScanerHook listener = new ScanerHook(); KeyboardHook k_hook; public Form1() { InitializeComponent(); listener.ScanerEvent += Listener_ScanerEvent; k_hook = new KeyboardHook(); k_hook.KeyDownEvent += K_hook_KeyDownEvent; k_hook.Start(); } HashSet<string> set = new HashSet<string>(); int count = 0; private void Listener_ScanerEvent(ScanerHook.ScanerCodes codes) { count++; DataGridViewRow row = new DataGridViewRow(); int index = dataGridView1.Rows.Add(row); int i = dataGridView1.Rows.Count - 1; dataGridView1.CurrentCell = dataGridView1[0, i]; dataGridView1.Rows[i].Selected = true; dataGridView1.FirstDisplayedCell = dataGridView1.Rows[i].Cells[0]; dataGridView1.Rows[index].Cells[0].Value= codes.Result.ToUpper(); // System.Console.WriteLine(codes.Result.ToUpper()); if (codes.Result.ToUpper().Length % 14 == 0) { if (!set.Contains(codes.Result.ToUpper())) { int h = 14; int k = codes.Result.ToUpper().Length; string n = codes.Result.ToUpper(); if (k >= 14) { int m = k / h; for (int x = 0; x < m; x++) { int t = x * h; string b = n.Substring(t, h); set.Add(b); } } } } label1.Text ="有效數量:"+ set.Count.ToString(); label2.Text = "縂數量:" +count; } private void Form1_Load(object sender, EventArgs e) { listener.Start(); } private void K_hook_KeyDownEvent(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.F4) { SendMsg sendMsg = new SendMsg(); foreach(Object s in set) { sendMsg.SendText(s+"\r"); } // sendMsg.SendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss\r")); if(MessageBox.Show("do you?","Confirm Message", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK) { set.Clear(); dataGridView1.Rows.Clear(); label1.Text = "有效數量:0"; label2.Text = "縂數量:0"; count = 0; } } } private void button1_Click(object sender, EventArgs e) { set.Clear(); dataGridView1.Rows.Clear(); label1.Text = "有效數量:0"; label2.Text = "縂數量:0"; count = 0; } } internal class SendMsg { [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId); [DllImport("user32.dll")] static extern bool GetGUIThreadInfo(uint idThread, ref GUITHREADINFO lpgui); [StructLayout(LayoutKind.Sequential)] public struct GUITHREADINFO { public int cbSize; public int flags; public IntPtr hwndActive; public IntPtr hwndFocus; public IntPtr hwndCapture; public IntPtr hwndMenuOwner; public IntPtr hwndMoveSize; public IntPtr hwndCaret; public RECT rectCaret; } [StructLayout(LayoutKind.Sequential)] public struct RECT { int left; int top; int right; int bottom; } public GUITHREADINFO? GetGuiThreadInfo(IntPtr hwnd) { if (hwnd != IntPtr.Zero) { uint threadId = GetWindowThreadProcessId(hwnd, IntPtr.Zero); GUITHREADINFO guiThreadInfo = new GUITHREADINFO(); guiThreadInfo.cbSize = Marshal.SizeOf(guiThreadInfo); if (GetGUIThreadInfo(threadId, ref guiThreadInfo) == false) return null; return guiThreadInfo; } return null; } public void SendText(string text) { IntPtr hwnd = GetForegroundWindow(); if (String.IsNullOrEmpty(text)) return; GUITHREADINFO? guiInfo = GetGuiThreadInfo(hwnd); if (guiInfo != null) { for (int i = 0; i < text.Length; i++) { SendMessage(guiInfo.Value.hwndFocus, 0x0102, (IntPtr)(int)text[i], IntPtr.Zero); } } } } internal class KeyboardHook { public event KeyEventHandler KeyDownEvent; public event KeyPressEventHandler KeyPressEvent; public event KeyEventHandler KeyUpEvent; public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam); static int hKeyboardHook = 0; //宣告鍵盤鉤子處理的初始值 //值在Microsoft SDK的Winuser.h裡查詢 public const int WH_KEYBOARD_LL = 13; //執行緒鍵盤鉤子監聽滑鼠訊息設為2,全域性鍵盤監聽滑鼠訊息設為13 HookProc KeyboardHookProcedure; //宣告KeyboardHookProcedure作為HookProc型別 //鍵盤結構 [StructLayout(LayoutKind.Sequential)] public class KeyboardHookStruct { public int vkCode; //定一個虛擬鍵碼。該程式碼必須有一個價值的範圍1至254 public int scanCode; // 指定的硬體掃描碼的關鍵 public int flags; // 鍵標誌 public int time; // 指定的時間戳記的這個訊息 public int dwExtraInfo; // 指定額外資訊相關的資訊 } //使用此功能,安裝了一個鉤子 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId); //呼叫此函式解除安裝鉤子 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] public static extern bool UnhookWindowsHookEx(int idHook); //使用此功能,通過資訊鉤子繼續下一個鉤子 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam); // 取得當前執行緒編號(執行緒鉤子需要用到) [DllImport("kernel32.dll")] static extern int GetCurrentThreadId(); //使用WINDOWS API函式代替獲取當前例項的函式,防止鉤子失效 [DllImport("kernel32.dll")] public static extern IntPtr GetModuleHandle(string name); public void Start() { // 安裝鍵盤鉤子 if (hKeyboardHook == 0) { KeyboardHookProcedure = new HookProc(KeyboardHookProc); hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0); //hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0); //************************************ //鍵盤執行緒鉤子 SetWindowsHookEx(13, KeyboardHookProcedure, IntPtr.Zero, GetCurrentThreadId());//指定要監聽的執行緒idGetCurrentThreadId(), //鍵盤全域性鉤子,需要引用空間(using System.Reflection;) //SetWindowsHookEx( 13,MouseHookProcedure,Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),0); // //關於SetWindowsHookEx (int idHook, HookProc lpfn, IntPtr hInstance, int threadId)函式將鉤子加入到鉤子連結串列中,說明一下四個引數: //idHook 鉤子型別,即確定鉤子監聽何種訊息,上面的程式碼中設為2,即監聽鍵盤訊息並且是執行緒鉤子,如果是全域性鉤子監聽鍵盤訊息應設為13, //執行緒鉤子監聽滑鼠訊息設為7,全域性鉤子監聽滑鼠訊息設為14。lpfn 鉤子子程的地址指標。如果dwThreadId引數為0 或是一個由別的程序建立的 //執行緒的標識,lpfn必須指向DLL中的鉤子子程。 除此以外,lpfn可以指向當前程序的一段鉤子子程程式碼。鉤子函式的入口地址,當鉤子鉤到任何 //訊息後便呼叫這個函式。hInstance應用程式例項的控制代碼。標識包含lpfn所指的子程的DLL。如果threadId 標識當前程序建立的一個執行緒,而且子 //程程式碼位於當前程序,hInstance必須為NULL。可以很簡單的設定其為本應用程式的例項控制代碼。threaded 與安裝的鉤子子程相關聯的執行緒的識別符號 //如果為0,鉤子子程與所有的執行緒關聯,即為全域性鉤子 //************************************ //如果SetWindowsHookEx失敗 if (hKeyboardHook == 0) { // Stop(); throw new Exception("安裝鍵盤鉤子失敗"); } } } public void Stop() { bool retKeyboard = true; if (hKeyboardHook != 0) { retKeyboard = UnhookWindowsHookEx(hKeyboardHook); hKeyboardHook = 0; } try { if (!(retKeyboard)) { // throw new Exception("解除安裝鉤子失敗!"); } } catch (Exception) { throw; } // if (!(retKeyboard)) throw new Exception("解除安裝鉤子失敗!"); } //ToAscii職能的轉換指定的虛擬鍵碼和鍵盤狀態的相應字元或字元 [DllImport("user32")] public static extern int ToAscii(int uVirtKey, //[in] 指定虛擬關鍵程式碼進行翻譯。 int uScanCode, // [in] 指定的硬體掃描碼的關鍵須翻譯成英文。高階位的這個值設定的關鍵,如果是(不壓) byte[] lpbKeyState, // [in] 指標,以256位元組陣列,包含當前鍵盤的狀態。每個元素(位元組)的陣列包含狀態的一個關鍵。如果高階位的位元組是一套,關鍵是下跌(按下)。在低位元,如果設定表明,關鍵是對切換。在此功能,只有肘位的CAPS LOCK鍵是相關的。在切換狀態的NUM個鎖和滾動鎖定鍵被忽略。 byte[] lpwTransKey, // [out] 指標的緩衝區收到翻譯字元或字元。 int fuState); // [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise. //獲取按鍵的狀態 [DllImport("user32")] public static extern int GetKeyboardState(byte[] pbKeyState); [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] private static extern short GetKeyState(int vKey); private const int WM_KEYDOWN = 0x100;//KEYDOWN private const int WM_KEYUP = 0x101;//KEYUP private const int WM_SYSKEYDOWN = 0x104;//SYSKEYDOWN private const int WM_SYSKEYUP = 0x105;//SYSKEYUP private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam) { // 偵聽鍵盤事件 if ((nCode >= 0) && (KeyDownEvent != null || KeyUpEvent != null || KeyPressEvent != null)) { KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); // raise KeyDown if (KeyDownEvent != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) { Keys keyData = (Keys)MyKeyboardHookStruct.vkCode; KeyEventArgs e = new KeyEventArgs(keyData); KeyDownEvent(this, e); } //鍵盤按下 if (KeyPressEvent != null && wParam == WM_KEYDOWN) { byte[] keyState = new byte[256]; GetKeyboardState(keyState); byte[] inBuffer = new byte[2]; if (ToAscii(MyKeyboardHookStruct.vkCode, MyKeyboardHookStruct.scanCode, keyState, inBuffer, MyKeyboardHookStruct.flags) == 1) { KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]); KeyPressEvent(this, e); } } // 鍵盤抬起 if (KeyUpEvent != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP)) { Keys keyData = (Keys)MyKeyboardHookStruct.vkCode; KeyEventArgs e = new KeyEventArgs(keyData); KeyUpEvent(this, e); } } //如果返回1,則結束訊息,這個訊息到此為止,不再傳遞。 //如果返回0或呼叫CallNextHookEx函式則訊息出了這個鉤子繼續往下傳遞,也就是傳給訊息真正的接受者 return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam); } ~KeyboardHook() { Stop(); } } } ![介面很low但不影響使用哈哈](https://img-blog.csdnimg.cn/a5450e53c6654225a29118385f4cf7b4.png#pic_center)
部分程式碼引用源文連線:[源文連結,如有侵權請聯絡刪除(https://blog.csdn.net/baidu_19356259/article/details/121998523)