已经在C#中对垃圾收集委托进行了调用?

我一直在使用这个我找到的密钥钩子脚本,但是在我的程序中使用它几秒后我仍然会收到错误。 错误说..已经对垃圾收集的委托’keylogger进行了调用!Utilities.globalKeyboardHook + keyboardHookProc :: Invoke’。

我怎样才能解决这个问题?

namespace Utilities { ///  /// A class that manages a global low level keyboard hook ///  class globalKeyboardHook { #region Constant, Structure and Delegate Definitions ///  /// defines the callback type for the hook ///  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 ///  /// The collections of keys to watch for ///  public List HookedKeys = new List(); ///  /// Handle to the hook, need this to unhook and call the next hook ///  IntPtr hhook = IntPtr.Zero; #endregion #region Events ///  /// Occurs when one of the hooked keys is pressed ///  public event KeyEventHandler KeyDown; ///  /// Occurs when one of the hooked keys is released ///  public event KeyEventHandler KeyUp; #endregion #region Constructors and Destructors ///  /// Initializes a new instance of the  class and installs the keyboard hook. ///  public globalKeyboardHook() { hook(); } ///  /// Releases unmanaged resources and performs other cleanup operations before the ///  is reclaimed by garbage collection and uninstalls the keyboard hook. ///  ~globalKeyboardHook() { unhook(); } #endregion #region Public Methods ///  /// Installs the global hook ///  public void hook() { IntPtr hInstance = LoadLibrary("User32"); hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0); } ///  /// Uninstalls the global hook ///  public void unhook() { UnhookWindowsHookEx(hhook); } ///  /// The callback for the keyboard hook ///  /// The hook code, if it isn't >= 0, the function shouldn't do anyting /// The event type /// The keyhook event information ///  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 ///  /// Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null ///  /// The id of the event you want to hook /// The callback. /// The handle you want to attach the event to, can be null /// The thread you want to attach the event to, can be null /// a handle to the desired hook [DllImport("user32.dll")] static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId); ///  /// Unhooks the windows hook. ///  /// The hook handle that was returned from SetWindowsHookEx /// True if successful, false otherwise [DllImport("user32.dll")] static extern bool UnhookWindowsHookEx(IntPtr hInstance); ///  /// Calls the next hook. ///  /// The hook id /// The hook code /// The wparam. /// The lparam. ///  [DllImport("user32.dll")] static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam); ///  /// Loads the library. ///  /// Name of the library /// A handle to the library [DllImport("kernel32.dll")] static extern IntPtr LoadLibrary(string lpFileName); #endregion } } 

globalKeyboardHook类:

 using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.Windows.Forms; using System.IO; namespace Utilities { ///  /// A class that manages a global low level keyboard hook ///  class globalKeyboardHook : IDisposable { private bool _disposed; #region Constant, Structure and Delegate Definitions ///  /// defines the callback type for the hook ///  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 ///  /// The collections of keys to watch for ///  public List HookedKeys = new List(); ///  /// Handle to the hook, need this to unhook and call the next hook ///  IntPtr hhook = IntPtr.Zero; #endregion #region Events ///  /// Occurs when one of the hooked keys is pressed ///  public event KeyEventHandler KeyDown; ///  /// Occurs when one of the hooked keys is released ///  public event KeyEventHandler KeyUp; #endregion #region Constructors and Destructors ///  /// Initializes a new instance of the  class and installs the keyboard hook. ///  public globalKeyboardHook() { hook(); _disposed = false; } public void Dispose() { Dispose(true); // Use SupressFinalize in case a subclass // of this type implements a finalizer. GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { // If you need thread safety, use a lock around these // operations, as well as in your methods that use the resource. if (!_disposed) { if (disposing) { unhook(); } // Indicate that the instance has been disposed. _disposed = true; } } ///  /// Releases unmanaged resources and performs other cleanup operations before the ///  is reclaimed by garbage collection and uninstalls the keyboard hook. ///  ~globalKeyboardHook() { Dispose(); } #endregion #region Public Methods ///  /// Installs the global hook ///  public void hook() { IntPtr hInstance = LoadLibrary("User32"); hhook = SetWindowsHookEx(WH_KEYBOARD_LL, new keyboardHookProc(hookProc), hInstance, 0); } ///  /// Uninstalls the global hook ///  public void unhook() { UnhookWindowsHookEx(hhook); } ///  /// The callback for the keyboard hook ///  /// The hook code, if it isn't >= 0, the function shouldn't do anyting /// The event type /// The keyhook event information ///  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 ///  /// Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null ///  /// The id of the event you want to hook /// The callback. /// The handle you want to attach the event to, can be null /// The thread you want to attach the event to, can be null /// a handle to the desired hook [DllImport("user32.dll")] static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId); ///  /// Unhooks the windows hook. ///  /// The hook handle that was returned from SetWindowsHookEx /// True if successful, false otherwise [DllImport("user32.dll")] static extern bool UnhookWindowsHookEx(IntPtr hInstance); ///  /// Calls the next hook. ///  /// The hook id /// The hook code /// The wparam. /// The lparam. ///  [DllImport("user32.dll")] static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam); ///  /// Loads the library. ///  /// Name of the library /// A handle to the library [DllImport("kernel32.dll")] static extern IntPtr LoadLibrary(string lpFileName); #endregion } } 

我用IDisposable更新了代码。 对于我应该做的事情,我可能会非常失望,但它仍然无法正常工作

问题是:

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

只是语法糖:

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

所以keyboardHookProc对象只是本地的,并且会被处理掉,因为SetWindowsHookEx在托管环境中没有做任何实际操作。

要解决此问题,请在您定义成员变量的顶部添加一个,如下所示:

 IntPtr hhook = IntPtr.Zero private keyboardHookProc hookProcDelegate; 

然后将您的构造函数更改为:

 public globalKeyboardHook() { hookProcDelegate = hookProc; hook(); } 

然后将hook()方法更改为:

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

这样您就可以使用存储为成员变量的委托,只要您的globalKeyboardHook对象处于活动状态,它就会处于活动状态。

听起来像是在实例化一个globalKeyboardHook然后让它被垃圾收集。 我猜你做的是这样的:

 public void InstallHook() { var hook = new globalKeyboardHook(); } 

您需要保持对globalKeyboardHook()的引用,以防止它被垃圾回收。

 globalKeyboardHook hook; public void InstallHook() { hook = new globalKeyboardHook(); } 

即使使用新代码我仍然得到上述错误,作为一个解决方案我只是在类范围保留了委托的实例,现在错误不再出现了。

 //do not forget to declare kbhproc class var this.kbhProc = new keyboardHookProc(hookProc); hhook = SetWindowsHookEx(WH_KEYBOARD_LL, this.kbhProc /*new keyboardHookProc(hookProc)*/, hInstance, 0); 

上面的代码基于问题的代码。

我想添加这个,以备将来参考,因为它可能有助于理解Tim的答案,也许可以调试正在发生的事情,如果你有复杂的代码:

callbackOnCollectedDelegate MDA

https://docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/callbackoncollecteddelegate-mda