2013-04-30 78 views
3

我使用此代碼GlobalKeyboardHook.cs鉤住鍵盤。「CallbackOnCollectedDelegate was detected」while trying to hook keyboard

我已經按照this post中的說明更改了所有指定的內容。現在,我的代碼如下所示:

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; 

     private static keyboardHookProc callbackDelegate; 

     #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"); 
      callbackDelegate = new keyboardHookProc(hookProc); 
      hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0); 
      if (hhook == IntPtr.Zero) throw new System.ComponentModel.Win32Exception(); 
     } 

     /// <summary> 
     /// Uninstalls the global hook 
     /// </summary> 
     public void unhook() 
     { 
      bool ok = UnhookWindowsHookEx(hhook); 
      if (!ok) throw new System.ComponentModel.Win32Exception(); 
      callbackDelegate = null; 
     } 

     /// <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 
    } 
} 

此功能在我的程序掛接& DOWN鍵時,我的程序窗口是可見的。它在可見性變化時解除這些鍵。

private void AutoCompleteMenu_VisibleChanged(object sender, EventArgs e) 
     { 
      if (this.Visible == true) 
      { 
       if (hookFlag == 0) 
       { 
        try 
        { 
         gkh.hook(); 
         gkh.HookedKeys.Add(Keys.Up); 
         gkh.HookedKeys.Add(Keys.Down); 
         gkh.KeyDown += new KeyEventHandler(gkh_KeyDown); 
         gkh.KeyUp += new KeyEventHandler(gkh_KeyUp); 
        } 
        catch (Exception) 
        { 
         MessageBox.Show("Cannot hook arrow keys! Please use mouse keys to select a word"); 
        } 
        hookFlag = 1; 
       } 
      } 
      else 
      { 
       if (hookFlag == 1) 
       { 
        hookFlag = 0; 
        gkh.unhook(); 
       } 
      } 
     } 

這個代碼寫在程序初始化部分:

檢測CallbackOnCollectedDelegate消息:globalKeyboardHook gkh = new globalKeyboardHook();

不過,每當我的程序試圖鉤鍵我得到這個錯誤的回調是由 'CaretPosition!Utilities.globalKeyboardHook + keyboardHookProc :: Invoke'的垃圾收集代表製作 。 這可能會導致應用程序崩潰,損壞和數據丟失。當 將代理傳遞給非託管代碼時,它們必須由管理的應用程序保持活動狀態,直到它確保它們永遠不會被調用 。

請幫我解決這個問題。

+0

脫鉤是否有任何機會,你Utilities.globalKeyboardHook類實例和應用程序生命週期,因此keyboardHookProc代表自毀? – Tommi 2013-04-30 08:31:28

+0

您想閱讀http://msdn.microsoft.com/en-us/library/system.gc.keepalive.aspx有關C#垃圾回收器的KeepAlive方法的msdn文章 - 也許它可以幫助您解決問題。 – Dietz 2013-04-30 09:02:43

回答

1

可能發生垃圾收集根本不會發生,並且當您關閉應用程序時,它會導致無效代理掛鉤(因爲應用程序已卸載)。

您不應該使用終結器〜globalKeyboardHook釋放分配的內存以外的系統資源,而是使用IDisposable而不是顯式解除綁定。或者你可以在

AppDomain.CurrentDomain.ProcessExit 

事件

+0

或者可能發生在第一代或第二代的globalKeyboardHook實例和GC僅收集了0代 – 2013-04-30 09:09:02

+0

我已經刪除了終結器(〜globalKeyboardHook)。我還在ProcessExit事件中添加了unhook()。仍存在同樣的問題。我現在應該怎麼做。? – Dreamist 2013-04-30 12:22:52