WTarlton
asked on
Keyboard hook won't work without a form?
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.F unctionPtr )> _
Private callback As KeyboardHookDelegate = New KeyboardHookDelegate(Addre ssOf 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(Key s.Menu) And &H8000) Then
'Alt key pressed
KeyData = KeyData Or Keys.Alt
End If
'Check if the Ctrl key is pressed
If CBool(GetAsyncKeyState(Key s.ControlK ey) And &H8000) Then
'Ctrl key pressed
KeyData = KeyData Or Keys.Control
End If
'Check if the Shift key was pressed
If CBool(GetAsyncKeyState(Key s.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(KeyboardHan dle, 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(Addre ssOf KeyboardCallback)
KeyboardHandle = SetWindowsHookEx( _
WH_KEYBOARD_LL, callback, _
Marshal.GetHINSTANCE( _
[Assembly].GetExecutingAss embly.GetM odules()(0 )).ToInt32 , 0)
Call CheckHooked()
End Sub
Public Overridable Sub Dispose() Implements IDisposable.Dispose
If Not disposed Then
disposed = True
'Unhook the keyboard
UnhookWindowsHookEx(Keyboa rdHandle)
Else
Throw New ObjectDisposedException("K eyboardHoo k")
End If
End Sub
Public Sub New()
''Create a keyboard low-level hook
'KeyboardHandle = SetWindowsHookEx( _
' WH_KEYBOARD_LL, callback, _
' Marshal.GetHINSTANCE( _
' [Assembly].GetExecutingAss embly.GetM odules()(0 )).ToInt32 , 0)
End Sub
Protected Overrides Sub Finalize()
MyBase.Finalize()
If Not disposed Then
Dispose()
End If
End Sub
Public Sub CheckHooked()
If (Hooked()) Then
Debug.WriteLine("Keyboard hooked...")
Else
Debug.WriteLine("Keyboard hook failed: " & Err.LastDllError)
End If
End Sub
Public Function rCheckHooked() As Boolean
If (Hooked()) Then
Return True
Else
Return False
End If
End Function
Private Function Hooked()
Hooked = KeyboardHandle <> 0
End Function
End Class
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
'Our keydown and keyup events
Public Event KeyDown As KeyEventHandler
Public Event KeyUp As KeyEventHandler
<MarshalAs(UnmanagedType.F
Private callback As KeyboardHookDelegate = New KeyboardHookDelegate(Addre
'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(Key
'Alt key pressed
KeyData = KeyData Or Keys.Alt
End If
'Check if the Ctrl key is pressed
If CBool(GetAsyncKeyState(Key
'Ctrl key pressed
KeyData = KeyData Or Keys.Control
End If
'Check if the Shift key was pressed
If CBool(GetAsyncKeyState(Key
'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(KeyboardHan
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(Addre
KeyboardHandle = SetWindowsHookEx( _
WH_KEYBOARD_LL, callback, _
Marshal.GetHINSTANCE( _
[Assembly].GetExecutingAss
Call CheckHooked()
End Sub
Public Overridable Sub Dispose() Implements IDisposable.Dispose
If Not disposed Then
disposed = True
'Unhook the keyboard
UnhookWindowsHookEx(Keyboa
Else
Throw New ObjectDisposedException("K
End If
End Sub
Public Sub New()
''Create a keyboard low-level hook
'KeyboardHandle = SetWindowsHookEx( _
' WH_KEYBOARD_LL, callback, _
' Marshal.GetHINSTANCE( _
' [Assembly].GetExecutingAss
End Sub
Protected Overrides Sub Finalize()
MyBase.Finalize()
If Not disposed Then
Dispose()
End If
End Sub
Public Sub CheckHooked()
If (Hooked()) Then
Debug.WriteLine("Keyboard hooked...")
Else
Debug.WriteLine("Keyboard hook failed: " & Err.LastDllError)
End If
End Sub
Public Function rCheckHooked() As Boolean
If (Hooked()) Then
Return True
Else
Return False
End If
End Function
Private Function Hooked()
Hooked = KeyboardHandle <> 0
End Function
End Class
Does it work when the form does not have focus?
ASKER
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.
ASKER
I am using a WithEvents and firing off events...this doesnt only work with forms does it?
Not my forte...but I'm pretty sure it has something to do with how services run in their own desktops:
http://support.microsoft.com/default.aspx?scid=kb;en-us;171890
http://support.microsoft.com/default.aspx?scid=kb;en-us;171890
ASKER
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.
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.
ASKER
Ok so the question I guess is how do I hook the service window?
Good question. As I said before though, "Not my forte..."
Someone will come along I'm sure and educate us both. =)
Someone will come along I'm sure and educate us both. =)
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.
http://msdn.microsoft.com/directx/sdk/
I still don't know how it will work with the service and desktops.
Let me know what you come up with.
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.
http://msdn.microsoft.com/directx/sdk/
I still don't know how it will work with the service and desktops.
Let me know what you come up with.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
That unfortunatly did not work. It did however allow me to use a msgbox which I wasn't able to get working before!