已经在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