I've managed to get this C# code (not my own!) working in VB.NET, to implement a mouse hook in a Console application. Here's the VB.NET code:
Imports System.Runtime.InteropServices
Imports System.Windows.Forms
Module Module1
Class InterceptMouse
Private Shared _proc As LowLevelMouseProc = AddressOf HookCallback
Private Shared _hookID As IntPtr = IntPtr.Zero
Public Shared Sub Main()
_hookID = SetHook(_proc)
Application.Run()
UnhookWindowsHookEx(_hookID)
End Sub
Private Shared Function SetHook(proc As LowLevelMouseProc) As IntPtr
Using curProcess As Process = Process.GetCurrentProcess()
Using curModule As ProcessModule = curProcess.MainModule
Return SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle(curModule.ModuleName), 0)
End Using
End Using
End Function
Private Delegate Function LowLevelMouseProc(nCode As Integer, wParam As IntPtr, lParam As IntPtr) As IntPtr
Private Shared Function HookCallback(nCode As Integer, wParam As IntPtr, lParam As IntPtr) As IntPtr
Dim MouseSetting As Integer = MouseMessages.WM_LBUTTONDOWN
Dim hookStruct As MSLLHOOKSTRUCT
If nCode >= 0 AndAlso MouseSetting = CType(wParam, MouseMessages) Then
hookStruct = CType(Marshal.PtrToStructure(lParam, GetType(MSLLHOOKSTRUCT)), MSLLHOOKSTRUCT)
Console.WriteLine(hookStruct.pt.x & ", " & hookStruct.pt.y)
End If
Dim pt As New POINT
pt.x = hookStruct.pt.x
pt.y = hookStruct.pt.y
MouseCoordinates.Add(pt)
Return CallNextHookEx(_hookID, nCode, wParam, lParam)
End Function
Private Const WH_MOUSE_LL As Integer = 14
Private Enum MouseMessages
WM_LBUTTONDOWN = &H201
WM_LBUTTONUP = &H202
WM_MOUSEMOVE = &H200
WM_MOUSEWHEEL = &H20A
WM_RBUTTONDOWN = &H204
WM_RBUTTONUP = &H205
End Enum
<StructLayout(LayoutKind.Sequential)> _
Public Structure POINT
Public x As Integer
Public y As Integer
End Structure
<StructLayout(LayoutKind.Sequential)> _
Private Structure MSLLHOOKSTRUCT
Public pt As POINT
Public mouseData As UInteger
Public flags As UInteger
Public time As UInteger
Public dwExtraInfo As IntPtr
End Structure
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function SetWindowsHookEx(idHook As Integer, lpfn As LowLevelMouseProc, hMod As IntPtr, dwThreadId As UInteger) As IntPtr
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function UnhookWindowsHookEx(hhk As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function CallNextHookEx(hhk As IntPtr, nCode As Integer, wParam As IntPtr, lParam As IntPtr) As IntPtr
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function GetModuleHandle(lpModuleName As String) As IntPtr
End Function
End Class
I've then called it from the Sub Main:
Sub Main()
InterceptMouse.Main()
End Sub
However, when I debug it, it goes to the Application.Run() line and it doesn't advance to the next line, so I can't see what it's doing behind-the-scenes. It then waits for user input and returns integer coordinates on the Console. This isn't what I want it to do. I want to have this code running in another function or a Background Worker, passing coordinates to the main thread whenever it receives a mouse-click, so that it can handle the rest. The problem is, this code is far too complex for my level of understanding and I don't really know how it works. If I can at least manage to make the Console return mouse coordinates whenever the user clicks, I should be able to ignore it and handle the rest of the code from there.