Search code examples
.netvb.netmousewheeluser32windows-messages

SetWindowsHookEx for WM_MOUSEWHEEL


I need a code example written in VB.NET to capture the mousewheel scrolling events outside the form using a low-level hook with user32.dll and WM_MOUSEWHEEL like said by Hans Passant answer in my other question: Record mouse Middle button and wheel scroll

This is a pseudo-example of what I need to do:

Dim mousewheel_up as boolean
Dim mousewheel_down as boolean

Sub that Overides the windows messages to set the mousewheel booleans

    If mousewheel_up then msgbox("MouseWheel up")
    If mousewheel_down then msgbox("MouseWheel down")

End sub

UPDATE

Tried this but it only works inside the form, also I don't know how to get the delta values:

   Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Application.AddMessageFilter(New MouseWheelMessageFilter())
    End Sub

Public Class MouseWheelMessageFilter : Implements IMessageFilter

    Public Function PreFilterMessage1(ByRef m As Message) As Boolean Implements IMessageFilter.PreFilterMessage

        ' Filter out WM_MOUSEWHEEL messages, which raise the MouseWheel event,
        ' whenever the Ctrl key is pressed. Otherwise, let them through.
        Const WM_MOUSEWHEEL As Integer = &H20A

        'If m.Msg = WM_MOUSEWHEEL & My.Computer.Keyboard.CtrlKeyDown Then
        If m.Msg = WM_MOUSEWHEEL Then
            ' Process the message here.
            If Form.ActiveForm IsNot Nothing Then
                MsgBox("Mouse scrolled!")
                ' TODO: Insert your code here to adjust the size of the active form.
                ' As shown above in the If statement, you can retrieve the form that
                ' is currently active using the static Form.ActiveForm property.
                ' ...
            End If
            Return True  ' swallow this particular message
        End If
        Return False    ' but let all other messages through
    End Function

End Class

Solution

  • You need to use the SetWindowsHookEx function and specify the hook type as *WH_MOUSE_LL* (14). Have the following declaration.

    Public Structure Point
        Public X As Integer
        Public Y As Integer
    End Structure
    
    Public Structure Msllhookstruct
        Public Location As Point
        Public MouseData As Integer
        Public Flags As Integer
        Public Time As Integer
        Public ExtraInfo As Integer
    End Structure
    
    Private Delegate Function HookProc(nCode As Integer, wParam As Integer, ByRef lParam As Msllhookstruct) As Integer
    
    <DllImport("user32.dll", SetLastError:=True)> _
    Private Function SetWindowsHookEx(ByVal hookType As Integer, ByVal lpfn As HookProc, ByVal hMod As IntPtr, ByVal dwThreadId As UInteger) As IntPtr
    End Function
    
    <DllImport("user32.dll")> _
    Private Function CallNextHookEx(ByVal hhk As IntPtr, ByVal nCode As Integer, ByVal wParam As IntPtr, ByRef lParam As Msllhookstruct) As IntPtr
    End Function
    
    Private Hook As IntPtr
    

    In your initialising routine, use it as follow

    Hook = SetWindowsHookEx(14, AddressOf Proc, Process.GetCurrentProcess().MainModule.BaseAddress.ToInt32(), 0)
    

    Where Proc is a function callback like so:

    Private Function Proc(nCode As Integer, wParam As Integer, ByRef lParam As Msllhookstruct) As IntPtr
        If wParam = 522 Then
            Dim Delta = CShort(lParam.MouseData >> 16)
    
            If Delta > 0 Then
                ' Up
            ElseIf Delta < 0 Then
                ' Down
            End If
        End If
    
        Return CallNextHookEx(Hook, nCode, wParam, lParam)
    End Function