KeyboardHook.vb

Imports System.Runtime.InteropServices

Public Class KeyboardHook

    <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)>

    Private Overloads Shared Function SetWindowsHookEx(ByVal idHook As Integer, ByVal HookProc As KBDLLHookProc, ByVal hInstance As IntPtr, ByVal wParam As Integer) As Integer

    End Function

    <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)>

    Private Overloads Shared Function CallNextHookEx(ByVal idHook As Integer, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer

    End Function

    <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)>

    Private Overloads Shared Function UnhookWindowsHookEx(ByVal idHook As Integer) As Boolean

    End Function

    <StructLayout(LayoutKind.Sequential)>

    Private Structure KBDLLHOOKSTRUCT

        Public vkCode As UInt32

        Public scanCode As UInt32

        Public flags As KBDLLHOOKSTRUCTFlags

        Public time As UInt32

        Public dwExtraInfo As UIntPtr

    End Structure

    <Flags()>

    Private Enum KBDLLHOOKSTRUCTFlags As UInt32

        LLKHF_EXTENDED = &H1

        LLKHF_INJECTED = &H10

        LLKHF_ALTDOWN = &H20

        LLKHF_UP = &H80

    End Enum

    Public Shared Event KeyDown(ByVal Key As Keys)

    Public Shared Event KeyUp(ByVal Key As Keys)

    Private Const WH_KEYBOARD_LL As Integer = 13

    Private Const HC_ACTION As Integer = 0

    Private Const WM_KEYDOWN = &H100

    Private Const WM_KEYUP = &H101

    Private Const WM_SYSKEYDOWN = &H104

    Private Const WM_SYSKEYUP = &H105

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

    Private KBDLLHookProcDelegate As KBDLLHookProc = New KBDLLHookProc(AddressOf KeyboardProc)

    Private HHookID As IntPtr = IntPtr.Zero

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

        If (nCode = HC_ACTION) Then

            Dim struct As KBDLLHOOKSTRUCT

            Select Case wParam

                Case WM_KEYDOWN, WM_SYSKEYDOWN

                    RaiseEvent KeyDown(CType(CType(Marshal.PtrToStructure(lParam, struct.GetType()), KBDLLHOOKSTRUCT).vkCode, Keys))

                Case WM_KEYUP, WM_SYSKEYUP

                    RaiseEvent KeyUp(CType(CType(Marshal.PtrToStructure(lParam, struct.GetType()), KBDLLHOOKSTRUCT).vkCode, Keys))

            End Select

        End If

        Return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam)

    End Function

    Public Sub New()

        HHookID = SetWindowsHookEx(WH_KEYBOARD_LL, KBDLLHookProcDelegate, System.Runtime.InteropServices.Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly.GetModules()(0)).ToInt32, 0)

        If HHookID = IntPtr.Zero Then

            Throw New Exception("Could not set keyboard hook")

        End If

    End Sub

    Protected Overrides Sub Finalize()

        If Not HHookID = IntPtr.Zero Then

            UnhookWindowsHookEx(HHookID)

        End If

        MyBase.Finalize()

    End Sub

End Class

Example Form Call/setup/usage for the KeyboardHook.vb

Public Class Form1


    Private WithEvents kbHook As New KeyboardHook

    Private Sub kbHook_KeyDown(ByVal Key As System.Windows.Forms.Keys) Handles kbHook.KeyDown

        Debug.WriteLine(Now & ">> " & Key.ToString)

        If Key = Keys.RShiftKey Then

            bRleaseMouse = True

        Else

            bRleaseMouse = False

        End If

        'If Key = Keys.F4 Then MsgBox("KeyDown: F4")

    End Sub

    Private Sub kbHook_KeyUp(ByVal Key As System.Windows.Forms.Keys) Handles kbHook.KeyUp

        'Debug.WriteLine(Key)

        If Key = Keys.F4 Then

            Beep()

            Label1.Text = "KeyUp: SHIFT: " & Now

            'Me.Activate()

            'Me.BringToFront()

        End If

    End Sub

End Class

Note: To run this inside Visual Studio, you will need to go to: 
Project -> [Project Name] Properties -> Debug -> Uncheck “Enable the Visual Studio hosting process” 
As that intercepts the hooked messages before your program.