SetWindowsHookEx works in Net 2 but not in Net 4
-
Monday, May 24, 2010 10:16 PM
I am developing an application that will engage a bar code scanner and will need to be listening for input even when it is not the active application or from a separate thread from within the application. I can get a handle to the hook when I set the project to compile for Net 2. When I set my project to compile for Net 4 I cannot get a handle from the SetWindowsHookEx. Same code, no changes at all. While obviously I can get a working DLL out of this it really is buggin the ____ out of me. Additionally, if one of my colegues needs to adjust this DLL in the future and does not know, they may compile it in Net 4. I also have no idea what will be the effects if I reference this DLL in another project that is compiled in Net 4. Lastly, I am worried that I may encounter more difficulties like this in the future if I cannot figure out what the difference is. Does anyone know why this is or what the solution might be?
Emory
<code>
Imports System.Reflection
Imports System.Runtime.InteropServices
Imports System.Threading
Imports System.Windows.Forms
Public Class KeyboardKeyEventListener
Implements IDisposable
#Region " Members "
Private Enum HookType As Integer
WH_JOURNALRECORD = 0
WH_JOURNALPLAYBACK = 1
WH_KEYBOARD = 2
WH_GETMESSAGE = 3
WH_CALLWNDPROC = 4
WH_CBT = 5
WH_SYSMSGFILTER = 6
WH_MOUSE = 7
WH_HARDWARE = 8
WH_DEBUG = 9
WH_SHELL = 10
WH_FOREGROUNDIDLE = 11
WH_CALLWNDPROCRET = 12
WH_KEYBOARD_LL = 13
WH_MOUSE_LL = 14
End Enum
'// The Code Indicating Low Level Keyboard Message
Private Const WH_KEYBOARD_LL As Integer = 13&
'// 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 As Integer = &H100
Private Const WM_KEYUP As Integer = &H101
Private Const WM_SYSKEYDOWN As Integer = &H104
Private Const WM_SYSKEYUP As Integer = &H105
'// Variables
Private KeyboardHandle As Integer
Private Disposed As Boolean = False
'// MarshalAs required to keep GC from recovering our delegate and creating a null reference when Windows sends the key event.
<MarshalAs(UnmanagedType.FunctionPtr)> Private KeyboardHookProcedure As KeyboardHookDelegate = New KeyboardHookDelegate(AddressOf ProcessKeyboardMessage)
#End Region
#Region " Events "
Public Event KeyDown(ByRef TheKeyArgs As Windows.Forms.KeyEventArgs)
Public Event KeyPress(ByRef TheKeyArgs As Windows.Forms.KeyEventArgs)
Public Event KeyUp(ByRef TheKeyArgs As Windows.Forms.KeyEventArgs)
#End Region
#Region " Delegates "
Private Delegate Function KeyboardHookDelegate(ByVal Code As Integer, _
ByVal WParam As Integer, _
ByRef LParam As KBDLLHOOKSTRUCT) As Integer
#End Region
#Region " Function Declarations for 'user32.dll' "
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
#End Region
#Region " Structures "
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
#End Region
#Region " Constructors, Destructors and Load Methods "
Public Sub New()
Try
Catch ex As Exception
Throw ex
End Try
End Sub
Public Overridable Sub Dispose() Implements IDisposable.Dispose
Try
If Not (Disposed = True) Then
Disposed = True
If Not (KeyboardHandle = 0) Then UnhookWindowsHookEx(KeyboardHandle)
Else
Throw New ObjectDisposedException("KeyboardHook")
End If
Catch ex As Exception
Throw ex
End Try
End Sub
Protected Overrides Sub Finalize()
Try
MyBase.Finalize()
If Not Disposed Then
Dispose()
End If
Catch ex As Exception
Throw ex
End Try
End Sub
#End Region
#Region " Methods - Primary "
Public Function StartListening() As Boolean
Try
'// The first argument to SetWindowsHookEx Sets the Type of Hook.
'// The second argument is the tagged delegate that contains the address of our local callback method.
'// The third argument is the handle (hWnd) of the application doing the hooking
'// The fourth argument is the thread to listen on. Passing 0 indicates all threads.
Console.WriteLine("KeyboardHandle = " + KeyboardHandle.ToString)
Console.WriteLine("WH_KEYBOARD_LL = " + WH_KEYBOARD_LL.ToString)
Console.WriteLine("KeyboardHookProcedure = " + KeyboardHookProcedure.ToString)
Console.WriteLine("Current Assembly Thread = " + Marshal.GetHINSTANCE([Assembly].GetExecutingAssembly.GetModules()(0)).ToInt32.ToString)
Console.WriteLine("")
KeyboardHandle = SetWindowsHookEx(WH_KEYBOARD_LL, _
KeyboardHookProcedure, _
Marshal.GetHINSTANCE([Assembly].GetExecutingAssembly.GetModules()(0)).ToInt32, _
0)
Console.WriteLine("KeyboardHandle = " + KeyboardHandle.ToString)
If (KeyboardHandle = 0) Then
Return False
Else
Return True
End If
Catch ex As Exception
Throw New Exception("Exception encountered in StartListening" + vbCrLf + ex.Message)
End Try
End Function
Public Function StopListening() As Boolean
Try
If Not (KeyboardHandle = 0) Then UnhookWindowsHookEx(KeyboardHandle)
Console.WriteLine(KeyboardHandle.ToString)
KeyboardHandle = 0
Return True
Catch ex As Exception
Throw ex
End Try
End Function
Private Function ProcessKeyboardMessage(ByVal Code As Integer, ByVal WParam As Integer, ByRef LParam As KBDLLHOOKSTRUCT) As Integer
Try
Dim TheKeys As Windows.Forms.Keys = CType(LParam.vkCode, Keys)
Dim TheKeyArgs As Windows.Forms.KeyEventArgs = New Windows.Forms.KeyEventArgs(TheKeys)
If WParam = WM_KEYDOWN Or WParam = WM_SYSKEYDOWN Then
RaiseEvent KeyDown(TheKeyArgs)
ElseIf WParam = WM_KEYUP Or WParam = WM_SYSKEYUP Then
RaiseEvent KeyUp(TheKeyArgs)
End If
'// Call the next hook in the hook chain and return the value
Return CallNextHookEx(KeyboardHandle, Code, WParam, LParam)
Catch ex As Exception
Throw New Exception("Exception encountered in ProcessKeyStroke" + vbCrLf + vbCrLf + ex.Message)
End Try
End Function
#End Region
#Region " Methods - Utility "
#End Region
End Class
</code>- Edited by EmoryH Monday, May 24, 2010 10:20 PM Include code
All Replies
-
Tuesday, June 15, 2010 9:52 PM
Having same issue in .Net 4.0 call failing. Any suggestions? Pointers?
Thanks in advance.
noorbakhsh حميد نوربخش -
Wednesday, June 16, 2010 5:31 AM
If it fails in 4.0 which goes exactly in the same way as in 2.0 and that is sure, then it is probably a bug.
That is the website for bugs.
Success
Cor -
Wednesday, June 16, 2010 3:02 PM
Cor,
Thanks for your reply. I changed my code and now it works. I noticed the following :
hMod [in]
HINSTANCE
A handle to the DLL containing the hook procedure pointed to by the lpfn parameter. The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by the current process and if the hook procedure is within the code associated with the current process.
Notice NULL setting for running this from the same process -> So in my call I now pass a
IntPtr.Zero (In my .Net 4.0 code)
instead of:
Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]) (This is the parameter passed in my .Net 2.0 code)and it works since the thread is created by the current process. I am not sure if this was like this before or not, but now it is working.
Edit: Above works great in Windows 7, but not in Windows XP, setting it to System.Diagnostics.Process.GetCurrentProcess().MainModule.BaseAddress will work in both (thanks to Patrick Klug for suggesting this)
noorbakhsh حميد نوربخش- Proposed As Answer by noorbakhsh Tuesday, October 12, 2010 3:15 PM
-
Wednesday, September 22, 2010 11:56 PM
Thanks for the edit to get it to work in both XP and Win 7.
Had issues with this in the ribbon control : http://ribbon.codeplex.com/
-
Thursday, April 04, 2013 1:54 PM

