2015-07-11 39 views
0

成功運行幾分鐘後,我的應用程序中出現此錯誤。錯誤:CallbackOnCollectedDelegate - 如何找到確切收集的項目?

的回調是對 '對myApp!myApp.globalKeyboardHook + keyboardHookProc ::調用' 類型的垃圾收集委託製作。

我知道這是因爲垃圾收集器已經殺死了一個對象或其他東西,我的代碼仍然指的是同樣的東西。

如果是這種情況,那麼GC更有可能收集哪個對象或組件。我怎樣才能克服這個錯誤。 (有一個ref ??)

因爲我無法弄清楚代碼的哪個部分負責這個問題,所以我在這裏發佈完整的類。 (我想這與我的其他類沒有問題)

class globalKeyboardHook 
    { 
     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; 
     } 

     IntPtr hInstance; 
     const int WH_KEYBOARD_LL = 13; 
     const int WM_KEYDOWN = 0x100; 
     const int WM_SYSKEYDOWN = 0x104; 

     public List<Keys> HookedKeys = new List<Keys>(); 
     IntPtr hhook = IntPtr.Zero; 

     // Events 
     public event KeyEventHandler KeyDown; 

     public globalKeyboardHook() 
     { 
      hook(); 
     } 

     ~globalKeyboardHook() 
     { 
      unhook(); 
     } 

     public void hook() 
     { 
      hInstance = LoadLibrary("User32"); 
      hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0); 
     } 

     public void unhook() 
     { 
      UnhookWindowsHookEx(hhook); 
     } 

     public int hookProc(int code, int wParam, ref keyboardHookStruct lParam) 
     { 
      if (code >= 0) 
      { 
       Keys key = (Keys)lParam.vkCode; 
       if (1 == 1) 
       { 
        KeyEventArgs kea = new KeyEventArgs(key); 
        if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null)) 
        { 
         KeyDown(this, kea); 
        } 
        if (kea.Handled) 
         return 1; 
       } 
      } 
      return CallNextHookEx(hhook, code, wParam, ref lParam); 
     } 


     // DLL imports 

     [DllImport("user32.dll")] 
     static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId); 

     [DllImport("user32.dll")] 
     static extern bool UnhookWindowsHookEx(IntPtr hInstance); 

     [DllImport("user32.dll")] 
     static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam); 

     [DllImport("kernel32.dll")] 
     static extern IntPtr LoadLibrary(string lpFileName); 

    } 
} 

回答

3
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0); 

你一定很難發現收集的項目,它是不是在你的程序中可見。 C#語言在這裏有點太友善了。您依靠其語法糖來自動創建hookProc的代理對象。此代碼編譯爲:

keyboardHookProc $unspeakable = new keyboardHookProc(hookProc); 
    hhook = SetWindowsHookEx(WH_KEYBOARD_LL, $unspeakable, hInstance, 0); 

這是PInvoke的問題,垃圾收集器不知道的是,$難言的委託對象實際上正在使用本地代碼。它只能看到對象的託管引用。因此,只要發生第#0代垃圾回收,它就會收集對象。當Windows進行鉤子回調時,Kaboom。

確保無法收集此委託對象取決於您。使用GCHandle.Alloc()。或者簡單的方法,明確地將其存儲在變量中。這裏很好,因爲你讓終結者破壞鉤子:

IntPtr hhook = IntPtr.Zero; 
    keyboardHookProc callback; 

    public void hook() 
    { 
     if (callback != null) throw new InvalidOperationException("Hook already installed"); 
     if (hInstance == IntPtr.Zero) hInstance = LoadLibrary("User32"); 
     callback = new keyboardHookProc(hookProc); 
     hhook = SetWindowsHookEx(WH_KEYBOARD_LL, callback, hInstance, 0); 
    } 

    public void unhook() 
    { 
     UnhookWindowsHookEx(hhook); 
     callback = null; 
    } 
相關問題