Click here to Skip to main content
15,868,066 members
Articles / Desktop Programming / Win32

Detect System Wide Mouse Events

Rate me:
Please Sign up or sign in to vote.
4.67/5 (8 votes)
7 Mar 2014CPOL2 min read 80.9K   3.2K   28   9
How to detect system wide mouse events in your application

An updated version of MouseHunter project with Mouse Wheel Direction detection support is available to download here: Download MouseHunter_1.1.zip.

Introduction

Maybe sometimes you needed to track the system wide mouse events from your application. But is it possible to steal a mouse event from the system? Also can your application get notified by events about which system event has occurred? If you want to know the secret of system wide mouse events detection and event stealing, the article below is for you.

Background

I got many samples on the Internet about hooking. But I always wanted a mouse hooking solution that will notify me with OnMouseClick types of events in my application if a MouseClick occurred anywhere in the system. Here exactly I have created one. It is a VB6 ActiveX DLL project. It can trace all the required mouse events you will be interested in and also it can raise events to your application using the library about the corresponding events occurring in the system. You'll be able to get the Caption (if any) of the window under your mouse, you can get the window handle (hWnd) under your mouse, also the x and y coordinates of the mouse on your computer screen. Finally, one more interesting thing is that, you'll be able to intercept any of the mouse events in the system supported by this library. It means, for example, you can decide not to pass the "Mouse right button down" event to the system, instead YOU will handle this within your application.

I have named the DLL project as MouseHunter. It has one class module named Tracer and one general module named modHook. The code of the Tracer class is given below:

VB.NET
Option Explicit
  
'* The SetWindowsHookEx function installs an application-defined hook 
'procedure into a hook chain. You would install a hook procedure to monitor 
'the system for certain types of events. These events are associated either 
'with a specific thread or with all threads in the system.
Private Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" _
    (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, _
    ByVal dwThreadId As Long) As Long
'* The UnhookWindowsHookEx function removes a hook procedure installed in a hook chain 
'by the SetWindowsHookEx function.
Private Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long
'* The CallNextHookEx function passes the hook information to the next hook procedure 
'in the current hook chain. A hook procedure can call this function either 
'before or after processing the hook information.
Private Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, _
    ByVal nCode As Long, ByVal wParam As Long, lParam As Any) As Long

'* The IsWindow function determines whether the specified window 
'handle identifies an existing window.
Private Declare Function IsWindow Lib "user32" (ByVal hwnd&) As Long

'* The GetWindowText function copies the text of the specified window’s 
'title bar (if it has one) into a buffer. If the specified window is a control, 
'the text of the control is copied.
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
    (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
'* The WindowFromPoint function retrieves the handle of the window that contains 
'the specified point.
Private Declare Function WindowFromPoint Lib "user32" _
    (ByVal xPoint As Long, ByVal yPoint As Long) As Long
'* The GetCursorPos function retrieves the cursor’s position, in screen coordinates.
Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long

'* Type to hold the mouse coordinates
Private Type POINTAPI
    X As Long
    Y As Long
End Type

Private point As POINTAPI

'******************************************************
'* System messages for Mouse that we want to trace
Private Const WM_MOUSEMOVE           As Long = &H200
Private Const WM_MOUSEWHEEL          As Long = &H20A
Private Const WM_LBUTTONDOWN         As Long = &H201
Private Const WM_LBUTTONUP           As Long = &H202
Private Const WM_RBUTTONDOWN         As Long = &H204
Private Const WM_RBUTTONUP           As Long = &H205
Private Const WM_MBUTTONDOWN         As Long = &H207
Private Const WM_MBUTTONUP           As Long = &H208
'******************************************************

'* System hook for mouse that we'll use
Private Const WH_MOUSE_LL            As Long = 14

Private mlngMouseHook               As Long
Private mstrWindowCaption           As String

'******************************************************
'* System events of Mouse that we can stop broadcasting
'* to the System and make only traceable from the caller application
Public Type EventThief
    LEFT_DOWN       As Boolean
    LEFT_UP         As Boolean
    RIGHT_DOWN      As Boolean
    RIGHT_UP        As Boolean
    MIDDLE_DOWN     As Boolean
    MIDDLE_UP       As Boolean
    WHEEL           As Boolean
    MOVE            As Boolean
End Type

Private mtypEventThief As EventThief

Private Const HC_ACTION = 0

'* Type to hold Mouse Hook information
Private Type MOUSELLHOOKSTRUCT
    point As POINTAPI
    data As Long
    flags As Long
    time As Long
    extra As Long
End Type

Private mousedata As MOUSELLHOOKSTRUCT

'******************************************************
'* Events that will be fired to the caller application
'* For different Mouse activities in the System
Public Event OnSystemMouseMove()
Public Event OnSystemMouseWheel()
Public Event OnSystemMouseLeftDown()
Public Event OnSystemMouseLeftUp()
Public Event OnSystemMouseRightDown()
Public Event OnSystemMouseRightUp()
Public Event OnSystemMouseMiddleDown()
Public Event OnSystemMouseMiddleUp()
'******************************************************

'* Get the mouse events that we want to steal from the System
Public Property Get StealMouseEvents() As EventThief
    StealMouseEvents = mtypEventThief
End Property

'* Set the mouse events that we want to steal from the System
Public Property Let StealMouseEvents(typEventThief As EventThief)
    mtypEventThief = typEventThief
End Property

'* Get the X coordinate of the Mouse in the current screen
Public Property Get CoordinateX() As Long
    CoordinateX = point.X
End Property

'* Get the Y coordinate of the Mouse in the current screen
Public Property Get CoordinateY() As Long
    CoordinateY = point.Y
End Property

'* Get the Caption (if any) of the window currently under the Mouse
Public Property Get WindowTextUnderMouse() As String
    WindowTextUnderMouse = GetWindowTitle(point.X, point.Y)
End Property

'* Get the handle (hWnd) of the window currently under the Mouse
Public Property Get WindowHandleUnderMouse() As Long
    WindowHandleUnderMouse = GetWindowHandle(point.X, point.Y)
End Property

'* Start tracing system wide mouse events
Public Sub StartMouseTracing(ByVal hwnd As Long)
    If IsWindow(hwnd) Then
        glnghWnd = hwnd
        
        '* If there is already a hook by the application, don't hook again
        If GetProp(hwnd, "MouseHook") Then
            Exit Sub
        End If
        
        '* Try to set the Object instance of the Tracer class in memory
        '* If successful, then set the hook
        If SetProp(hwnd, ByVal "MouseHook", ObjPtr(Me)) Then
            mlngMouseHook = SetWindowsHookEx(WH_MOUSE_LL, AddressOf modHook.MouseProc, _
            App.hInstance, 0)
        End If
    End If
End Sub

'* Stop tracing system wide mouse events
Public Sub StopMouseTracing()
    '* First check if the hook was established
    If mlngMouseHook <> 0 Then
        RemoveProp glnghWnd, "MouseHook"
        UnhookWindowsHookEx mlngMouseHook
    End If
End Sub

Private Sub Class_Terminate()
    '* Make sure the hook is removed after the class is terminated
    StopMouseTracing
End Sub

'* Bypassed MouseProc from the modHook's MouseProc.
'* The trick is necessary here to raise the appropriate events to the caller
'* for particular System events of the Mouse
Friend Function MouseProc(ByVal nCode As Long, _
    ByVal wParam As Long, ByVal lParam As Long) As Long
    If (nCode = HC_ACTION) Then
        ' Mouse data not used in this example, but useful
        CopyMemory mousedata, ByVal lParam, Len(mousedata)
        Select Case wParam
            Case WM_MOUSEMOVE
                '* Trace the coordinate of the Mouse whenever it moves
                GetCursorPos point
                '* Raise the event to the caller Window
                RaiseEvent OnSystemMouseMove
                '* Check if caller wanted this event as non traceable to the System
                If mtypEventThief.MOVE Then
                    '* If so, steal the event from the system and make it only traceable
                    '* to the window of the caller application
                    MouseProc = -1
                Else
                    '* If not, release the event to the System
                    MouseProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)
                End If
            Case WM_MOUSEWHEEL
                '* Raise the event to the caller Window
                RaiseEvent OnSystemMouseWheel
                '* Check if caller wanted this event as non traceable to the System
                If mtypEventThief.WHEEL Then
                    '* If so, steal the event from the system and make it only traceable
                    '* to the window of the caller application
                    MouseProc = -1
                Else
                    '* If not, release the event to the System
                    MouseProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)
                End If
            Case WM_LBUTTONDOWN
                '* Raise the event to the caller Window
                RaiseEvent OnSystemMouseLeftDown
                '* Check if caller wanted this event as non traceable to the System
                If mtypEventThief.LEFT_DOWN Then
                    '* If so, steal the event from the system and make it only traceable
                    '* to the window of the caller application
                    MouseProc = -1
                Else
                    '* If not, release the event to the System
                    MouseProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)
                End If
            Case WM_LBUTTONUP
                '* Raise the event to the caller Window
                RaiseEvent OnSystemMouseLeftUp
                '* Check if caller wanted this event as non traceable to the System
                If mtypEventThief.LEFT_UP Then
                    '* If so, steal the event from the system and make it only traceable
                    '* to the window of the caller application
                    MouseProc = -1
                Else
                    '* If not, release the event to the System
                    MouseProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)
                End If
            Case WM_RBUTTONDOWN
                '* Raise the event to the caller Window
                RaiseEvent OnSystemMouseRightDown
                '* Check if caller wanted this event as non traceable to the System
                If mtypEventThief.RIGHT_DOWN Then
                    '* If so, steal the event from the system and make it only traceable
                    '* to the window of the caller application
                    MouseProc = -1
                Else
                    '* If not, release the event to the System
                    MouseProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)
                End If
            Case WM_RBUTTONUP
                '* Raise the event to the caller Window
                RaiseEvent OnSystemMouseRightUp
                '* Check if caller wanted this event as non traceable to the System
                If mtypEventThief.RIGHT_UP Then
                    '* If so, steal the event from the system and make it only traceable
                    '* to the window of the caller application
                    MouseProc = -1
                Else
                    '* If not, release the event to the System
                    MouseProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)
                End If
            Case WM_MBUTTONDOWN
                '* Raise the event to the caller Window
                RaiseEvent OnSystemMouseMiddleDown
                '* Check if caller wanted this event as non traceable to the System
                If mtypEventThief.MIDDLE_DOWN Then
                    '* If so, steal the event from the system and make it only traceable
                    '* to the window of the caller application
                    MouseProc = -1
                Else
                    '* If not, release the event to the System
                    MouseProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)
                End If
            Case WM_MBUTTONUP
                '* Raise the event to the caller Window
                RaiseEvent OnSystemMouseMiddleUp
                '* Check if caller wanted this event as non traceable to the System
                If mtypEventThief.MIDDLE_UP Then
                    '* If so, steal the event from the system and make it only traceable
                    '* to the window of the caller application
                    MouseProc = -1
                Else
                    '* If not, release the event to the System
                    MouseProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)
                End If
            Case Else
                ' not implemented yet
        End Select
    End If
End Function

'* Returns the Window Caption (if any) under the mouse
Private Function GetWindowTitle(CoordX As Long, CoordY As Long) As String
    Dim strTitle As String
    strTitle = String(255, Chr$(0))
    GetWindowText WindowFromPoint(CoordX, CoordY), strTitle, 255
    strTitle = Left$(strTitle, InStr(strTitle, Chr$(0)) - 1)
    GetWindowTitle = strTitle
End Function

'* Returns the Window Handle (hWnd) under the mouse
Private Function GetWindowHandle(CoordX As Long, CoordY As Long) As Long
    GetWindowHandle = WindowFromPoint(CoordX, CoordY)
End Function

Here is the code of the general module modHook:

VB.NET
Option Explicit

'* The CopyMemory function copies a block of memory from one location to another.
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
    (Destination As Any, Source As Any, ByVal Length As Long)
'* The GetProp function retrieves a data handle from the property list 
'of the given window. The given character string identifies the handle 
'to be retrieved. The string and handle must have been added to the 
'property list by a previous call to the SetProp function.
Public Declare Function GetProp Lib "user32" Alias "GetPropA" _
    (ByVal hwnd As Long, ByVal lpString As String) As Long
'* The SetProp function adds a new entry or changes an existing entry 
'in the property list of the specified window. The function adds a new entry 
'to the list if the specified character string does not exist already in the list. 
'The new entry contains the string and the handle. 
'Otherwise, the function replaces the string’s current handle with the specified handle.
Public Declare Function SetProp Lib "user32" Alias "SetPropA" _
    (ByVal hwnd As Long, ByVal lpString As String, ByVal hData As Long) As Long
'* The RemoveProp function removes an entry from the property list 
'of the specified window. The specified character string identifies 
'the entry to be removed.
Public Declare Function RemoveProp Lib "user32" Alias "RemovePropA" _
    (ByVal hwnd&, ByVal lpString$) As Long

'* Global variable to hold the hWnd of the caller application's window
Global glnghWnd   As Long

'* Main procedure to trace the mouse events, but it is bypassed
'* to the Tracer class's MouseProc method so that the events can be raised from there.
'* As you know events can't be raised from a general module.
Public Function MouseProc(ByVal nCode As Long, _
    ByVal wParam As Long, ByVal lParam As Long) As Long
    MouseProc = TracerFromMemory(glnghWnd).MouseProc(nCode, wParam, lParam)
End Function

'* This method retrieves the object of the tracer class created in the memory
Private Function TracerFromMemory(ByVal hwnd As Long) As Tracer
    Dim MsgHookEx   As Tracer
    Dim ptrObject   As Long
    
    '* Getting the already created Object of the Tracer class
    '* from memory in ptrObjec
    ptrObject = GetProp(glnghWnd, ByVal "MouseHook")
    
    '* Setting the Tracer class object from ptrObject to MsgHookEx
    CopyMemory MsgHookEx, ptrObject, Len(ptrObject)
    
    '* return the Tracer class object
    Set TracerFromMemory = MsgHookEx
    
    '* Remove the reference of the Tracer class object from memory
    CopyMemory MsgHookEx, 0&, Len(ptrObject)
End Function

Using the Code

You can use the library from any COM compatible high level language. I have created a sample VB6 standard EXE application that shows the use of this library. There is only one form named frmMain and it has two labels named lblWindowTitle & lblEvents. Just one thing to remember about this test application is that, don't close it from the Stop button in the VB IDE. Instead, use the Form's cross icon to close the application. Here is the code below:

VB.NET
'********************************************************
'* WARNING!!!
'* PLEASE DO NOT STOP THIS APPLICATION FROM THE VB IDE
'* CLICK THE FORM'S CROSS ICON IF YOU INTEND TO CLOSE
'********************************************************

Option Explicit

'* Declaring the mouse tracer object using WithEvents so that the events
'* can be traced
Private WithEvents MyMouseHunter As MouseHunter.Tracer

Private Sub Form_Load()
    '* EventStealingInfo will help to steal any particular mouse event you want.
    '* This will help you to grab the event just within your application
    '* And will not pass the event to the system
    Dim EventStealingInfo As EventThief
    
    lblWindowTitle.Caption = ""
    lblEvents.Caption = ""
    
    Set MyMouseHunter = New MouseHunter.Tracer
    
    '* Selecting the particular mouse events that you want to steal
    '* from the system
    '* Here, you'll steal the Mouse's Right Button's UP & Down events
    '* From the System, so you'll see your right button of the Mouse
    '* Is not working in anywhere in the System, But your application
    '* is still tracing the events!!
    With EventStealingInfo
        .RIGHT_DOWN = True
        .RIGHT_UP = True
    End With
    
    '* Passing the EventStealingInfo to the tracer class
    MyMouseHunter.StealMouseEvents = EventStealingInfo
    '* Start tracing the system wide mouse events
    MyMouseHunter.StartMouseTracing Me.hWnd
    
End Sub

Private Sub Form_Unload(Cancel As Integer)
    '* make sure the system wide mouse tracing is stopped after the application
    '* is closed
    MyMouseHunter.StopMouseTracing
End Sub

'* Tracking the system wide mouse events
Private Sub MyMouseHunter_OnSystemMouseMove()
    '* Choose anything you want to trace when your mouse moves
    '* You can trace the X and Y coordinate of your mouse position in the window
    '* Or the Window handle (hWnd) of the window under your mouse
    '* Or the Window title (Caption) under your mouse.
    
    'lblWindowTitle.Caption = MyMouseHunter.CoordinateX & "," _
                & MyMouseHunter.CoordinateY
    'lblWindowTitle.Caption = MyMouseHunter.WindowHandleUnderMouse
    lblWindowTitle.Caption = MyMouseHunter.WindowTextUnderMouse
    
    lblEvents.Caption = "Moving..."
End Sub

Private Sub MyMouseHunter_OnSystemMouseLeftDown()
    lblEvents.Caption = "Left Down"
End Sub

Private Sub MyMouseHunter_OnSystemMouseLeftUp()
    lblEvents.Caption = "Left Up"
End Sub

Private Sub MyMouseHunter_OnSystemMouseRightDown()
    lblEvents.Caption = "Right Down"
End Sub

Private Sub MyMouseHunter_OnSystemMouseRightUp()
    lblEvents.Caption = "Right Up"
End Sub

Private Sub MyMouseHunter_OnSystemMouseMiddleDown()
    lblEvents.Caption = "Middle Down"
End Sub

Private Sub MyMouseHunter_OnSystemMouseMiddleUp()
    lblEvents.Caption = "Middle Up"
End Sub

Private Sub MyMouseHunter_OnSystemMouseWheel()
    lblEvents.Caption = "Wheel..."
End Sub

'* ENJOY!!!

Points of Interest

Using this library, the system wide mouse events tracing will be very easy for you. Hope you'll enjoy the code.

History

  • Uploaded by Zakir Hossain on 27th September, 2008 at 6:46 PM (+6 GMT)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Orion Informatics Ltd.
Bangladesh Bangladesh
I have been doing computer programming for about last 12 years. Started practicing with QBASIC 4.5. Though started loving VB 4.0 quickly. I worked for national organization like BRAC and still working for international organization like British Council. Once I used to develop web applications with classic ASP 3.0. In my long programming journey I developed many customised user controls in VB6 and many custom functionaly enriched library to meet client requirements. Right now doing ASP.NET and C#. (Also did not stop coding in VB6). Also learning WPF, WCF and SilverLight in .NET 4. Anyway, life is not that bad Smile | :)

Contact: neolithian@msn.com

Comments and Discussions

 
Question77 ERRORS in VB.net 2010 Nice work Pin
Ludwig Skiller10-Apr-15 11:26
Ludwig Skiller10-Apr-15 11:26 
Questionproblem with "stealing" mouse hook Pin
friendsterjoel8-Feb-13 2:02
friendsterjoel8-Feb-13 2:02 
QuestionThanks a lot Pin
willy.tedyanto27-Jul-12 18:00
willy.tedyanto27-Jul-12 18:00 
Questionhow to use this DLL in my .net Pin
Suresh271295-Jul-12 2:08
Suresh271295-Jul-12 2:08 
QuestionLove the code Pin
David Mowbray4-Mar-12 11:56
David Mowbray4-Mar-12 11:56 
QuestionGet Coordinates from Mouse Wheel Pin
John Brazillian Connor26-Jan-11 16:48
John Brazillian Connor26-Jan-11 16:48 
GeneralOutstanding Pin
Developers Of Bangladesh (DOB)10-Nov-09 1:39
Developers Of Bangladesh (DOB)10-Nov-09 1:39 
GeneralExcellent - Thanks!!! Pin
zdastrix29-Jun-09 1:30
zdastrix29-Jun-09 1:30 
GeneralVista 64 Pin
rj_21_200315-Oct-08 23:38
rj_21_200315-Oct-08 23:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.