Keyboard hook won't work without a form?

Posted on 2006-04-11
Last Modified: 2012-06-27
Im trying to write a service which uses a keyboard hook. When I do a test application which has a form it works great. When I implemented it into my service it doesn't seem to recognize when a key is pressed. Does anybody have any ideas why? Here is the low level hook code which is pretty much standard on the net:

Public Class clsKeyboardHook
    Implements IDisposable
    'Works only on Windows NT, version 4.0 SP3 and later

    Private Declare Function UnhookWindowsHookEx Lib "user32" _
      (ByVal hHook As Integer) As Integer

    Private Declare Function SetWindowsHookEx Lib "user32" _
      Alias "SetWindowsHookExA" (ByVal idHook As Integer, _
      ByVal lpfn As KeyboardHookDelegate, ByVal hmod As Integer, _
      ByVal dwThreadId As Integer) As Integer

    Private Declare Function GetAsyncKeyState Lib "user32" _
      (ByVal vKey As Integer) As Integer

    Private Declare Function CallNextHookEx Lib "user32" _
      (ByVal hHook As Integer, _
      ByVal nCode As Integer, _
      ByVal wParam As Integer, _
      ByVal lParam As KBDLLHOOKSTRUCT) As Integer

    Private Structure KBDLLHOOKSTRUCT
        Public vkCode As Integer
        Public scanCode As Integer
        Public flags As Integer
        Public time As Integer
        Public dwExtraInfo As Integer
    End Structure

    ' Low-Level Keyboard Constants
    Private Const HC_ACTION As Integer = 0
    Private Const LLKHF_EXTENDED As Integer = &H1
    Private Const LLKHF_INJECTED As Integer = &H10
    Private Const LLKHF_ALTDOWN As Integer = &H20
    Private Const LLKHF_UP As Integer = &H80

    'KeyUp/KeyDown constants
    Private Const WM_KEYDOWN = &H100
    Private Const WM_KEYUP = &H101
    Private Const WM_SYSKEYDOWN = &H104
    Private Const WM_SYSKEYUP = &H105

    Private Const WH_KEYBOARD_LL As Integer = 13&
    Private KeyboardHandle As Integer
    Private disposed As Boolean = False

    Private Delegate Function KeyboardHookDelegate(ByVal Code As Integer, ByVal wParam As Integer, ByRef lParam As KBDLLHOOKSTRUCT) As Integer

    'Our keydown and keyup events
    Public Event KeyDown As KeyEventHandler
    Public Event KeyUp As KeyEventHandler

    <MarshalAs(UnmanagedType.FunctionPtr)> _
    Private callback As KeyboardHookDelegate = New KeyboardHookDelegate(AddressOf KeyboardCallback)

    'Keyboard callback function
    Private Function KeyboardCallback(ByVal Code As Integer, ByVal wParam As Integer, ByRef lParam As KBDLLHOOKSTRUCT) As Integer

        If (Code = HC_ACTION) Then
            'Call the appropriate event

            Dim KeyData As Keys
            Dim args As KeyEventArgs

            KeyData = lParam.vkCode

            'Check if the ALT key is pressed
            If CBool(GetAsyncKeyState(Keys.Menu) And &H8000) Then
                'Alt key pressed
                KeyData = KeyData Or Keys.Alt
            End If

            'Check if the Ctrl key is pressed
            If CBool(GetAsyncKeyState(Keys.ControlKey) And &H8000) Then
                'Ctrl key pressed
                KeyData = KeyData Or Keys.Control
            End If

            'Check if the Shift key was pressed
            If CBool(GetAsyncKeyState(Keys.ShiftKey) And &H8000) Then
                'Shift key pressed
                KeyData = KeyData Or Keys.Shift
            End If

            'Create the event args
            args = New KeyEventArgs(KeyData)

            'Raise the appropriate event
            If wParam = WM_KEYDOWN Or wParam = WM_SYSKEYDOWN Then
                OnKeyDown(Me, args)
            ElseIf wParam = WM_KEYUP Or wParam = WM_SYSKEYUP Then
                OnKeyUp(Me, args)
            End If

            'Determine if we should block the key
            If args.Handled Then
                Return 1
            End If
        End If

        'Call the next hook in the hook chain and return the value
        Return CallNextHookEx(KeyboardHandle, Code, wParam, lParam)

    End Function

    Protected Sub OnKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
        RaiseEvent KeyDown(sender, e)
    End Sub

    Protected Sub OnKeyUp(ByVal sender As Object, ByVal e As KeyEventArgs)
        RaiseEvent KeyUp(sender, e)
    End Sub

    Public Sub HookKeyboard()
        callback = New KeyboardHookDelegate(AddressOf KeyboardCallback)

        KeyboardHandle = SetWindowsHookEx( _
          WH_KEYBOARD_LL, callback, _
          Marshal.GetHINSTANCE( _
          [Assembly].GetExecutingAssembly.GetModules()(0)).ToInt32, 0)

        Call CheckHooked()
    End Sub

    Public Overridable Sub Dispose() Implements IDisposable.Dispose
        If Not disposed Then
            disposed = True

            'Unhook the keyboard
            Throw New ObjectDisposedException("KeyboardHook")
        End If
    End Sub

    Public Sub New()
        ''Create a keyboard low-level hook

        'KeyboardHandle = SetWindowsHookEx( _
        '    WH_KEYBOARD_LL, callback, _
        '    Marshal.GetHINSTANCE( _
        '    [Assembly].GetExecutingAssembly.GetModules()(0)).ToInt32, 0)
    End Sub

    Protected Overrides Sub Finalize()

        If Not disposed Then
        End If
    End Sub

    Public Sub CheckHooked()
        If (Hooked()) Then
            Debug.WriteLine("Keyboard hooked...")
            Debug.WriteLine("Keyboard hook failed: " & Err.LastDllError)
        End If
    End Sub

    Public Function rCheckHooked() As Boolean
        If (Hooked()) Then
            Return True
            Return False
        End If
    End Function
    Private Function Hooked()
        Hooked = KeyboardHandle <> 0
    End Function

End Class
Question by:WTarlton
    LVL 9

    Expert Comment

    Does it work when the form does not have focus?

    Author Comment

    Yea it does. I don't know if theres a setting or something? It just won't work when im running it as a service.

    Author Comment

    I am using a WithEvents and firing off events...this doesnt only work with forms does it?
    LVL 85

    Expert Comment

    by:Mike Tomlinson
    Not my forte...but I'm pretty sure it has something to do with how services run in their own desktops:;en-us;171890

    Author Comment

    I think your right Idle Mind...

    Application-defined hooks are limited in the same way that Windows messages are. The hook procedure of a process running in a particular desktop will only get messages targeted for windows created in the same desktop.

    Author Comment

    Ok so the question I guess is how do I hook the service window?
    LVL 85

    Expert Comment

    by:Mike Tomlinson
    Good question.  As I said before though, "Not my forte..."

    Someone will come along I'm sure and educate us both.  =)
    LVL 9

    Expert Comment

    You might want to try using DirectX.

    I started playing with it recently. I am building a remote control. I wanted to 'catch' the buttons when they were pressed. I am planning on using DirectX for this. If you download the directx SDK, it has code examples for input programs which catch keystrokes and joystick inputs.

    I still don't know how it will work with the service and desktops.

    Let me know what you come up with.
    LVL 8

    Accepted Solution

    Hi there,

    have you tried setting the "Allow sevice to interact with the desktop" property for the service?

    This is found under "Services" (from Control Panel/Administrative Tools).
    Select your service, goto properties, select the "Log on" tab, and check the box that says "Allow the service to interact with desktop".

    Not sure if this will work or not, but I found it useful for debugging a service in the past, when I wanted to let it display a messagebox with exception information.

    Best Regards,


    Author Comment

    That unfortunatly did not work. It did however allow me to use a msgbox which I wasn't able to get working before!

    Write Comment

    Please enter a first name

    Please enter a last name

    We will never share this with anyone.

    Featured Post

    How your wiki can always stay up-to-date

    Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
    - Increase transparency
    - Onboard new hires faster
    - Access from mobile/offline

    If you're writing a .NET application to connect to an Access .mdb database and use pre-existing queries that require parameters, you've come to the right place! Let's say the pre-existing query(qryCust) in Access takes a Date as a parameter and l…
    The ECB site provides FX rates for major currencies since its inception in 1999 in the form of an XML feed. The files have the following format (reducted for brevity) (CODE) There are three files available HERE (…
    In this sixth video of the Xpdf series, we discuss and demonstrate the PDFtoPNG utility, which converts a multi-page PDF file to separate color, grayscale, or monochrome PNG files, creating one PNG file for each page in the PDF. It does this via a c…
    Sending a Secure fax is easy with eFax Corporate ( First, Just open a new email message.  In the To field, type your recipient's fax number You can even send a secure international fax — just include t…

    737 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question

    Need Help in Real-Time?

    Connect with top rated Experts

    19 Experts available now in Live!

    Get 1:1 Help Now