2015-04-06 153 views
3

我使用條形碼掃描器成功地在我的WPF項目中使用鍵盤鉤子進行管理,如下所示(我省略了一些細節,但基本上,我可以依靠事實知道哪個鍵盤是我的掃描儀) 。Winforms按鍵和條形碼掃描器

/// <summary> 
/// Add this KeyboardHook to a window 
/// </summary> 
/// <param name="window">The window to add to</param> 
public void AddHook(Window window) { 
    if (form == null) 
    throw new ArgumentNullException("window"); 
    if (mHwndSource != null) 
    throw new InvalidOperationException("Hook already present"); 

    WindowInteropHelper w = new WindowInteropHelper(window); 
    IntPtr hwnd = w.Handle; 
    mHwndSource = HwndSource.FromHwnd(hwnd); 
    if (mHwndSource == null) 
    throw new ApplicationException("Failed to receive window source"); 

    mHwndSource.AddHook(WndProc); 

    RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[1]; 

    rid[0].usUsagePage = 0x01; 
    rid[0].usUsage = 0x06; 
    rid[0].dwFlags = RIDEV_INPUTSINK; 
    rid[0].hwndTarget = hwnd; 

    if (!RegisterRawInputDevices(rid, (uint)rid.Length, (uint)Marshal.SizeOf(rid[0]))) 
    throw new ApplicationException("Failed to register raw input device(s)."); 
} 

的方法,然後處理WM_INPUT信息檢索有關出現並相應地處理該事件,如果它是從已經知道的條形碼掃描器的任何鍵盤事件的信息。

現在的事情是,在的WinForms我不應該使用掛鉤,但覆蓋的WndProc作爲陳述here,但我不知怎麼努力理解我如何使用的WndProc,因爲我需要知道:

一)什麼事件我真的需要在WndProc方法

b)來處理我怎麼能確定解僱任何幫助將是非常讚賞的事件

設備! 乾杯!

+2

我用[這段代碼]的修改版(http://www.codeproject.com/Articles/17123/Using-Raw-Input-從C到手柄多鍵盤)取得了巨大的成功。它不是微不足道的,但它是唯一能夠隔離VID/PID的方法之一,並且可以將Winforms的輸入連接到足夠低的級別以防止妨礙。 – helrich 2015-04-06 11:51:56

+0

哼哼......我其實已經在使用一些非常相似的東西了。更令人討厭的是對非終端服務器環境的限制......這是因爲原始輸入還是另一個原因? – neggenbe 2015-04-06 16:45:41

+0

可能。我沒有使用終端服務器的代碼,但可以看到如何可能導致問題。恐怕我可能無法在這方面進一步幫助你。 – helrich 2015-04-06 17:41:18

回答

3

我使用以下方法結束:

public class BarcodeScannedEventArgs : EventArgs { 

    public BarcodeScannedEventArgs(string text) { 
     mText = text; 
    } 
    public string ScannedText { get { return mText; } } 

    private readonly string mText; 
    } 

    public class BarCodeListener : IDisposable { 
    DateTime _lastKeystroke = new DateTime(0); 
    string _barcode = string.Empty; 
    Form _form; 
    bool isKeyPreview; 

    public bool ProcessCmdKey(ref Message msg, Keys keyData) { 
     bool res = processKey(keyData); 
     return keyData == Keys.Enter ? res : false; 
    } 

    protected bool processKey(Keys key) { 
     // check timing (>7 keystrokes within 50 ms ending with "return" char) 
     TimeSpan elapsed = (DateTime.Now - _lastKeystroke); 
     if (elapsed.TotalMilliseconds > 50) { 
     _barcode = string.Empty; 
     } 

     // record keystroke & timestamp -- do NOT add return at the end of the barcode line 
     if (key != Keys.Enter) { 
     _barcode += (char)key; 
     } 
     _lastKeystroke = DateTime.Now; 

     // process barcode only if the return char is entered and the entered barcode is at least 7 digits long. 
     // This is a "magical" rule working well for EAN-13 and EAN-8, which both have at least 8 digits... 
     if (key == Keys.Enter && _barcode.Length > 7) { 
     if (BarCodeScanned != null) { 
      BarCodeScanned(_form, new BarcodeScannedEventArgs(_barcode)); 
     } 
     _barcode = string.Empty; 
     return true; 
     } 
     return false; 
    } 

    public event EventHandler<BarcodeScannedEventArgs> BarCodeScanned; 

    public BarCodeListener(Form f) { 
     _form = f; 
     isKeyPreview = f.KeyPreview; 
     // --- set preview and register event... 
     f.KeyPreview = true; 
    } 

    public void Dispose() { 
     if (_form != null) { 
     _form.KeyPreview = isKeyPreview; 
     //_form.KeyPress -= KeyPress_scanner_preview; 
     } 
    } 
    } 
} 

然後,添加代碼到您的窗體以下行這是聽您的掃描儀:

private BarCodeListener ScannerListener; 
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { 
    bool res = false; 
    if (ScannerListener != null) { 
    res = ScannerListener.ProcessCmdKey(ref msg, keyData); 
    } 
    res = keyData == Keys.Enter ? res : base.ProcessCmdKey(ref msg, keyData); 
    return res; 
} 
+0

但我仍然認爲不如原來的WPF代碼,你知道在原來的代碼,它只會聽最後一個輸入設備。但是在這段代碼中,如果你從'_barcode.Length> 7'刪除限制,那麼每當你按下Enter鍵時,它就會執行。所以我們不能遙控鍵盤觸發嗎?像原始代碼? – qakmak 2016-03-07 11:54:25

+0

然後,我會發布我以前的工作代碼。所有學分歸入以下清單:http://www.codeproject.com/Articles/17123/Using-Raw-Input-from-C-to-handle-multiple-keyboard – neggenbe 2016-03-07 15:13:58

0

我張貼在這裏的一段代碼在使用上述版本之前,我使用了一段時間。它來自here,我沒有重新測試。當屬是,希望它有助於反正...

public BarCodeListener(IntPtr handle) { 
    SetDeviceHook(handle); 
} 

public void SetDeviceHook(IntPtr handle) { 
    if (string.IsNullOrWhiteSpace(LibConst.ScannerDeviceName)) { 
    return; 
    } 
    var hook = new KeyboardHook(); 
    var availableScanners = KeyboardHook.GetKeyboardDevices(); 
    foreach (string s in availableScanners) { 
    // you need to figure out the device name and put it here!!!! 
    if (s.Contains("VID_0C2E&PID_0200")) { 
     hook.SetDeviceFilter(s); 
     hook.KeyPressed += OnBarcodeKeyWpf; 
     hook.AddHook(handle); 
    } 
    } 
} 

string InputText; 
void OnBarcodeKey(object sender, KeyPressEventArgs e) { 
    if (this.isInitialisationChar(e.KeyChar.ToString())) { 
    InputText = String.Empty; 
    } 
    else if (this.isTerminationChar(e.KeyChar.ToString())) { 
    if ((BarCodeScanned != null) && (!string.IsNullOrEmpty(InputText))) { 
     BarCodeScanned(this, new BarcodeScannedEventArgs(InputText)); 
     InputText = String.Empty; 
    } 
    } 
    else { 
    InputText += e.KeyChar.ToString(); 
    } 
} 

void OnBarcodeKeyWpf(object sender, KeyPressedEventArgs e) { 
    if (this.isInitialisationChar(e.Text)){ 
    InputText = String.Empty; 
    } 
    else if (this.isTerminationChar(e.Text)){ 
    if ((BarCodeScanned != null) && (!string.IsNullOrEmpty(InputText))) { 
     BarCodeScanned(this, new BarcodeScannedEventArgs(InputText)); 
     InputText = String.Empty; 
    } 
    } 
    else{ 
    InputText += e.Text; 
    } 
} 

bool isInitialisationChar(string s) { 
    return string.IsNullOrEmpty(s); 
} 
bool isTerminationChar(string s) { 
    return ((s == "\r") || string.IsNullOrEmpty(s)); 
} 
} 

public class KeyPressedEventArgs : EventArgs 
{ 

public KeyPressedEventArgs(string text) { 
    mText = text; 
} 
public string Text { get { return mText; } } 

private readonly string mText; 
} 

public partial class KeyboardHook 
: IDisposable 
{ 
private static readonly Regex DeviceNamePattern = new Regex(@"#([^#]+)"); 
public event EventHandler<KeyPressedEventArgs> KeyPressed; 

/// <summary> 
/// Set the device to use in keyboard hook 
/// </summary> 
/// <param name="deviceId">Name of device</param> 
/// <returns>true if device is found</returns> 
public bool SetDeviceFilter(string deviceId) { 
    Dictionary<string, IntPtr> devices = FindAllKeyboardDevices(); 
    return devices.TryGetValue(deviceId, out mHookDeviceId); 
} 

/// <summary> 
/// Add this KeyboardHook to a window 
/// </summary> 
/// <param name="window">The window to add to</param> 
public void AddHook(BaseForm form) { 
    if (form == null) 
    throw new ArgumentNullException("window"); 
    if (mHwndSource != null) 
    throw new InvalidOperationException("Hook already present"); 

    //--- NEG: wpf code: 
    //WindowInteropHelper w = new WindowInteropHelper(form); 
    //IntPtr hwnd = w.Handle; 
    //--- 
    IntPtr hwnd = form.Handle; 
    mHwndSource = HwndSource.FromHwnd(hwnd); 
    if (mHwndSource == null) 
    throw new ApplicationException("Failed to receive window source"); 

    mHwndSource.AddHook(WndProc); 

    RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[1]; 

    rid[0].usUsagePage = 0x01; 
    rid[0].usUsage = 0x06; 
    rid[0].dwFlags = RIDEV_INPUTSINK; 
    rid[0].hwndTarget = hwnd; 

    if (!RegisterRawInputDevices(rid, (uint)rid.Length, (uint)Marshal.SizeOf(rid[0]))) 
    throw new ApplicationException("Failed to register raw input device(s)."); 
} 

/// <summary> 
/// Add this KeyboardHook to a window 
/// </summary> 
/// <param name="window">The window to add to</param> 
public void AddHook(IntPtr handle) { 
    if (handle == null) 
    throw new ArgumentNullException("window"); 
    if (mHwndSource != null) 
    throw new InvalidOperationException("Hook already present"); 

    //--- NEG: wpf code: 
    //WindowInteropHelper w = new WindowInteropHelper(form); 
    //IntPtr hwnd = w.Handle; 
    //--- 
    IntPtr hwnd = handle; 
    mHwndSource = HwndSource.FromHwnd(hwnd); 
    if (mHwndSource == null) 
    throw new ApplicationException("Failed to receive window source"); 

    mHwndSource.AddHook(WndProc); 

    RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[1]; 

    rid[0].usUsagePage = 0x01; 
    rid[0].usUsage = 0x06; 
    rid[0].dwFlags = RIDEV_INPUTSINK; 
    rid[0].hwndTarget = hwnd; 

    if (!RegisterRawInputDevices(rid, (uint)rid.Length, (uint)Marshal.SizeOf(rid[0]))) 
    throw new ApplicationException("Failed to register raw input device(s)."); 
} 

/// <summary> 
/// Remove this keyboard hook from window (if it is added) 
/// </summary> 
public void RemoveHook() { 
    if (mHwndSource == null) 
    return; // not an error 

    RAWINPUTDEVICE[] rid = new RAWINPUTDEVICE[1]; 

    rid[0].usUsagePage = 0x01; 
    rid[0].usUsage = 0x06; 
    rid[0].dwFlags = 0x00000001; 
    rid[0].hwndTarget = IntPtr.Zero; 

    RegisterRawInputDevices(rid, (uint)rid.Length, (uint)Marshal.SizeOf(rid[0])); 
    mHwndSource.RemoveHook(WndProc); 
    mHwndSource.Dispose(); 
    mHwndSource = null; 
} 

public void Dispose() { 
    RemoveHook(); 
} 

private IntPtr mHookDeviceId; 
private HwndSource mHwndSource; 

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { 
    switch (msg) { 
    case WM_INPUT: 
     if (ProcessInputCommand(mHookDeviceId, lParam)) { 
     MSG message; 
     PeekMessage(out message, IntPtr.Zero, WM_KEYDOWN, WM_KEYDOWN, PM_REMOVE); 
     } 
     break; 
    } 
    return IntPtr.Zero; 
} 

/// <summary> 
/// Get a list of keyboard devices available 
/// </summary> 
/// <returns>Collection of devices available</returns> 
public static ICollection<string> GetKeyboardDevices() { 
    return FindAllKeyboardDevices().Keys; 
} 

private static Dictionary<string, IntPtr> FindAllKeyboardDevices() { 
    Dictionary<string, IntPtr> deviceNames = new Dictionary<string, IntPtr>(); 
    uint deviceCount = 0; 
    int dwSize = (Marshal.SizeOf(typeof(RAWINPUTDEVICELIST))); 

    if (GetRawInputDeviceList(IntPtr.Zero, ref deviceCount, (uint)dwSize) == 0) { 
    IntPtr pRawInputDeviceList = Marshal.AllocHGlobal((int)(dwSize*deviceCount)); 

    try { 
     GetRawInputDeviceList(pRawInputDeviceList, ref deviceCount, (uint)dwSize); 

     for (int i = 0; i < deviceCount; i++) { 
     uint pcbSize = 0; 

     var rid = (RAWINPUTDEVICELIST)Marshal.PtrToStructure(
             new IntPtr((pRawInputDeviceList.ToInt32() + (dwSize*i))), 
             typeof(RAWINPUTDEVICELIST)); 

     GetRawInputDeviceInfo(rid.hDevice, RIDI_DEVICENAME, IntPtr.Zero, ref pcbSize); 

     if (pcbSize > 0) { 
      IntPtr pData = Marshal.AllocHGlobal((int)pcbSize); 
      try { 
      GetRawInputDeviceInfo(rid.hDevice, RIDI_DEVICENAME, pData, ref pcbSize); 
      string deviceName = Marshal.PtrToStringAnsi(pData); 

      // The list will include the "root" keyboard and mouse devices 
      // which appear to be the remote access devices used by Terminal 
      // Services or the Remote Desktop - we're not interested in these 
      // so the following code with drop into the next loop iteration 
      if (deviceName.ToUpper().Contains("ROOT")) 
       continue; 

      // If the device is identified as a keyboard or HID device, 
      // Check if it is the one we're looking for 
      if (rid.dwType == RIM_TYPEKEYBOARD || rid.dwType == RIM_TYPEHID) { 
       Match match = DeviceNamePattern.Match(deviceName); 
       if (match.Success) 
       deviceNames.Add(match.Groups[1].Value, rid.hDevice); 
      } 
      } 
      finally { 
      Marshal.FreeHGlobal(pData); 
      } 
     } 
     } 
    } 
    finally { 
     Marshal.FreeHGlobal(pRawInputDeviceList); 
    } 
    } 
    return deviceNames; 
} 

/// <summary> 
/// Processes WM_INPUT messages to retrieve information about any 
/// keyboard events that occur. 
/// </summary> 
/// <param name="deviceId">Device to process</param> 
/// <param name="lParam">The WM_INPUT message to process.</param> 
private bool ProcessInputCommand(IntPtr deviceId, IntPtr lParam) { 
    uint dwSize = 0; 

    try { 
    // First call to GetRawInputData sets the value of dwSize 
    // dwSize can then be used to allocate the appropriate amount of memory, 
    // storing the pointer in "buffer". 
    GetRawInputData(lParam, RID_INPUT, IntPtr.Zero,ref dwSize, (uint)Marshal.SizeOf(typeof(RAWINPUTHEADER))); 

    IntPtr buffer = Marshal.AllocHGlobal((int)dwSize); 
    try { 
     // Check that buffer points to something, and if so, 
     // call GetRawInputData again to fill the allocated memory 
     // with information about the input 
     if (buffer != IntPtr.Zero && 
      GetRawInputData(lParam, RID_INPUT, buffer, ref dwSize, (uint)Marshal.SizeOf(typeof(RAWINPUTHEADER))) == dwSize) { 
     // Store the message information in "raw", then check 
     // that the input comes from a keyboard device before 
     // processing it to raise an appropriate KeyPressed event. 

     RAWINPUT raw = (RAWINPUT)Marshal.PtrToStructure(buffer, typeof(RAWINPUT)); 

     if (raw.header.hDevice != deviceId) 
      return false; 

     if (raw.header.dwType != RIM_TYPEKEYBOARD) 
      return false; 
     if (raw.keyboard.Message != WM_KEYDOWN && raw.keyboard.Message != WM_SYSKEYDOWN) 
      return false; 

     // On most keyboards, "extended" keys such as the arrow or page 
     // keys return two codes - the key's own code, and an "extended key" flag, which 
     // translates to 255. This flag isn't useful to us, so it can be 
     // disregarded. 
     if (raw.keyboard.VKey > VK_LAST_KEY) 
      return false; 

     if (KeyPressed != null) { 
      string scannedText = null; 
      lock (mLocalBuffer) { 
      if (GetKeyboardState(mKeyboardState)) { 
       if (ToUnicode(raw.keyboard.VKey, raw.keyboard.MakeCode, mKeyboardState, mLocalBuffer, 64, 0) > 0) { 
       if (mLocalBuffer.Length > 0) { 
        scannedText = mLocalBuffer.ToString(); 
       } 
       } 
      } 
      } 
      if (!string.IsNullOrEmpty(scannedText)) 
      KeyPressed(this, new KeyPressedEventArgs(scannedText)); 
     } 
     return true; 
     } 
    } 
    finally { 
     Marshal.FreeHGlobal(buffer); 
    } 
    } 
    catch (Exception e) { 
    throw new AppException(SCL_Languages.getValue("internalerror"), e.Message, e); 
    } 
    return false; 
} 
private static readonly StringBuilder mLocalBuffer = new StringBuilder(64); 
private static readonly byte[] mKeyboardState = new byte[256]; 
} 

public partial class KeyboardHook 
{ 
private const int RIDEV_INPUTSINK = 0x00000100; 
private const int RIDEV_REMOVE = 0x00000001; 
private const int RID_INPUT = 0x10000003; 

private const int FAPPCOMMAND_MASK = 0xF000; 
private const int FAPPCOMMAND_MOUSE = 0x8000; 
private const int FAPPCOMMAND_OEM = 0x1000; 

private const int RIM_TYPEMOUSE = 0; 
private const int RIM_TYPEKEYBOARD = 1; 
private const int RIM_TYPEHID = 2; 

private const int RIDI_DEVICENAME = 0x20000007; 

private const int WM_KEYDOWN = 0x0100; 
private const int WM_SYSKEYDOWN = 0x0104; 
private const int WM_INPUT = 0x00FF; 
private const int VK_OEM_CLEAR = 0xFE; 
private const int VK_LAST_KEY = VK_OEM_CLEAR; // this is a made up value used as a sentinal 

private const int PM_REMOVE = 0x01; 

[StructLayout(LayoutKind.Sequential)] 
private struct RAWINPUTDEVICELIST 
{ 
    public IntPtr hDevice; 

    [MarshalAs(UnmanagedType.U4)] 
    public int dwType; 
} 

[StructLayout(LayoutKind.Explicit)] 
private 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)] 
private 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.Sequential)] 
private struct RAWHID 
{ 
    [MarshalAs(UnmanagedType.U4)] 
    public int dwSizHid; 

    [MarshalAs(UnmanagedType.U4)] 
    public int dwCount; 
} 

[StructLayout(LayoutKind.Sequential)] 
private struct BUTTONSSTR 
{ 
    [MarshalAs(UnmanagedType.U2)] 
    public ushort usButtonFlags; 

    [MarshalAs(UnmanagedType.U2)] 
    public ushort usButtonData; 
} 

[StructLayout(LayoutKind.Explicit)] 
private 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)] 
private 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.Sequential)] 
private struct RAWINPUTDEVICE 
{ 
    [MarshalAs(UnmanagedType.U2)] 
    public ushort usUsagePage; 

    [MarshalAs(UnmanagedType.U2)] 
    public ushort usUsage; 

    [MarshalAs(UnmanagedType.U4)] 
    public int dwFlags; 

    public IntPtr hwndTarget; 
} 

[DllImport("User32.dll")] 
private static extern uint GetRawInputDeviceList(IntPtr pRawInputDeviceList, ref uint uiNumDevices, uint cbSize); 

[DllImport("User32.dll")] 
private static extern uint GetRawInputDeviceInfo(IntPtr hDevice, uint uiCommand, IntPtr pData, ref uint pcbSize); 

[DllImport("User32.dll")] 
private static extern bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevice, uint uiNumDevices, uint cbSize); 

[DllImport("User32.dll")] 
private static extern uint GetRawInputData(IntPtr hRawInput, uint uiCommand, IntPtr pData, ref uint pcbSize, uint cbSizeHeader); 

[DllImport("user32.dll", SetLastError = true)] 
[return: MarshalAs(UnmanagedType.Bool)] 
private static extern bool GetKeyboardState(byte[] lpKeyState); 
[DllImport("user32.dll")] 
private static extern int ToUnicode(uint wVirtKey, uint wScanCode, byte[] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)] StringBuilder pwszBuff, 
            int cchBuff, uint wFlags); 

[DllImport("user32.dll")] 
[return: MarshalAs(UnmanagedType.Bool)] 
internal static extern bool PeekMessage(out MSG lpmsg, IntPtr hwnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);