CallbackOnCollectedDelegate
- Hi everybody,
I use the following code in my project, but after a while i get the error:
CallbackOnCollectedDelegate was detected
Message: A callback was made on a garbage collected delegate of type 'WindowsApplication6!WindowsApplication6.hookKeyBoard+LowLevelKeyboardProcDelegate::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.
This is some code:
Private Function LowLevelKeyboardProc( _
ByVal nCode As Integer, _
ByVal wParam As Integer, _
ByVal lParam As KBDLLHOOKSTRUCT) As Integer
If (nCode = HC_ACTION) Then
If wParam = WM_KEYDOWN Or _
wParam = WM_SYSKEYDOWN Or _
wParam = WM_KEYUP Or _
wParam = WM_SYSKEYUP Then
Return 1
End If
Return CallNextHookEx(hhkLowLevelKybd, _
nCode, wParam, lParam)
End If
End Function
Private Delegate Function LowLevelKeyboardProcDelegate( _
ByVal nCode As Integer, _
ByVal wParam As Integer, _
ByVal lParam As KBDLLHOOKSTRUCT) As Integer
Public Sub EnableKeyboard()
UnhookWindowsHookEx(hhkLowLevelKybd)
End Sub
Public Sub HookKeyboard()
Dim callback = New LowLevelKeyboardProcDelegate(AddressOf LowLevelKeyboardProc)
KeyboardHandle = SetWindowsHookEx(WH_KEYBOARD_LL, _
AddressOf LowLevelKeyboardProc, Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly.GetModules()(0)).ToInt32, 0)
GC.KeepAlive(callback)
End Sub
Doe someone knows what is going wrong?
Thanks,
Jurgen
Answers
See bullet # 4 in http://msdn2.microsoft.com/en-us/library/843s5s5x.aspx. A common technique is to use a static (shared) variable for the callback method instead of a local variable to avoid the delegate from being collected... Additional info on the MDA rule can be found at http://msdn2.microsoft.com/en-us/library/43yky316.aspx.
Best regards,
Johan Stenberg
All Replies
See bullet # 4 in http://msdn2.microsoft.com/en-us/library/843s5s5x.aspx. A common technique is to use a static (shared) variable for the callback method instead of a local variable to avoid the delegate from being collected... Additional info on the MDA rule can be found at http://msdn2.microsoft.com/en-us/library/43yky316.aspx.
Best regards,
Johan Stenberg- Thanks Johan,
It works now.
Best regards,
Jurgen - Hello JTE,
we have implemented the same code and receiving the same error... could u please share the details about how you solved the issue.
regards
...vijai
Vijai - Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Runtime.InteropServices
Imports System.Windows.Forms
Namespace Utilities
''' <summary>
''' A class that manages a global low level keyboard hook
''' </summary>
Public Class globalKeyboardHook
#Region "Constant, Structure and Delegate Definitions"
''' <summary>
''' defines the callback type for the hook
''' </summary>
Public Delegate Function keyboardHookProc(ByVal code As Integer, ByVal wParam As Integer, ByRef lParam As keyboardHookStruct) As Integer
Public Structure keyboardHookStruct
Public vkCode As Integer
Public scanCode As Integer
Public flags As Integer
Public time As Integer
Public dwExtraInfo As Integer
End Structure
Const WH_KEYBOARD_LL As Integer = 13
Const WM_KEYDOWN As Integer = &H100
Const WM_KEYUP As Integer = &H101
Const WM_SYSKEYDOWN As Integer = &H104
Const WM_SYSKEYUP As Integer = &H105
#End Region
#Region "Instance Variables"
''' <summary>
''' The collections of keys to watch for
''' </summary>
Public HookedKeys As New List(Of Keys)()
''' <summary>
''' Handle to the hook, need this to unhook and call the next hook
''' </summary>
Private hhook As IntPtr = IntPtr.Zero
#End Region
#Region "Events"
''' <summary>
''' Occurs when one of the hooked keys is pressed
''' </summary>
Public Event KeyDown As KeyEventHandler
''' <summary>
''' Occurs when one of the hooked keys is released
''' </summary>
Public Event KeyUp As KeyEventHandler
#End Region
#Region "Constructors and Destructors"
''' <summary>
''' Initializes a new instance of the <see cref="globalKeyboardHook"/> class and installs the keyboard hook.
''' </summary>
Public Sub New()
hook()
End Sub
Protected Overrides Sub Finalize()
Try
unhook()
Finally
MyBase.Finalize()
End Try
End Sub
#End Region
#Region "Public Methods"
''' <summary>
''' Installs the global hook
''' </summary>
Public Sub hook()
Try
Dim hInstance As IntPtr = LoadLibrary("User32")
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, AddressOf hookProc, hInstance, 0)
Catch ex As Exception
End Try
End Sub
''' <summary>
''' Uninstalls the global hook
''' </summary>
Public Sub unhook()
Try
UnhookWindowsHookEx(hhook)
Catch ex As Exception
End Try
End Sub
''' <summary>
''' The callback for the keyboard hook
''' </summary>
''' <param name="code">The hook code, if it isn't >= 0, the function shouldn't do anyting</param>
''' <param name="wParam">The event type</param>
''' <param name="lParam">The keyhook event information</param>
''' <returns></returns>
Public Function hookProc(ByVal code As Integer, ByVal wParam As Integer, ByRef lParam As keyboardHookStruct) As Integer
Try
If code >= 0 Then
Dim key As Keys = DirectCast(lParam.vkCode, Keys)
If HookedKeys.Contains(key) Then
Dim kea As New KeyEventArgs(key)
If (wParam = WM_KEYDOWN Or wParam = WM_SYSKEYDOWN) Then
RaiseEvent KeyDown(Me, kea)
ElseIf (wParam = WM_KEYUP Or wParam = WM_SYSKEYUP) Then
RaiseEvent KeyUp(Me, kea)
End If
If kea.Handled Then
Return 1
End If
End If
End If
Return CallNextHookEx(hhook, code, wParam, lParam)
Catch ex As Exception
End Try
End Function
#End Region
'#Region "DLL imports"
''' <summary>
''' Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null
''' </summary>
''' <param name="idHook">The id of the event you want to hook</param>
''' <param name="callback">The callback.</param>
''' <param name="hInstance">The handle you want to attach the event to, can be null</param>
''' <param name="threadId">The thread you want to attach the event to, can be null</param>
''' <returns>a handle to the desired hook</returns>
<DllImport("user32.dll")> _
Private Shared Function SetWindowsHookEx(ByVal idHook As Integer, ByVal callback As keyboardHookProc, ByVal hInstance As IntPtr, ByVal threadId As UInteger) As IntPtr
End Function
''' <summary>
''' Unhooks the windows hook.
''' </summary>
''' <param name="hInstance">The hook handle that was returned from SetWindowsHookEx</param>
''' <returns>True if successful, false otherwise</returns>
<DllImport("user32.dll")> _
Private Shared Function UnhookWindowsHookEx(ByVal hInstance As IntPtr) As Boolean
End Function
''' <summary>
''' Calls the next hook.
''' </summary>
''' <param name="idHook">The hook id</param>
''' <param name="nCode">The hook code</param>
''' <param name="wParam">The wparam.</param>
''' <param name="lParam">The lparam.</param>
''' <returns></returns>
<DllImport("user32.dll")> _
Private Shared Function CallNextHookEx(ByVal idHook As IntPtr, ByVal nCode As Integer, ByVal wParam As Integer, ByRef lParam As keyboardHookStruct) As Integer
End Function
''' <summary>
''' Loads the library.
''' </summary>
''' <param name="lpFileName">Name of the library</param>
''' <returns>A handle to the library</returns>
<DllImport("kernel32.dll")> _
Private Shared Function LoadLibrary(ByVal lpFileName As String) As IntPtr
End Function
End Class
' #End Region
End Namespace
i got the error in this code
error is
CallbackOnCollectedDelegate was detected
Message: A callback was made on a garbage collected delegate of type 'Cyber_Client!Cyber_Client.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.
plz anybody help me
- I stuck for 3 hours on this problem...now I solved it:
I programmed in C#, so what you have to do is NOT to directly pass the hookProc to SetWindowsHookEx. It seems so, that .net creates somehow
a new delegation variable, which is garbage collected afterwards...
So do following: (C#)
public static keyboardHookProc SAFE_delegate_callback = new keyboardHookProc(hookProc);
public void hook()
{
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, SAFE_delegate_callback, hInstance, 0);
}
This is INSTEAD OF:
{
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
}
So what did I:
1. Create a public static variable of type delegate (this variable is wether the function hookProc nor the delegate definition).
2. Pass the static variable to SetWindowsHookEx
This worked fine for me.
Greetings
Marc
- Here my complete class of the Cool KeyHooker, I just inserted a static Instance:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Utilities
{
public struct keyboardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam);
/// <summary>
/// A class that manages a global low level keyboard hook
/// </summary>
public class GlobalKeyListener
{
static GlobalKeyListener _instance;
static GlobalKeyListener()
{
_instance = new GlobalKeyListener();
}
public static GlobalKeyListener Instance
{
get
{
return _instance;
}
}
#region Constant, Structure and Delegate Definitions
/// <summary>
/// defines the callback type for the hook
/// </summary>
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
/// <summary>
/// The collections of keys to watch for
/// </summary>
public List<Keys> HookedKeys = new List<Keys>();
/// <summary>
/// Handle to the hook, need this to unhook and call the next hook
/// </summary>
IntPtr hhook = IntPtr.Zero;
#endregion
#region Events
/// <summary>
/// Occurs when one of the hooked keys is pressed
/// </summary>
public event KeyEventHandler KeyDown;
/// <summary>
/// Occurs when one of the hooked keys is released
/// </summary>
public event KeyEventHandler KeyUp;
#endregion
#region Constructors and Destructors
/// <summary>
/// Initializes a new instance of the <see cref="globalKeyboardHook"/> class and installs the keyboard hook.
/// </summary>
public GlobalKeyListener()
{
hook();
}
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="globalKeyboardHook"/> is reclaimed by garbage collection and uninstalls the keyboard hook.
/// </summary>
~GlobalKeyListener()
{
Console.WriteLine("GlobalKeyListener collected");
unhook();
}
#endregion
#region Public Methods
/// <summary>
/// Installs the global hook
/// </summary>
///Muss in einer eigenen Variablen (statisch) gespeichert werden --- anscheinend erzeugt
///.NET beim Aufruf von SetWindowsHookEx immer eine neue Variable des Typs keyboardHookProc
public static keyboardHookProc SAFE_delegate_callback = new keyboardHookProc(hookProc);
public void hook()
{
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, SAFE_delegate_callback, hInstance, 0);
}
/// <summary>
/// Uninstalls the global hook
/// </summary>
public void unhook()
{
UnhookWindowsHookEx(hhook);
}
/// <summary>
/// The callback for the keyboard hook
/// </summary>
/// <param name="code">The hook code, if it isn't >= 0, the function shouldn't do anyting</param>
/// <param name="wParam">The event type</param>
/// <param name="lParam">The keyhook event information</param>
/// <returns></returns>
public static int hookProc(int code, int wParam, ref keyboardHookStruct lParam)
{
Console.WriteLine(code);
if (code >= 0)
{
Keys key = (Keys)lParam.vkCode;
if (Instance.HookedKeys != null)
{
if (Instance.HookedKeys.Contains(key))
{
KeyEventArgs kea = new KeyEventArgs(key);
if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (Instance.KeyDown != null))
{
Instance.KeyDown(Instance, kea);
}
else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (Instance.KeyUp != null))
{
Instance.KeyUp(Instance, kea);
}
if (kea.Handled)
return 1;
}
}
}
return CallNextHookEx(Instance.hhook, code, wParam, ref lParam);
}
#endregion
#region DLL imports
/// <summary>
/// Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null
/// </summary>
/// <param name="idHook">The id of the event you want to hook</param>
/// <param name="callback">The callback.</param>
/// <param name="hInstance">The handle you want to attach the event to, can be null</param>
/// <param name="threadId">The thread you want to attach the event to, can be null</param>
/// <returns>a handle to the desired hook</returns>
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);
/// <summary>
/// Unhooks the windows hook.
/// </summary>
/// <param name="hInstance">The hook handle that was returned from SetWindowsHookEx</param>
/// <returns>True if successful, false otherwise</returns>
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
/// <summary>
/// Calls the next hook.
/// </summary>
/// <param name="idHook">The hook id</param>
/// <param name="nCode">The hook code</param>
/// <param name="wParam">The wparam.</param>
/// <param name="lParam">The lparam.</param>
/// <returns></returns>
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam);
/// <summary>
/// Loads the library.
/// </summary>
/// <param name="lpFileName">Name of the library</param>
/// <returns>A handle to the library</returns>
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
#endregion
}
}


