Error : CallbackOnCollectedDelegate - how to find the exact collected item?
Error : CallbackOnCollectedDelegate - how to find the exact collected item?
成功几分钟后 运行 我在我的应用程序中收到此错误。
A callback was made on a garbage collected delegate of type
'myApp!myApp.globalKeyboardHook+keyboardHookProc::Invoke'.
我知道这是因为垃圾收集器已经杀死了一个对象或其他东西,而我的代码仍然引用同样的东西。
如果是这种情况,哪个对象或组件更有可能被 GC 收集。我怎样才能克服这个错误。 (有参考??)
由于我无法弄清楚是哪部分代码导致了这个问题,所以我将完整的 class 发布在这里。 (我想我的其他 classes 没有问题)
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);
}
}
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
您肯定很难找到收集的项目,它在您的程序中不可见。 C#语言在这里有点太友好了。您依靠它的语法糖自动为 hookProc
创建委托对象。此代码编译为:
keyboardHookProc $unspeakable = new keyboardHookProc(hookProc);
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, $unspeakable, hInstance, 0);
这是 pinvoke 中的一个问题,垃圾收集器不知道本机代码实际上正在使用 $unspeakable 委托对象。它只能看到对对象的托管引用。因此,它会在第 0 代垃圾收集发生时立即收集对象。 Kaboom 当 Windows 进行钩子回调时。
由您来确保无法收集此委托对象。使用 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;
}
成功几分钟后 运行 我在我的应用程序中收到此错误。
A callback was made on a garbage collected delegate of type 'myApp!myApp.globalKeyboardHook+keyboardHookProc::Invoke'.
我知道这是因为垃圾收集器已经杀死了一个对象或其他东西,而我的代码仍然引用同样的东西。
如果是这种情况,哪个对象或组件更有可能被 GC 收集。我怎样才能克服这个错误。 (有参考??)
由于我无法弄清楚是哪部分代码导致了这个问题,所以我将完整的 class 发布在这里。 (我想我的其他 classes 没有问题)
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);
}
}
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
您肯定很难找到收集的项目,它在您的程序中不可见。 C#语言在这里有点太友好了。您依靠它的语法糖自动为 hookProc
创建委托对象。此代码编译为:
keyboardHookProc $unspeakable = new keyboardHookProc(hookProc);
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, $unspeakable, hInstance, 0);
这是 pinvoke 中的一个问题,垃圾收集器不知道本机代码实际上正在使用 $unspeakable 委托对象。它只能看到对对象的托管引用。因此,它会在第 0 代垃圾收集发生时立即收集对象。 Kaboom 当 Windows 进行钩子回调时。
由您来确保无法收集此委托对象。使用 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;
}