2015-09-21 74 views
2

當討論鍵盤鉤子,this article得到了很多。C#鍵盤鉤子拋出System.NullReferenceException

所以我綁它建議類:

using System; 
    using System.Collections.Generic; 
    using System.Text; 
    using System.Runtime.InteropServices; 
    using System.Windows.Forms; 

    namespace Utilities { 
    /// <summary> 
    /// A class that manages a global low level keyboard hook 
    /// </summary> 
    class globalKeyboardHook { 
    #region Constant, Structure and Delegate Definitions 
    /// <summary> 
    /// defines the callback type for the hook 
    /// </summary> 
    public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam); 

    public struct keyboardHookStruct { 
     public int vkCode; 
     public int scanCode; 
     public int flags; 
     public int time; 
     public int dwExtraInfo; 
    } 

    const int WH_KEYBOARD_LL = 13; 
    const int WM_KEYDOWN = 0x100; 
    const int WM_KEYUP = 0x101; 
    const int WM_SYSKEYDOWN = 0x104; 
    const int WM_SYSKEYUP = 0x105; 
    #endregion 

    #region Instance Variables 
    /// <summary> 
    /// The collections of keys to watch for 
    /// </summary> 
    public List<Keys> HookedKeys = new List<Keys>(); 
    /// <summary> 
    /// Handle to the hook, need this to unhook and call the next hook 
    /// </summary> 
    IntPtr hhook = IntPtr.Zero; 
    #endregion 

    #region Events 
    /// <summary> 
    /// Occurs when one of the hooked keys is pressed 
    /// </summary> 
    public event KeyEventHandler KeyDown; 
    /// <summary> 
    /// Occurs when one of the hooked keys is released 
    /// </summary> 
    public event KeyEventHandler KeyUp; 
    #endregion 

    #region Constructors and Destructors 
    /// <summary> 
    /// Initializes a new instance of the <see cref="globalKeyboardHook"/> class and installs the keyboard hook. 
    /// </summary> 
    public globalKeyboardHook() { 
     hook(); 
    } 

    /// <summary> 
    /// Releases unmanaged resources and performs other cleanup operations before the 
    /// <see cref="globalKeyboardHook"/> is reclaimed by garbage collection and uninstalls the keyboard hook. 
    /// </summary> 
    ~globalKeyboardHook() { 
     unhook(); 
    } 
    #endregion 

    #region Public Methods 
    /// <summary> 
    /// Installs the global hook 
    /// </summary> 
    public void hook() { 
     IntPtr hInstance = LoadLibrary("User32"); 
     hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0); 
    } 

    /// <summary> 
    /// Uninstalls the global hook 
    /// </summary> 
    public void unhook() { 
     UnhookWindowsHookEx(hhook); 
    } 

    /// <summary> 
    /// The callback for the keyboard hook 
    /// </summary> 
    /// <param name="code">The hook code, if it isn't >= 0, the function shouldn't do anyting</param> 
    /// <param name="wParam">The event type</param> 
    /// <param name="lParam">The keyhook event information</param> 
    /// <returns></returns> 
    public int hookProc(int code, int wParam, ref keyboardHookStruct lParam) { 
     if (code >= 0) { 
      Keys key = (Keys)lParam.vkCode; 
      if (HookedKeys.Contains(key)) { 
       KeyEventArgs kea = new KeyEventArgs(key); 
       if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null)) { 
        KeyDown(this, kea) ; 
       } else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null)) { 
        KeyUp(this, kea); 
       } 
       if (kea.Handled) 
        return 1; 
      } 
     } 
     return CallNextHookEx(hhook, code, wParam, ref lParam); 
    } 
    #endregion 

    #region DLL imports 
    /// <summary> 
    /// Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null 
    /// </summary> 
    /// <param name="idHook">The id of the event you want to hook</param> 
    /// <param name="callback">The callback.</param> 
    /// <param name="hInstance">The handle you want to attach the event to, can be null</param> 
    /// <param name="threadId">The thread you want to attach the event to, can be null</param> 
    /// <returns>a handle to the desired hook</returns> 
    [DllImport("user32.dll")] 
    static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId); 

    /// <summary> 
    /// Unhooks the windows hook. 
    /// </summary> 
    /// <param name="hInstance">The hook handle that was returned from SetWindowsHookEx</param> 
    /// <returns>True if successful, false otherwise</returns> 
    [DllImport("user32.dll")] 
    static extern bool UnhookWindowsHookEx(IntPtr hInstance); 

    /// <summary> 
    /// Calls the next hook. 
    /// </summary> 
    /// <param name="idHook">The hook id</param> 
    /// <param name="nCode">The hook code</param> 
    /// <param name="wParam">The wparam.</param> 
    /// <param name="lParam">The lparam.</param> 
    /// <returns></returns> 
    [DllImport("user32.dll")] 
    static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam); 

    /// <summary> 
    /// Loads the library. 
    /// </summary> 
    /// <param name="lpFileName">Name of the library</param> 
    /// <returns>A handle to the library</returns> 
    [DllImport("kernel32.dll")] 
    static extern IntPtr LoadLibrary(string lpFileName); 
    #endregion 
} 
} 

它適用於一點,但使用電腦一段時間後,它會拋出一個隨機按鍵一個System.NullReferenceException。特別是圍繞組合鍵。

什麼可能導致這種情況,以及如何解決?

編輯:這是我使用啓動鉤代碼:

globalKeyboardHook globalKeyboardHook = new globalKeyboardHook(); 
private void Form1_Load(object sender, EventArgs e) 
{ 
    globalKeyboardHook.KeyDown += gkh_KeyDown; 
    globalKeyboardHook.KeyUp += gkh_KeyUp; 
    globalKeyboardHook.hook(); 
} 

這是完全錯誤我得到:

類型的未處理的異常「系統.NullReferenceException' 發生在System.Windows.Forms.dll 附加信息:未將對象引用設置爲對象的實例。

+2

你能告訴我們您正在使用調用這個代碼,並確切的錯誤你得到些什麼? – Nikerym

+1

我記得,很多問題是因爲GC,程序必須用KeepAlive保護 – Spawn

+0

我沒有看到你用Ctrl,Alt和Shift鍵工作的地方。 其他變體 - 異步程序,您可以在(event!= null)檢查之後但在事件委託啓動之前解除鍵的鎖定。 – Oxoron

回答

1

嘗試使用字段來存儲回調以避免被GC收集委託:

private static keyboardHookProc callback; 

public void hook() 
{ 
    callback = new keyboardHookProc(hookProc); 
    var hhook = SetWindowsHookEx(1, callback, IntPtr.Zero, 0); 
} 
+0

用這個替換鉤子代碼似乎可以防止鉤住任何鍵。 – Andy