Results 1 to 5 of 5

Thread: CallbackOnCollectedDelegate in keyboard hook

  1. #1

    Thread Starter
    New Member
    Join Date
    Jun 2014
    Posts
    4

    Question CallbackOnCollectedDelegate in keyboard hook

    Hello,

    I'm trying to set a keyboar hook, but I'm getting a CallbackOnCollectedDelegate exception when the object (where an instance of the hook is declared), gets disposed . The exception occures on the line where CallNextHookEx is called.

    (it's an existing source, wich I translated from C# to VB.NET & modified)
    Code:
    #Region "File Description"
    '-----------------------------------------------------------------------------
    ' KeyboardUtils.cs
    '
    ' Microsoft XNA Community Game Platform
    ' Copyright (C) Microsoft Corporation. All rights reserved.
    '-----------------------------------------------------------------------------
    #End Region
    
    #Region "Using Statements"
    
    Imports System.Collections.Generic
    Imports System.Runtime.InteropServices
    Imports System.Windows.Forms
    
    #End Region
    
    
    
    ' This class exposes WinForms-style key events.
    Namespace Hooks
    
    
    
        ''' <summary>
        ''' A class to provide text input capabilities to an XNA application svia Win32 hooks.
        ''' </summary>
        Public Class TextInput
            Implements IDisposable
            Private m_buffer As String = ""
            Private m_backSpace As Boolean = False
    
            Public ReadOnly Property Buffer() As String
                Get
                    Return m_buffer
                End Get
            End Property
    
            Public ReadOnly Property BackSpace() As Boolean
                Get
                    Dim b As Boolean = m_backSpace
                    m_backSpace = False
                    Return b
                End Get
            End Property
    
            Public Sub clearBuffer()
                m_buffer = ""
            End Sub
    
    #Region "Win32"
    
            ''' <summary>
            ''' Types of hook that can be installed using the SetWindwsHookEx function.
            ''' </summary>
            Public Enum HookId
                WH_CALLWNDPROC = 4
                WH_CALLWNDPROCRET = 12
                WH_CBT = 5
                WH_DEBUG = 9
                WH_FOREGROUNDIDLE = 11
                WH_GETMESSAGE = 3
                WH_HARDWARE = 8
                WH_JOURNALPLAYBACK = 1
                WH_JOURNALRECORD = 0
                WH_KEYBOARD = 2
                WH_KEYBOARD_LL = 13
                WH_MAX = 11
                WH_MAXHOOK = WH_MAX
                WH_MIN = -1
                WH_MINHOOK = WH_MIN
                WH_MOUSE_LL = 14
                WH_MSGFILTER = -1
                WH_SHELL = 10
                WH_SYSMSGFILTER = 6
            End Enum
    
            ''' <summary>
            ''' Window message types.
            ''' </summary>
            ''' <remarks>Heavily abridged, naturally.</remarks>
            Public Enum WindowMessage
                WM_KEYDOWN = &H100
                WM_KEYUP = &H101
                WM_CHAR = &H102
            End Enum
    
            ''' <summary>
            ''' A delegate used to create a hook callback.
            ''' </summary>
            Public Delegate Function GetMsgProc(nCode As Integer, wParam As Integer, ByRef msg As Message) As Integer
    
            ''' <summary>
            ''' Install an application-defined hook procedure into a hook chain.
            ''' </summary>
            ''' <param name="idHook">Specifies the type of hook procedure to be installed.</param>
            ''' <param name="lpfn">Pointer to the hook procedure.</param>
            ''' <param name="hmod">Handle to the DLL containing the hook procedure pointed to by the lpfn parameter.</param>
            ''' <param name="dwThreadId">Specifies the identifier of the thread with which the hook procedure is to be associated.</param>
            ''' <returns>If the function succeeds, the return value is the handle to the hook procedure. Otherwise returns 0.</returns>
            <DllImport("user32.dll", EntryPoint:="SetWindowsHookExA")> _
            Public Shared Function SetWindowsHookEx(idHook As HookId, lpfn As GetMsgProc, hmod As IntPtr, dwThreadId As Integer) As IntPtr
            End Function
    
            ''' <summary>
            ''' Removes a hook procedure installed in a hook chain by the SetWindowsHookEx function. 
            ''' </summary>
            ''' <param name="hHook">Handle to the hook to be removed. This parameter is a hook handle obtained by a previous call to SetWindowsHookEx.</param>
            ''' <returns>If the function fails, the return value is zero. To get extended error information, call GetLastError.</returns>
            <DllImport("user32.dll")> _
            Public Shared Function UnhookWindowsHookEx(hHook As IntPtr) As Integer
            End Function
    
            ''' <summary>
            ''' Passes the hook information to the next hook procedure in the current hook chain.
            ''' </summary>
            ''' <param name="hHook">Ignored.</param>
            ''' <param name="ncode">Specifies the hook code passed to the current hook procedure.</param>
            ''' <param name="wParam">Specifies the wParam value passed to the current hook procedure.</param>
            ''' <param name="lParam">Specifies the lParam value passed to the current hook procedure.</param>
            ''' <returns>This value is returned by the next hook procedure in the chain.</returns>
            <DllImport("user32.dll")> _
            Public Shared Function CallNextHookEx(hHook As Integer, ncode As Integer, wParam As Integer, ByRef lParam As Message) As Integer
            End Function
    
            ''' <summary>
            ''' Translates virtual-key messages into character messages.
            ''' </summary>
            ''' <param name="lpMsg">Pointer to an Message structure that contains message information retrieved from the calling thread's message queue.</param>
            ''' <returns>If the message is translated (that is, a character message is posted to the thread's message queue), the return value is true.</returns>
            <DllImport("user32.dll")> _
            Public Shared Function TranslateMessage(ByRef lpMsg As Message) As Boolean
            End Function
    
    
            ''' <summary>
            ''' Retrieves the thread identifier of the calling thread.
            ''' </summary>
            ''' <returns>The thread identifier of the calling thread.</returns>
            <DllImport("kernel32.dll")> _
            Public Shared Function GetCurrentThreadId() As Integer
            End Function
    
    #End Region
    
    #Region "Hook management and class construction."
    
            ''' <summary>Handle for the created hook.</summary>
            Private ReadOnly HookHandle As IntPtr
    
            '  Private ReadOnly  ProcessMessagesCallback As GetMsgProc
            Private ReadOnly ProcessMessagesCallback As GetMsgProc
    
            ''' <summary>Create an instance of the TextInputHandler.</summary>
            ''' <param name="whnd">Handle of the window you wish to receive messages (and thus keyboard input) from.</param>
            Public Sub New(whnd As IntPtr)
                ' Create the delegate callback:
                Me.ProcessMessagesCallback = New GetMsgProc(AddressOf ProcessMessages)
                ' Create the keyboard hook:
                Me.HookHandle = SetWindowsHookEx(HookId.WH_GETMESSAGE, Me.ProcessMessagesCallback, IntPtr.Zero, GetCurrentThreadId())
            End Sub
    
            Public Sub Dispose() Implements IDisposable.Dispose
                ' Remove the hook.
                If Me.HookHandle <> IntPtr.Zero Then
                    UnhookWindowsHookEx(Me.HookHandle)
                End If
    
            End Sub
    
    #End Region
    
    #Region "Message processing"
    
            Private Function ProcessMessages(nCode As Integer, wParam As Integer, ByRef msg As Message) As Integer
    
                ' Check if we must process this message (and whether it has been retrieved via GetMessage):
                If nCode = 0 AndAlso wParam = 1 Then
    
                    ' We need character input, so use TranslateMessage to generate WM_CHAR messages.
                    TranslateMessage(msg)
    
                    ' If it's one of the keyboard-related messages, raise an event for it:
                    Select Case CType(msg.Msg, WindowMessage)
                        Case WindowMessage.WM_CHAR
                            Me.OnKeyPress(New KeyPressEventArgs(Chr(msg.WParam.ToInt32)))
                            Exit Select
                        Case WindowMessage.WM_KEYDOWN
                            Me.OnKeyDown(New KeyEventArgs(CType(msg.WParam, Keys)))
                            Exit Select
                        Case WindowMessage.WM_KEYUP
                            Me.OnKeyUp(New KeyEventArgs(CType(msg.WParam, Keys)))
                            Exit Select
    
                    End Select
                End If
    
                Return CallNextHookEx(0, nCode, wParam, msg)
              
            End Function
    
    #End Region
    
    #Region "Events"
    
            Public Event KeyUp As KeyEventHandler
            Protected Overridable Sub OnKeyUp(e As KeyEventArgs)
                RaiseEvent KeyUp(Me, e)
            End Sub
    
            Public Event KeyDown As KeyEventHandler
            Protected Overridable Sub OnKeyDown(e As KeyEventArgs)
                RaiseEvent KeyDown(Me, e)
            End Sub
    
            Public Event KeyPress As KeyPressEventHandler
            Protected Overridable Sub OnKeyPress(e As KeyPressEventArgs)
                RaiseEvent KeyPress(Me, e)
                If e.KeyChar.GetHashCode().ToString() = "524296" Then
                    m_backSpace = True
                Else
                    m_buffer += e.KeyChar
                End If
            End Sub
    
    #End Region
        End Class
    
    
    
    End Namespace

    What am I doing wrong/how would I fix this?

  2. #2
    Frenzied Member
    Join Date
    May 2014
    Location
    Central Europe
    Posts
    1,388

    Re: CallbackOnCollectedDelegate in keyboard hook

    i cant replicate your error using the code you provided. maybe you have set up two hooks on the same window?

    this is how i tested it:
    Code:
    Public Class Form1
    
        Private WithEvents o As Hooks.TextInput
    
        Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
            o = New Hooks.TextInput(Me.Handle)
        End Sub
    
    
        Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
            o.Dispose()
        End Sub
    
        Private Sub o_KeyDown(sender As Object, e As System.Windows.Forms.KeyEventArgs) Handles o.KeyDown
            Debug.Print(DateTime.Now)
        End Sub
    End Class

  3. #3

    Thread Starter
    New Member
    Join Date
    Jun 2014
    Posts
    4

    Re: CallbackOnCollectedDelegate in keyboard hook

    Don't you get an error when you dispose Form1?
    Even when I disposed the instance of the hook, I still get the error when disposing Form1.

    I'll check if I accedently set up 2 hooks.

  4. #4
    Frenzied Member
    Join Date
    May 2014
    Location
    Central Europe
    Posts
    1,388

    Re: CallbackOnCollectedDelegate in keyboard hook

    no error here. i just created a new project, added your code to a new class, added two buttons to the form and my code in the form and ran it. once button1 is clicked i do get debug output with each keystroke, once button2 is clicked no more output as expected, can close the form all without any error popping up.

    the error message you report may mean that CallNextHookEx attemted to jump to a memory location that was already collected by the GC. thats why i came up with the idea that you maybe have multiple hooks on the same window.

  5. #5

    Thread Starter
    New Member
    Join Date
    Jun 2014
    Posts
    4

    Re: CallbackOnCollectedDelegate in keyboard hook

    (Sorry for this gravedig)
    The error kept happening for some wierd reason (I should clean up my code ), but for those wondering; I temporary 'fixed' it this way:
    I created a global variable for the hook in a module & created an instance at launch.

    Sorry for my bad English!

    Duts

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width