EDN Admin
Well-known member
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
View the full article
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<br/>
Imports System.Runtime.InteropServices<br/>
Imports System.Threading<br/>
Imports System.Windows.Forms<br/>
<br/>
Public Class KeyboardKeyEventListener<br/>
Implements IDisposable<br/>
<br/>
#Region " Members "<br/>
<br/>
Private Enum HookType As Integer<br/>
WH_JOURNALRECORD = 0<br/>
WH_JOURNALPLAYBACK = 1<br/>
WH_KEYBOARD = 2<br/>
WH_GETMESSAGE = 3<br/>
WH_CALLWNDPROC = 4<br/>
WH_CBT = 5<br/>
WH_SYSMSGFILTER = 6<br/>
WH_MOUSE = 7<br/>
WH_HARDWARE = 8<br/>
WH_DEBUG = 9<br/>
WH_SHELL = 10<br/>
WH_FOREGROUNDIDLE = 11<br/>
WH_CALLWNDPROCRET = 12<br/>
WH_KEYBOARD_LL = 13<br/>
WH_MOUSE_LL = 14<br/>
End Enum<br/>
<br/>
// The Code Indicating Low Level Keyboard Message<br/>
Private Const WH_KEYBOARD_LL As Integer = 13&<br/>
<br/>
// Low-Level Keyboard Constants<br/>
Private Const HC_ACTION As Integer = 0<br/>
Private Const LLKHF_EXTENDED As Integer = &H1<br/>
Private Const LLKHF_INJECTED As Integer = &H10<br/>
Private Const LLKHF_ALTDOWN As Integer = &H20<br/>
Private Const LLKHF_UP As Integer = &H80<br/>
<br/>
// KeyUp/KeyDown Constants<br/>
Private Const WM_KEYDOWN As Integer = &H100<br/>
Private Const WM_KEYUP As Integer = &H101<br/>
Private Const WM_SYSKEYDOWN As Integer = &H104<br/>
Private Const WM_SYSKEYUP As Integer = &H105<br/>
<br/>
// Variables<br/>
Private KeyboardHandle As Integer<br/>
Private Disposed As Boolean = False<br/>
<br/>
// MarshalAs required to keep GC from recovering our delegate and creating a null reference when Windows sends the key event.<br/>
<MarshalAs(UnmanagedType.FunctionPtr)> Private KeyboardHookProcedure As KeyboardHookDelegate = New KeyboardHookDelegate(AddressOf ProcessKeyboardMessage)<br/>
<br/>
#End Region<br/>
<br/>
#Region " Events "<br/>
<br/>
Public Event KeyDown(ByRef TheKeyArgs As Windows.Forms.KeyEventArgs)<br/>
Public Event KeyPress(ByRef TheKeyArgs As Windows.Forms.KeyEventArgs)<br/>
Public Event KeyUp(ByRef TheKeyArgs As Windows.Forms.KeyEventArgs)<br/>
<br/>
#End Region<br/>
<br/>
#Region " Delegates "<br/>
<br/>
Private Delegate Function KeyboardHookDelegate(ByVal Code As Integer, _<br/>
ByVal WParam As Integer, _<br/>
ByRef LParam As KBDLLHOOKSTRUCT) As Integer<br/>
<br/>
#End Region<br/>
<br/>
#Region " Function Declarations for user32.dll "<br/>
<br/>
Private Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Integer) As Integer<br/>
<br/>
Private Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Integer, _<br/>
ByVal lpfn As KeyboardHookDelegate, _<br/>
ByVal hmod As Integer, _<br/>
ByVal dwThreadId As Integer) As Integer<br/>
<br/>
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Integer) As Integer<br/>
<br/>
Private Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Integer, _<br/>
ByVal nCode As Integer, _<br/>
ByVal wParam As Integer, _<br/>
ByVal lParam As KBDLLHOOKSTRUCT) As Integer<br/>
<br/>
#End Region<br/>
<br/>
#Region " Structures "<br/>
<br/>
Private Structure KBDLLHOOKSTRUCT<br/>
Public vkCode As Integer<br/>
Public scanCode As Integer<br/>
Public flags As Integer<br/>
Public time As Integer<br/>
Public dwExtraInfo As Integer<br/>
End Structure<br/>
<br/>
#End Region<br/>
<br/>
#Region " Constructors, Destructors and Load Methods "<br/>
<br/>
Public Sub New()<br/>
<br/>
Try<br/>
<br/>
Catch ex As Exception<br/>
Throw ex<br/>
End Try<br/>
<br/>
End Sub<br/>
<br/>
Public Overridable Sub Dispose() Implements IDisposable.Dispose<br/>
<br/>
Try<br/>
If Not (Disposed = True) Then<br/>
Disposed = True<br/>
If Not (KeyboardHandle = 0) Then UnhookWindowsHookEx(KeyboardHandle)<br/>
Else<br/>
Throw New ObjectDisposedException("KeyboardHook")<br/>
End If<br/>
<br/>
Catch ex As Exception<br/>
Throw ex<br/>
End Try<br/>
<br/>
End Sub<br/>
<br/>
Protected Overrides Sub Finalize()<br/>
<br/>
Try<br/>
MyBase.Finalize()<br/>
<br/>
If Not Disposed Then<br/>
Dispose()<br/>
End If<br/>
<br/>
Catch ex As Exception<br/>
Throw ex<br/>
End Try<br/>
<br/>
End Sub<br/>
<br/>
#End Region<br/>
<br/>
#Region " Methods - Primary "<br/>
<br/>
Public Function StartListening() As Boolean<br/>
<br/>
Try<br/>
// The first argument to SetWindowsHookEx Sets the Type of Hook.<br/>
// The second argument is the tagged delegate that contains the address of our local callback method.<br/>
// The third argument is the handle (hWnd) of the application doing the hooking<br/>
// The fourth argument is the thread to listen on. Passing 0 indicates all threads.<br/>
<br/>
Console.WriteLine("KeyboardHandle = " + KeyboardHandle.ToString)<br/>
Console.WriteLine("WH_KEYBOARD_LL = " + WH_KEYBOARD_LL.ToString)<br/>
Console.WriteLine("KeyboardHookProcedure = " + KeyboardHookProcedure.ToString)<br/>
Console.WriteLine("Current Assembly Thread = " + Marshal.GetHINSTANCE([Assembly].GetExecutingAssembly.GetModules()(0)).ToInt32.ToString)<br/>
Console.WriteLine("")<br/>
<br/>
KeyboardHandle = SetWindowsHookEx(WH_KEYBOARD_LL, _<br/>
KeyboardHookProcedure, _<br/>
Marshal.GetHINSTANCE([Assembly].GetExecutingAssembly.GetModules()(0)).ToInt32, _<br/>
0)<br/>
<br/>
Console.WriteLine("KeyboardHandle = " + KeyboardHandle.ToString)<br/>
<br/>
If (KeyboardHandle = 0) Then<br/>
Return False<br/>
Else<br/>
Return True<br/>
End If<br/>
<br/>
Catch ex As Exception<br/>
Throw New Exception("Exception encountered in StartListening" + vbCrLf + ex.Message)<br/>
End Try<br/>
<br/>
End Function<br/>
<br/>
Public Function StopListening() As Boolean<br/>
<br/>
Try<br/>
If Not (KeyboardHandle = 0) Then UnhookWindowsHookEx(KeyboardHandle)<br/>
<br/>
Console.WriteLine(KeyboardHandle.ToString)<br/>
KeyboardHandle = 0<br/>
<br/>
Return True<br/>
<br/>
Catch ex As Exception<br/>
Throw ex<br/>
End Try<br/>
<br/>
End Function<br/>
<br/>
Private Function ProcessKeyboardMessage(ByVal Code As Integer, ByVal WParam As Integer, ByRef LParam As KBDLLHOOKSTRUCT) As Integer<br/>
<br/>
Try<br/>
Dim TheKeys As Windows.Forms.Keys = CType(LParam.vkCode, Keys)<br/>
Dim TheKeyArgs As Windows.Forms.KeyEventArgs = New Windows.Forms.KeyEventArgs(TheKeys)<br/>
<br/>
If WParam = WM_KEYDOWN Or WParam = WM_SYSKEYDOWN Then<br/>
RaiseEvent KeyDown(TheKeyArgs)<br/>
ElseIf WParam = WM_KEYUP Or WParam = WM_SYSKEYUP Then<br/>
RaiseEvent KeyUp(TheKeyArgs)<br/>
End If<br/>
<br/>
// Call the next hook in the hook chain and return the value<br/>
Return CallNextHookEx(KeyboardHandle, Code, WParam, LParam)<br/>
<br/>
Catch ex As Exception<br/>
Throw New Exception("Exception encountered in ProcessKeyStroke" + vbCrLf + vbCrLf + ex.Message)<br/>
End Try<br/>
<br/>
End Function<br/>
<br/>
#End Region<br/>
<br/>
#Region " Methods - Utility "<br/>
<br/>
#End Region<br/>
<br/>
End Class<br/>
<br/>
View the full article