Microsoft 开发人员网络 > 论坛主页 > Visual Basic IDE > CallbackOnCollectedDelegate
提出问题提出问题
 

已答复CallbackOnCollectedDelegate

  • 2006年12月7日 11:57jte 用户奖牌用户奖牌用户奖牌用户奖牌用户奖牌
     
    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


答案

全部回复

  • 2006年12月7日 15:53MSFT Johan Stenberg版主用户奖牌用户奖牌用户奖牌用户奖牌用户奖牌
     已答复

    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

     

     

  • 2006年12月9日 19:31jte 用户奖牌用户奖牌用户奖牌用户奖牌用户奖牌
     
    Thanks Johan,

    It works now.

    Best regards,
    Jurgen
  • 2008年9月22日 9:40nvvijaianand 用户奖牌用户奖牌用户奖牌用户奖牌用户奖牌
     
    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
  • 2008年12月23日 13:09Anup Debnath 用户奖牌用户奖牌用户奖牌用户奖牌用户奖牌
     
    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
     
  • 2009年10月29日 21:04Marc Wimmer 用户奖牌用户奖牌用户奖牌用户奖牌用户奖牌
     
    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

  • 2009年10月29日 21:08Marc Wimmer 用户奖牌用户奖牌用户奖牌用户奖牌用户奖牌
     
    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

        }
    }