2012-03-31 42 views
10

Tôi đang sử dụng lớp móc khóa bàn phím chung. Lớp này cho phép kiểm tra xem phím bàn phím có được nhấn ở bất kỳ đâu không. Và sau một thời gian tôi đang gặp một lỗi:CallbackOnCollectedDelegate trong globalKeyboardHook đã được phát hiện

 **CallbackOnCollectedDelegate was detected** 

A callback was made on a garbage collected delegate of type 'Browser!Utilities.globalKeyboardHook+keyboardHookProc::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called. 

Dưới đây là globalkeyboardHook lớp:

 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; 

     public List<Keys> HookedKeys = new List<Keys>(); 

     IntPtr hhook = IntPtr.Zero; 

     public event KeyEventHandler KeyDown; 
     public event KeyEventHandler KeyUp; 

     public globalKeyboardHook() 
     { 
      hook(); 
     } 

     ~globalKeyboardHook() 
     { 
      unhook(); 
     } 

     public void hook() 
     { 
      IntPtr 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 (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); 
     } 

     [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); 
     #endregion 

Bất kỳ ý tưởng làm thế nào để sửa chữa nó? Chương trình hoạt động tốt, nhưng sau một thời gian chương trình đóng băng kiến ​​tôi nhận được lỗi này.

+0

Hãy thử giữ tham chiếu đến đại biểu trong lớp học của bạn để hookProc - một thành viên thực sự. Tôi không chắc chắn nếu điều đó sẽ giải quyết tất cả mọi thứ, nhưng nó sẽ giải quyết vấn đề bộ sưu tập của bạn miễn là lớp móc của bạn vẫn còn sống. –

Trả lời

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

Có vấn đề của bạn. Bạn đang dựa vào đường cú pháp C# để nó tự động tạo đối tượng đại biểu cho hookProc. Tạo mã thực tế trông như thế này:

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

Chỉ có một tham chiếu đến đối tượng đại biểu, $ temp. Nhưng nó là biến cục bộ và biến mất ngay khi phương thức hook() của bạn ngừng thực hiện và trả về. Các bộ thu gom rác là không có sức mạnh để thấy rằng Windows có một 'tham chiếu' với nó là tốt, nó không thể thăm dò mã không được quản lý để tham khảo. Vì vậy, lần sau khi bộ thu gom rác chạy, đối tượng ủy nhiệm sẽ bị hủy. Và đó là một kaboom khi Windows gọi lại hook. MDA tích hợp phát hiện sự cố và tạo chẩn đoán hữu ích trước khi chương trình gặp sự cố với AccessViolation.

Bạn sẽ cần phải tạo tham chiếu bổ sung cho đối tượng ủy nhiệm tồn tại đủ lâu. Bạn có thể sử dụng GCHandle chẳng hạn. Hoặc dễ dàng hơn, chỉ cần lưu trữ một tài liệu tham khảo để người thu gom rác luôn có thể xem tham chiếu. Thêm một trường vào lớp của bạn. Làm cho nó tĩnh là một cách chắc chắn để đảm bảo rằng các đối tượng không thể được thu thập:

private static keyboardHookProc callbackDelegate; 

    public void hook() 
    { 
     if (callbackDelegate != null) throw new InvalidOperationException("Can't hook more than once"); 
     IntPtr hInstance = LoadLibrary("User32"); 
     callbackDelegate = new keyboardHookProc(hookProc); 
     hhook = SetWindowsHookEx(WH_KEYBOARD_LL, callbackDelegate, hInstance, 0); 
     if (hhook == IntPtr.Zero) throw new Win32Exception(); 
    } 

    public void unhook() 
    { 
     if (callbackDelegate == null) return; 
     bool ok = UnhookWindowsHookEx(hhook); 
     if (!ok) throw new Win32Exception(); 
     callbackDelegate = null; 
    } 

Không cần phải pinvoke FreeLibrary, user32.dll luôn được tải cho đến khi chương trình của bạn chấm dứt.

+0

Cảm ơn sự giúp đỡ của bạn. Bây giờ nó hoạt động hoàn hảo :) Vấn đề được giải quyết. –

+0

nếu User32 luôn được tải, tại sao tôi tải nó một cách rõ ràng trong móc phương thức()? –

+0

Tôi không có một cỗ máy thời gian để biết nếu vẫn còn đúng 20 năm kể từ bây giờ. –

Các vấn đề liên quan