Friday, January 06, 2006
« window.open sizing | Main | VS2005 - vshost »

Sometimes, it is useful to be able to track windows events even when your application doesn't have focus.  In order to accomplish this, you can use the function SetWindowsHookEx.  Below is an example of using this API in .NET and then wrapping the events with managed mouse and keyboard events.  The code is not production strength, but hopefully, it will give you a good start.  Let me know if you have suggestions for improvements, and I'll update the post.  Here are two other sources on the subject:

Here's my implementation (some of the code follows): 13.5k GlobalHook2.0.zip

Class GlobalHook

Imports System.Runtime.InteropServices

Imports System.Windows.Forms

 

Public Class GlobalHook : Implements IDisposable

 

    Public Event MouseDown As MouseEventHandler

    Public Event MouseMove As MouseEventHandler

    Public Event MouseUp As MouseEventHandler

    Public Event MouseWheel As MouseEventHandler

 

    Public Event KeyDown As KeyEventHandler

    Public Event KeyPress As KeyPressEventHandler

    Public Event KeyUp As KeyEventHandler

 

#Region " CTOR/DTOR "

 

    Public Sub New()

        InstallHooks()

    End Sub

 

    Protected Overrides Sub Finalize()

        Dispose()

        MyBase.Finalize()

    End Sub

 

    Public Sub Dispose() Implements System.IDisposable.Dispose

        RemoveHooks()

    End Sub

 

#End Region

 

#Region " PROTECTED OnXXXX SUBS "

 

    Protected Overridable Sub OnMouseDown(ByVal e As MouseEventArgs)

        'Debug.WriteLine("OnMouseDown")

        RaiseEvent MouseDown(Me, e)

    End Sub

 

    Protected Overridable Sub OnMouseMove(ByVal e As MouseEventArgs)

        'Debug.WriteLine("OnMouseMove")

        RaiseEvent MouseMove(Me, e)

    End Sub

 

    Protected Overridable Sub OnMouseUp(ByVal e As MouseEventArgs)

        'Debug.WriteLine("OnMouseUp")

        RaiseEvent MouseUp(Me, e)

    End Sub

 

    Protected Overridable Sub OnMouseWheel(ByVal e As MouseEventArgs)

        'Debug.WriteLine("OnMouseWheel")

        RaiseEvent MouseWheel(Me, e)

    End Sub

 

    Protected Overridable Sub OnKeyDown(ByVal e As KeyEventArgs)

        'Debug.WriteLine("OnKeyDown")

        For Each handler As KeyEventHandler In KeyDownEvent.GetInvocationList

            handler.Invoke(Me, e)

            If e.Handled Then

                Exit For

            End If

        Next

    End Sub

 

    Protected Overridable Sub OnKeyUp(ByVal sender As Object, ByVal e As KeyEventArgs)

        'Debug.WriteLine("OnKeyUp")

        For Each handler As KeyEventHandler In KeyUpEvent.GetInvocationList

            handler.Invoke(Me, e)

            If e.Handled Then

                Exit For

            End If

        Next

    End Sub

 

    Protected Overridable Sub OnKeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs)

        'Debug.WriteLine("OnKeyPress")

        For Each handler As KeyPressEventHandler In KeyPressEvent.GetInvocationList

            handler.Invoke(Me, e)

            If e.Handled Then

                Exit For

            End If

        Next

    End Sub

 

#End Region

 

    Private Shared hMouseHook As Integer = 0

    Private Shared hKeyboardHook As Integer = 0

 

    Private MouseHookProcedure As Win32.HookProc

    Private KeyboardHookProcedure As Win32.HookProc

 

    Public Sub InstallHooks()

        If hMouseHook = 0 Then

            MouseHookProcedure = New Win32.HookProc(AddressOf MouseHookProc)

 

            hMouseHook = Win32.SetWindowsHookEx( _

                Win32.WH.WH_MOUSE_LL, _

                MouseHookProcedure, _

                Marshal.GetHINSTANCE(Reflection.Assembly.GetExecutingAssembly().GetModules()(0)), _

                0)

 

            If hMouseHook = 0 Then 'SetWindowsHookEx failed

                RemoveHooks()

                Throw New Exception("SetWindowsHookEx failed.")

            End If

        End If

 

        If hKeyboardHook = 0 Then ' install Keyboard hook

            KeyboardHookProcedure = New Win32.HookProc(AddressOf KeyboardHookProc)

            hKeyboardHook = Win32.SetWindowsHookEx( _

                Win32.WH.WH_KEYBOARD_LL, _

                KeyboardHookProcedure, _

                Marshal.GetHINSTANCE(Reflection.Assembly.GetExecutingAssembly().GetModules()(0)), _

                0)

 

            If (hKeyboardHook = 0) Then 'SetWindowsHookEx failed

                RemoveHooks()

                Throw New Exception("SetWindowsHookEx failed.")

            End If

        End If

    End Sub

 

    Public Sub RemoveHooks()

        Dim mouseResult As Boolean = True

        Dim keyboardResult As Boolean = True

 

        If hMouseHook <> 0 Then

            mouseResult = Win32.UnhookWindowsHookEx(hMouseHook)

            hMouseHook = 0

        End If

 

        If hKeyboardHook <> 0 Then

            keyboardResult = Win32.UnhookWindowsHookEx(hKeyboardHook)

            hKeyboardHook = 0

        End If

 

        If Not (mouseResult And keyboardResult) Then 'UnhookWindowsHookEx failed

            Throw New Exception("UnhookWindowsHookEx failed.")

        End If

    End Sub

 

    Private Function MouseHookProc(ByVal nCode As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer

        If nCode >= 0 Then

            Select Case wParam

                Case _

                    Win32.WM.WM_LBUTTONDOWN, _

                    Win32.WM.WM_LBUTTONDBLCLK, _

                    Win32.WM.WM_MBUTTONDOWN, _

                    Win32.WM.WM_MBUTTONDBLCLK, _

                    Win32.WM.WM_RBUTTONDOWN, _

                    Win32.WM.WM_RBUTTONDBLCLK, _

                    Win32.WM.WM_XBUTTONDOWN, _

                    Win32.WM.WM_XBUTTONDBLCLK

                    If MouseDownEvent Is Nothing Then

                        Win32.CallNextHookEx(hMouseHook, nCode, wParam, lParam)

                        Exit Function

                    End If

                Case _

                    Win32.WM.WM_LBUTTONUP, _

                    Win32.WM.WM_MBUTTONUP, _

                    Win32.WM.WM_RBUTTONUP, _

                    Win32.WM.WM_XBUTTONUP

                    If MouseUpEvent Is Nothing Then

                        Win32.CallNextHookEx(hMouseHook, nCode, wParam, lParam)

                        Exit Function

                    End If

 

                Case Win32.WM.WM_MOUSEWHEEL

                    If MouseWheelEvent Is Nothing Then

                        Win32.CallNextHookEx(hMouseHook, nCode, wParam, lParam)

                        Exit Function

                    End If

 

                Case Win32.WM.WM_MOUSEMOVE

                    If MouseMoveEvent Is Nothing Then

                        Win32.CallNextHookEx(hMouseHook, nCode, wParam, lParam)

                        Exit Function

                    End If

            End Select

 

            Dim clickCount As Integer = 0

            Dim buttons As MouseButtons = MouseButtons.None

            Dim mhs As New Win32.MSLLHOOKSTRUCT

            Dim hiWord As Integer

            Dim delta As Integer = 0

 

            Marshal.PtrToStructure(lParam, mhs)

            hiWord = Win32.HIWORD(mhs.mouseData)

 

            Select Case (wParam)

                Case _

                    Win32.WM.WM_LBUTTONDOWN, _

                    Win32.WM.WM_LBUTTONUP, _

                    Win32.WM.WM_LBUTTONDBLCLK

                    buttons = MouseButtons.Left

 

                Case Win32.WM.WM_MBUTTONDOWN, _

                    Win32.WM.WM_MBUTTONUP, _

                    Win32.WM.WM_MBUTTONDBLCLK

                    buttons = MouseButtons.Middle

 

                Case Win32.WM.WM_RBUTTONDOWN, _

                    Win32.WM.WM_RBUTTONUP, _

                    Win32.WM.WM_RBUTTONDBLCLK

                    buttons = MouseButtons.Right

 

                Case _

                    Win32.WM.WM_XBUTTONDOWN, _

                    Win32.WM.WM_XBUTTONUP, _

                    Win32.WM.WM_XBUTTONDBLCLK, _

                    Win32.WM.WM_NCXBUTTONDOWN, _

                    Win32.WM.WM_NCXBUTTONUP, _

                    Win32.WM.WM_NCXBUTTONDBLCLK

 

                    If hiWord = 1 Then

                        buttons = MouseButtons.XButton1

                    ElseIf hiWord = 2 Then

                        buttons = MouseButtons.XButton2

                    End If

 

                Case Win32.WM.WM_MOUSEWHEEL

                    delta = hiWord

 

            End Select

 

            If buttons <> MouseButtons.None Then

                Select Case wParam

                    Case _

                        Win32.WM.WM_LBUTTONDBLCLK, _

                        Win32.WM.WM_MBUTTONDBLCLK, _

                        Win32.WM.WM_RBUTTONDBLCLK, _