Determine when a mouse hook has handled the last message

433 Views Asked by At

I have a form, that acts like a drop-down, that I display non-modal. I attach a mouse hook to the form to determine when the mouse is clicked out of it, so that I know to close it - by setting Visible = False.

Because I want the HookProc to handle the last click, I can't dispose the Hook or my Dropdown until I'm sure that my event handler has returned to the HookProc.

It's a bit hard to explain, but I hope the code below makes it a little clearer:-

//Loop to display the dropdown.
Dim dd as New DropDown
dd.Visible = True
Do While dd.Visible
    Application.DoEvents()
    NativeMethods.MsgWaitForMultipleObjectsEx(0, IntPtr.Zero, 250, &HFF, 4)
Loop
// I want to dispose dd now, but how can I be sure that e.Handled (See below)
// has been returned to HookProc?

//A handler within dropdown to determine what to do with the mouse click.
Private Sub DropDown_MouseHookClick(ByVal sender As Object, ByVal e As MouseClickEventArgs)
    If IWantToCloseTheDropDown Then
        e.Handled = True
        MyHook.UnHook
        Me.Visible = False
    End If
    // All done, e.Handled is returned to HookProc.
    // But which happens first? Will e.Handled arrive at HookProc first, or will
    // the form display loop, above, notice that Visible is now False?
End Sub

//The main part of the hooking class.
Public Function MouseHookProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
    Dim MyMouseHookStruct As MouseHookStruct = DirectCast(Marshal.PtrToStructure(lParam, GetType(MouseHookStruct)), MouseHookStruct)
    If nCode < 0 Then
        Return CallNextHookEx(hHook, nCode, wParam, lParam)
    Else
        Dim handle As Integer = MyMouseHookStruct.hwnd
        Dim c As Control = Control.FromHandle(New IntPtr(handle))
        If MouseUpOrDown Then
            Dim e As MouseHookClickEventArgs
            OnMouseClick(e)
            If e.Handled Then
                Return 1
            EndIf
        End If
        Return CallNextHookEx(hHook, nCode, wParam, lParam)
    End If
End Function
2

There are 2 best solutions below

2
On

Why not just handle the focus events? Form_LostFocus will tell you when they focus on another control/form. At that point you can hide your form.

A mouse hook seems like overkill for detecting if your form has focus or not.

0
On

You are bypassing .NET mechanisms to handle windows events

You should not need anything native to deal with WinForms. Also the whole code is garbage collected so you do SHOULD not have to worry about deleting hooks

You might want to look at the form.deactivate event