I have searched the web tirelessly and tried every possible solution I have come across; yet cannot find a working solution.
I have a mouse hook that I have been using for a long time now and it works great except for when I try to distinguish forward/back mouse wheel messages.
I have seen the mouseData
member of the MSLLHOOKSTRUCT
structure declared as an Integer
and UInteger
. Either way I declare it, I am unable to get the HIWORD
for the WM_MOUSEWHEEL
message to distinguish forwards from back because the value is always the same when I wheel forward or back.
Test Output:
Wheel Forward:
mouseData: 23074474
HIWORD: 352
Wheel Back:
mouseData: 23074474
HIWORD: 352
Code:
<Extension>
Public Function HIWORD(value As Integer) As Integer
Return BitConverter.ToInt16(BitConverter.GetBytes(value), 2)
End Function
Private Shared MouseHookProcedure As HookProcHandler
Private Shared PreviousCallback As Object()
Private Shared ProcPtrSet As Boolean
Private Shared HookHandle As IntPtr
Public Shared Function Start() As Boolean
If HookHandle <> IntPtr.Zero Then Return True
If Not ProcPtrSet Then
ProcPtrSet = True
MouseHookProcedure = New HookProcHandler(AddressOf HookProc)
End If
Using p As Process = Process.GetCurrentProcess()
Using m As ProcessModule = p.MainModule
SetWindowsHookEx(HOOKTYPE.WH_MOUSE, MouseHookProcedure, Pinvoke.Kernel32.Functions.GetModuleHandle(m.ModuleName), Pinvoke.Kernel32.Functions.GetCurrentThreadId)
End Using
End Using
If HookHandle = 0 Then Return False Else Return True
End Function
Public Shared Function [Stop]() As Boolean
Return UnhookWindowsHookEx(HookHandle)
End Function
Private Shared Function HookProc(nCode As Integer, wParam As IntPtr, lParam As IntPtr) As Integer
Dim MSLLHS As MSLLHOOKSTRUCT = Marshal.PtrToStructure(lParam, GetType(MSLLHOOKSTRUCT))
If nCode < 0 Then
Return CallNextHookEx(HookHandle, nCode, wParam, lParam)
Else
' Catch duplicate messages caused by the default behavior of the MouseProc function.
Dim Msg As MouseMessages = CType(wParam, MouseMessages)
If PreviousCallback IsNot Nothing Then
If CType(PreviousCallback(0), MouseMessages) = Msg AndAlso CompairMSLLHOOKSRUCT(PreviousCallback(1), MSLLHS) AndAlso CType(PreviousCallback(2), Date) = Now Then
GoTo NextCallback
End If
End If
' Raise Event
Select Case Msg
Case MouseMessages.MouseWheel
Msg = If(CType(MSLLHS.mouseData, Integer).HIWORD > 0, MouseMessages.MouseWheelForward, MouseMessages.MouseWheelBack)
RaiseEvent Message(Msg MSLLHS)
Case Else
RaiseEvent Message(Msg, MSLLHS)
End Select
PreviousCallback = New Object(2) {CType(wParam, MouseMessages), MSLLHS, Now}
NextCallback:
Try
Return CallNextHookEx(HookHandle, nCode, wParam, lParam)
Catch ex As Exception
Return [Stop]()
End Try
End If
End Function
Private Shared Function CompairMSLLHOOKSRUCT(left As MSLLHOOKSTRUCT, right As MSLLHOOKSTRUCT) As Boolean
Return left.pt.X = right.pt.X AndAlso left.pt.Y = right.pt.Y AndAlso left.mouseData = right.mouseData AndAlso left.flags = right.flags AndAlso left.time = right.time AndAlso left.dwExtraInfo = right.dwExtraInfo
End Function
Pinvoke:
Public Delegate Function HookProcHandler(nCode As Integer, wParam As IntPtr, lParam As IntPtr) As Integer
<DllImport(User32, CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall, SetLastError:=True)>
Public Shared Function UnhookWindowsHookEx(idHook As IntPtr) As Boolean
End Function
<DllImport(User32, CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall, SetLastError:=True)>
Public Shared Function SetWindowsHookEx(idHook As Integer, lpfn As HookProcHandler, hInstance As IntPtr, threadId As Integer) As IntPtr
End Function
<DllImport(User32, CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall, SetLastError:=True)>
Public Shared Function CallNextHookEx(hhk As IntPtr, nCode As Integer, wParam As IntPtr, lParam As IntPtr) As IntPtr
End Function
<DllImport(Kernel32, CharSet:=CharSet.Auto, SetLastError:=True)>
Public Shared Function GetModuleHandle(lpModuleName As String) As IntPtr
End Function
<StructLayout(LayoutKind.Sequential)>
Public Structure MSLLHOOKSTRUCT
Public pt As POINT
Public mouseData As UInteger
Public flags As UInteger
Public time As UInteger
Public dwExtraInfo As IntPtr
End Structure
<StructLayout(LayoutKind.Sequential)>
Public Structure POINT
Public X As Integer
Public Y As Integer
Public Sub New(ByVal X As Integer, ByVal Y As Integer)
Me.X = X
Me.Y = Y
End Sub
End Structure
Public Enum MouseMessages As UInteger
MouseWheel = WindowsMessages.WM_MOUSEWHEEL ' &H20E
' Etc...
End Enum