diff --git a/InputHelper Library/InputHelper Library/InputHelper Library.vbproj b/InputHelper Library/InputHelper Library/InputHelper Library.vbproj index 0387150..9237066 100644 --- a/InputHelper Library/InputHelper Library/InputHelper Library.vbproj +++ b/InputHelper Library/InputHelper Library/InputHelper Library.vbproj @@ -9,7 +9,7 @@ {F29BA8AA-4C2E-48AD-8184-7D8A5FCA96A1} Library - InputHelperLibrary + InputHelperLib InputHelper 512 Windows diff --git a/InputHelper Library/InputHelper Library/InputHelper.vb b/InputHelper Library/InputHelper Library/InputHelper.vb index 6b56d80..5eba5d7 100644 --- a/InputHelper Library/InputHelper Library/InputHelper.vb +++ b/InputHelper Library/InputHelper Library/InputHelper.vb @@ -35,6 +35,8 @@ Imports System.Runtime.InteropServices Imports System.Windows.Forms Imports System.Drawing Imports System.Text +Imports System.Reflection +Imports System.ComponentModel ''' ''' A static class for handling and simulating mouse and keyboard input. @@ -130,15 +132,576 @@ Public NotInheritable Class InputHelper #End Region #Region "Enumerations" - Public Enum ModifierKey As Integer - Control = 0 - Shift - Alt + _ + Public Enum ModifierKeys As Integer + ''' + ''' No modifiers specified. + ''' + ''' + None = 0 + + ''' + ''' The CTRL modifier key. + ''' + ''' + Control = 1 + + ''' + ''' The SHIFT modifier key. + ''' + ''' + Shift = 2 + + ''' + ''' The ALT modifier key. + ''' + ''' + Alt = 4 + + ''' + ''' The Windows modifier key. + ''' + ''' + Windows = 8 End Enum #End Region #Region "Subclasses" +#Region "Hooks" + ''' + ''' A static class for dealing with mouse and keyboard hooks. + ''' + ''' + Public NotInheritable Class Hooks + Private Sub New() + End Sub + +#Region "EventArgs" + Public Class KeyboardHookEventArgs + Inherits EventArgs + + Private _extended As Boolean + Private _keyCode As Keys + Private _keyState As KeyState + Private _modifiers As ModifierKeys + Private _scanCode As UInteger + + ''' + ''' Gets or sets a boolean value indicating whether the keystroke should be blocked from reaching any windows. + ''' + ''' + Public Property Block As Boolean = False + + ''' + ''' Gets a boolean value indicating whether the keystroke message originated from one of the additional keys on the enhanced keyboard + ''' (see: https://msdn.microsoft.com/en-us/library/windows/desktop/ms646267(v=vs.85).aspx#_win32_Keystroke_Message_Flags). + ''' + ''' + Public ReadOnly Property Extended As Boolean + Get + Return _extended + End Get + End Property + + ''' + ''' Gets the keyboard code of the key that generated the keystroke. + ''' + ''' + Public ReadOnly Property KeyCode As Keys + Get + Return _keyCode + End Get + End Property + + ''' + ''' Gets the current state of the key that generated the keystroke. + ''' + ''' + Public ReadOnly Property KeyState As KeyState + Get + Return _keyState + End Get + End Property + + ''' + ''' Gets the modifier keys that was pressed in combination with the keystroke. + ''' + ''' + Public ReadOnly Property Modifiers As ModifierKeys + Get + Return _modifiers + End Get + End Property + + ''' + ''' Gets the hardware scan code of the key that generated the keystroke. + ''' + ''' + Public ReadOnly Property ScanCode As UInteger + Get + Return _scanCode + End Get + End Property + + Public Overrides Function ToString() As String + Return String.Format("{{KeyCode: {0}, ScanCode: {1}, Extended: {2}, KeyState: {3}, Modifiers: {4}}}", _ + Me.KeyCode, Me.ScanCode, Me.Extended, Me.KeyState, Me.Modifiers) + End Function + + ''' + ''' Initializes a new instance of the KeyboardHookEventArgs class. + ''' + ''' The keyboard code of the key that generated the keystroke. + ''' The hardware scan code of the key that generated the keystroke. + ''' Whether the keystroke message originated from one of the additional keys on the enhanced keyboard + ''' (see: https://msdn.microsoft.com/en-us/library/windows/desktop/ms646267(v=vs.85).aspx#_win32_Keystroke_Message_Flags). + ''' The current state of the key that generated the keystroke. + ''' The modifier keys that was pressed in combination with the keystroke. + ''' + Public Sub New(ByVal KeyCode As Keys, _ + ByVal ScanCode As UInteger, _ + ByVal Extended As Boolean, _ + ByVal KeyState As KeyState, _ + ByVal Modifiers As ModifierKeys) + Me._keyCode = KeyCode + Me._scanCode = ScanCode + Me._extended = Extended + Me._keyState = KeyState + Me._modifiers = Modifiers + End Sub + End Class + + Public Class MouseHookEventArgs + Inherits EventArgs + + Private _button As MouseButtons + Private _buttonState As KeyState + Private _delta As Integer + Private _doubleClick As Boolean + Private _location As Point + Private _scrollDirection As ScrollDirection + + ''' + ''' Gets or sets a boolean value indicating whether the mouse event should be blocked from reaching any windows. + ''' + ''' + Public Property Block As Boolean = False + + ''' + ''' Gets which mouse button was pressed or released. + ''' + ''' + Public ReadOnly Property Button As MouseButtons + Get + Return _button + End Get + End Property + + ''' + ''' Gets the current state of the button that generated the mouse event. + ''' + ''' + Public ReadOnly Property ButtonState As KeyState + Get + Return _buttonState + End Get + End Property + + ''' + ''' Gets a signed count of the number of detents the mouse wheel has rotated. A detent is one notch of the mouse wheel. + ''' + ''' + Public ReadOnly Property Delta As Integer + Get + Return _delta + End Get + End Property + + ''' + ''' Gets a boolean value indicating whether the event was caused by a double click. + ''' + ''' + Public ReadOnly Property DoubleClick As Boolean + Get + Return _doubleClick + End Get + End Property + + ''' + ''' Gets the location of the mouse (in screen coordinates). + ''' + ''' + Public ReadOnly Property Location As Point + Get + Return _location + End Get + End Property + + ''' + ''' Gets which direction the mouse wheel was scrolled in. + ''' + ''' + Public ReadOnly Property ScrollDirection As ScrollDirection + Get + Return _scrollDirection + End Get + End Property + + Public Overrides Function ToString() As String + Return String.Format("{{Button: {0}, State: {1}, DoubleClick: {2}, Location: {3}, Scroll: {4}, Delta: {5}}}", _ + Me.Button, Me.ButtonState, Me.DoubleClick, Me.Location, Me.ScrollDirection, Me.Delta) + End Function + + ''' + ''' Initializes a new instance of the MouseHookEventArgs class. + ''' + ''' Which mouse button was pressed or released. + ''' The current state of the button that generated the mouse event. + ''' Whether the event was caused by a double click. + ''' The location of the mouse (in screen coordinates). + ''' Which direction the mouse wheel was scrolled in. + ''' A signed count of the number of detents the mouse wheel has rotated. + ''' + Public Sub New(ByVal Button As MouseButtons, _ + ByVal ButtonState As KeyState, _ + ByVal DoubleClick As Boolean, _ + ByVal Location As Point, _ + ByVal ScrollDirection As ScrollDirection, _ + ByVal Delta As Integer) + Me._button = Button + Me._buttonState = ButtonState + Me._doubleClick = DoubleClick + Me._location = Location + Me._scrollDirection = ScrollDirection + Me._delta = Delta + End Sub + End Class +#End Region + +#Region "Keyboard hook" + ''' + ''' A global low-level keyboard hook that raises events when a key is pressed or released. + ''' + ''' + Public Class KeyboardHook + Implements IDisposable + + ''' + ''' Occurs when a key is pressed or held down. + ''' + ''' + Public Event KeyDown As EventHandler(Of KeyboardHookEventArgs) + + ''' + ''' Occurs when a key is released. + ''' + ''' + Public Event KeyUp As EventHandler(Of KeyboardHookEventArgs) + + Private hHook As IntPtr = IntPtr.Zero + Private HookProcedureDelegate As New NativeMethods.LowLevelKeyboardProc(AddressOf HookCallback) + + Private Modifiers As ModifierKeys = ModifierKeys.None + + Private Function HookCallback(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr + Dim Block As Boolean = False + + If nCode >= NativeMethods.HookCode.HC_ACTION AndAlso _ + (wParam = NativeMethods.KeyMessage.WM_KEYDOWN OrElse _ + wParam = NativeMethods.KeyMessage.WM_KEYUP OrElse _ + wParam = NativeMethods.KeyMessage.WM_SYSKEYDOWN OrElse _ + wParam = NativeMethods.KeyMessage.WM_SYSKEYUP) Then + + Dim KeystrokeInfo As NativeMethods.KBDLLHOOKSTRUCT = _ + CType(Marshal.PtrToStructure(lParam, GetType(NativeMethods.KBDLLHOOKSTRUCT)), NativeMethods.KBDLLHOOKSTRUCT) + + Select Case wParam + + Case NativeMethods.KeyMessage.WM_KEYDOWN, NativeMethods.KeyMessage.WM_SYSKEYDOWN + Dim HookEventArgs As KeyboardHookEventArgs = Me.CreateEventArgs(True, KeystrokeInfo) + + If InputHelper.IsModifier(HookEventArgs.KeyCode, ModifierKeys.Control) = True Then Me.Modifiers = Me.Modifiers Or ModifierKeys.Control + If InputHelper.IsModifier(HookEventArgs.KeyCode, ModifierKeys.Shift) = True Then Me.Modifiers = Me.Modifiers Or ModifierKeys.Shift + If InputHelper.IsModifier(HookEventArgs.KeyCode, ModifierKeys.Alt) = True Then Me.Modifiers = Me.Modifiers Or ModifierKeys.Alt + If InputHelper.IsModifier(HookEventArgs.KeyCode, ModifierKeys.Windows) = True Then Me.Modifiers = Me.Modifiers Or ModifierKeys.Windows + + RaiseEvent KeyDown(Me, HookEventArgs) + Block = HookEventArgs.Block + + Case NativeMethods.KeyMessage.WM_KEYUP, NativeMethods.KeyMessage.WM_SYSKEYUP + Dim KeyCode As Keys = CType(KeystrokeInfo.vkCode And Integer.MaxValue, Keys) + + 'Must be done before creating the HookEventArgs during KeyUp. + If InputHelper.IsModifier(KeyCode, ModifierKeys.Control) = True Then Me.Modifiers = Me.Modifiers And Not ModifierKeys.Control + If InputHelper.IsModifier(KeyCode, ModifierKeys.Shift) = True Then Me.Modifiers = Me.Modifiers And Not ModifierKeys.Shift + If InputHelper.IsModifier(KeyCode, ModifierKeys.Alt) = True Then Me.Modifiers = Me.Modifiers And Not ModifierKeys.Alt + If InputHelper.IsModifier(KeyCode, ModifierKeys.Windows) = True Then Me.Modifiers = Me.Modifiers And Not ModifierKeys.Windows + + Dim HookEventArgs As KeyboardHookEventArgs = Me.CreateEventArgs(False, KeystrokeInfo) + + RaiseEvent KeyUp(Me, HookEventArgs) + Block = HookEventArgs.Block + + End Select + End If + + Return If(Block, New IntPtr(1), NativeMethods.CallNextHookEx(hHook, nCode, wParam, lParam)) + End Function + + Private Function CreateEventArgs(ByVal KeyDown As Boolean, ByVal KeystrokeInfo As NativeMethods.KBDLLHOOKSTRUCT) As KeyboardHookEventArgs + Dim KeyCode As Keys = CType(KeystrokeInfo.vkCode And Integer.MaxValue, Keys) + Dim ScanCode As UInteger = KeystrokeInfo.scanCode + Dim Extended As Boolean = (KeystrokeInfo.flags And NativeMethods.LowLevelKeyboardHookFlags.LLKHF_EXTENDED) = NativeMethods.LowLevelKeyboardHookFlags.LLKHF_EXTENDED + Dim AltDown As Boolean = (KeystrokeInfo.flags And NativeMethods.LowLevelKeyboardHookFlags.LLKHF_ALTDOWN) = NativeMethods.LowLevelKeyboardHookFlags.LLKHF_ALTDOWN + + If AltDown = True _ + AndAlso InputHelper.IsModifier(KeyCode, ModifierKeys.Alt) = False _ + AndAlso (Me.Modifiers And ModifierKeys.Alt) <> ModifierKeys.Alt Then + + Me.Modifiers = Me.Modifiers Or ModifierKeys.Alt + End If + + Return New KeyboardHookEventArgs(KeyCode, ScanCode, Extended, If(KeyDown, KeyState.Down, KeyState.Up), Me.Modifiers) + End Function + + Public Sub New() + hHook = NativeMethods.SetWindowsHookEx(NativeMethods.HookType.WH_KEYBOARD_LL, HookProcedureDelegate, NativeMethods.GetModuleHandle(Nothing), 0) + If hHook = IntPtr.Zero Then + Dim Win32Error As Integer = Marshal.GetLastWin32Error() + Throw New Win32Exception(Win32Error, "Failed to create keyboard hook! (" & Win32Error & ")") + End If + End Sub + +#Region "IDisposable Support" + Private disposedValue As Boolean ' To detect redundant calls + + ' IDisposable + Protected Overridable Sub Dispose(disposing As Boolean) + If Not Me.disposedValue Then + If disposing Then + ' TODO: dispose managed state (managed objects). + End If + + ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below. + ' TODO: set large fields to null. + If hHook <> IntPtr.Zero Then NativeMethods.UnhookWindowsHookEx(hHook) + End If + Me.disposedValue = True + End Sub + + Protected Overrides Sub Finalize() + ' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above. + Dispose(False) + MyBase.Finalize() + End Sub + + ' This code added by Visual Basic to correctly implement the disposable pattern. + Public Sub Dispose() Implements IDisposable.Dispose + ' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above. + Dispose(True) + GC.SuppressFinalize(Me) + End Sub +#End Region + + End Class +#End Region + +#Region "Mouse hook" + ''' + ''' A global low-level mouse hook that raises events when a mouse event occurs. + ''' + ''' + Public Class MouseHook + Implements IDisposable + + ''' + ''' Occurs when a mouse button is pressed or held down. + ''' + ''' + Public Event MouseDown As EventHandler(Of MouseHookEventArgs) + + ''' + ''' Occurs when a mouse button is released. + ''' + ''' + Public Event MouseUp As EventHandler(Of MouseHookEventArgs) + + ''' + ''' Occurs when the mouse moves. + ''' + ''' + Public Event MouseMove As EventHandler(Of MouseHookEventArgs) + + ''' + ''' Occurs when the mouse wheel is scrolled. + ''' + ''' + Public Event MouseWheel As EventHandler(Of MouseHookEventArgs) + + Private hHook As IntPtr = IntPtr.Zero + Private HookProcedureDelegate As New NativeMethods.LowLevelMouseProc(AddressOf HookCallback) + + Private LeftClickTimeStamp As Integer = 0 + Private MiddleClickTimeStamp As Integer = 0 + Private RightClickTimeStamp As Integer = 0 + + Private Function HookCallback(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr + Dim Block As Boolean = False + + If nCode >= NativeMethods.HookCode.HC_ACTION AndAlso _ + (wParam = NativeMethods.MouseMessage.WM_LBUTTONDOWN OrElse _ + wParam = NativeMethods.MouseMessage.WM_LBUTTONUP OrElse _ + wParam = NativeMethods.MouseMessage.WM_MBUTTONDOWN OrElse _ + wParam = NativeMethods.MouseMessage.WM_MBUTTONUP OrElse _ + wParam = NativeMethods.MouseMessage.WM_RBUTTONDOWN OrElse _ + wParam = NativeMethods.MouseMessage.WM_RBUTTONUP OrElse _ + wParam = NativeMethods.MouseMessage.WM_MOUSEWHEEL OrElse _ + wParam = NativeMethods.MouseMessage.WM_MOUSEHWHEEL OrElse _ + wParam = NativeMethods.MouseMessage.WM_MOUSEMOVE) Then + + Dim MouseEventInfo As NativeMethods.MSLLHOOKSTRUCT = _ + CType(Marshal.PtrToStructure(lParam, GetType(NativeMethods.MSLLHOOKSTRUCT)), NativeMethods.MSLLHOOKSTRUCT) + + Select Case wParam + Case NativeMethods.MouseMessage.WM_LBUTTONDOWN + Dim DoubleClick As Boolean = (Environment.TickCount - LeftClickTimeStamp) <= NativeMethods.GetDoubleClickTime() + LeftClickTimeStamp = Environment.TickCount + + Dim HookEventArgs As New MouseHookEventArgs(MouseButtons.Left, KeyState.Down, DoubleClick, _ + New Point(MouseEventInfo.pt.x, MouseEventInfo.pt.y), ScrollDirection.None, 0) + RaiseEvent MouseDown(Me, HookEventArgs) + Block = HookEventArgs.Block + + + Case NativeMethods.MouseMessage.WM_LBUTTONUP + Dim HookEventArgs As New MouseHookEventArgs(MouseButtons.Left, KeyState.Up, False, _ + New Point(MouseEventInfo.pt.x, MouseEventInfo.pt.y), ScrollDirection.None, 0) + RaiseEvent MouseUp(Me, HookEventArgs) + Block = HookEventArgs.Block + + + Case NativeMethods.MouseMessage.WM_MBUTTONDOWN + Dim DoubleClick As Boolean = (Environment.TickCount - MiddleClickTimeStamp) <= NativeMethods.GetDoubleClickTime() + MiddleClickTimeStamp = Environment.TickCount + + Dim HookEventArgs As New MouseHookEventArgs(MouseButtons.Middle, KeyState.Down, DoubleClick, _ + New Point(MouseEventInfo.pt.x, MouseEventInfo.pt.y), ScrollDirection.None, 0) + RaiseEvent MouseDown(Me, HookEventArgs) + Block = HookEventArgs.Block + + + Case NativeMethods.MouseMessage.WM_MBUTTONUP + Dim HookEventArgs As New MouseHookEventArgs(MouseButtons.Middle, KeyState.Up, False, _ + New Point(MouseEventInfo.pt.x, MouseEventInfo.pt.y), ScrollDirection.None, 0) + RaiseEvent MouseUp(Me, HookEventArgs) + Block = HookEventArgs.Block + + + Case NativeMethods.MouseMessage.WM_RBUTTONDOWN + Dim DoubleClick As Boolean = (Environment.TickCount - RightClickTimeStamp) <= NativeMethods.GetDoubleClickTime() + RightClickTimeStamp = Environment.TickCount + + Dim HookEventArgs As New MouseHookEventArgs(MouseButtons.Right, KeyState.Down, DoubleClick, _ + New Point(MouseEventInfo.pt.x, MouseEventInfo.pt.y), ScrollDirection.None, 0) + RaiseEvent MouseDown(Me, HookEventArgs) + Block = HookEventArgs.Block + + + Case NativeMethods.MouseMessage.WM_RBUTTONUP + Dim HookEventArgs As New MouseHookEventArgs(MouseButtons.Right, KeyState.Up, False, _ + New Point(MouseEventInfo.pt.x, MouseEventInfo.pt.y), ScrollDirection.None, 0) + RaiseEvent MouseUp(Me, HookEventArgs) + Block = HookEventArgs.Block + + + Case NativeMethods.MouseMessage.WM_MOUSEWHEEL + Dim Delta As Integer = New NativeMethods.SignedDWORD(MouseEventInfo.mouseData).High + Dim HookEventArgs As New MouseHookEventArgs(MouseButtons.None, KeyState.Up, False, _ + New Point(MouseEventInfo.pt.x, MouseEventInfo.pt.y), ScrollDirection.Vertical, Delta) + RaiseEvent MouseWheel(Me, HookEventArgs) + Block = HookEventArgs.Block + + + Case NativeMethods.MouseMessage.WM_MOUSEHWHEEL + Dim Delta As Integer = New NativeMethods.SignedDWORD(MouseEventInfo.mouseData).High + Dim HookEventArgs As New MouseHookEventArgs(MouseButtons.None, KeyState.Up, False, _ + New Point(MouseEventInfo.pt.x, MouseEventInfo.pt.y), ScrollDirection.Horizontal, Delta) + RaiseEvent MouseWheel(Me, HookEventArgs) + Block = HookEventArgs.Block + + + Case NativeMethods.MouseMessage.WM_MOUSEMOVE + Dim HookEventArgs As New MouseHookEventArgs(MouseButtons.None, KeyState.Up, False, _ + New Point(MouseEventInfo.pt.x, MouseEventInfo.pt.y), ScrollDirection.None, 0) + RaiseEvent MouseMove(Me, HookEventArgs) + Block = HookEventArgs.Block + + + End Select + End If + + Return If(Block, New IntPtr(1), NativeMethods.CallNextHookEx(hHook, nCode, wParam, lParam)) + End Function + + Public Sub New() + hHook = NativeMethods.SetWindowsHookEx(NativeMethods.HookType.WH_MOUSE_LL, HookProcedureDelegate, NativeMethods.GetModuleHandle(Nothing), 0) + If hHook = IntPtr.Zero Then + Dim Win32Error As Integer = Marshal.GetLastWin32Error() + Throw New Win32Exception(Win32Error, "Failed to create mouse hook! (" & Win32Error & ")") + End If + End Sub + +#Region "IDisposable Support" + Private disposedValue As Boolean ' To detect redundant calls + + ' IDisposable + Protected Overridable Sub Dispose(disposing As Boolean) + If Not Me.disposedValue Then + If disposing Then + ' TODO: dispose managed state (managed objects). + End If + + ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below. + ' TODO: set large fields to null. + If hHook <> IntPtr.Zero Then NativeMethods.UnhookWindowsHookEx(hHook) + End If + Me.disposedValue = True + End Sub + + Protected Overrides Sub Finalize() + ' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above. + Dispose(False) + MyBase.Finalize() + End Sub + + ' This code added by Visual Basic to correctly implement the disposable pattern. + Public Sub Dispose() Implements IDisposable.Dispose + ' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above. + Dispose(True) + GC.SuppressFinalize(Me) + End Sub +#End Region + + End Class +#End Region + +#Region "Enumerations" + Public Enum KeyState As Integer + Up = 0 + Down = 1 + End Enum + + Public Enum ScrollDirection As Integer + None = 0 + Vertical = 1 + Horizontal = 2 + End Enum +#End Region + + End Class +#End Region + #Region "Keyboard" ''' ''' A static class for handling and simulating physical keyboard input. @@ -544,20 +1107,23 @@ Public NotInheritable Class InputHelper ''' ''' The modifier to check. ''' - Public Shared Function IsModifierDown(ByVal Modifier As ModifierKey) As Boolean + Public Shared Function IsModifierDown(ByVal Modifier As ModifierKeys) As Boolean Select Case Modifier - Case ModifierKey.Control + Case ModifierKeys.Control Return WindowMessages.IsKeyDown(Keys.ControlKey) OrElse _ WindowMessages.IsKeyDown(Keys.LControlKey) OrElse _ WindowMessages.IsKeyDown(Keys.RControlKey) - Case ModifierKey.Shift + Case ModifierKeys.Shift Return WindowMessages.IsKeyDown(Keys.ShiftKey) OrElse _ WindowMessages.IsKeyDown(Keys.LShiftKey) OrElse _ WindowMessages.IsKeyDown(Keys.RShiftKey) - Case ModifierKey.Alt + Case ModifierKeys.Alt Return WindowMessages.IsKeyDown(Keys.Menu) OrElse _ WindowMessages.IsKeyDown(Keys.LMenu) OrElse _ WindowMessages.IsKeyDown(Keys.RMenu) + Case ModifierKeys.Windows + Return WindowMessages.IsKeyDown(Keys.LWin) OrElse _ + WindowMessages.IsKeyDown(Keys.RWin) End Select Throw New ArgumentOutOfRangeException("Modifier", CType(Modifier, Integer) & " is not a valid modifier key!") End Function @@ -585,14 +1151,14 @@ Public NotInheritable Class InputHelper ''' Public Shared Sub SendMouseClick(ByVal Button As MouseButtons, ByVal Location As Point, ByVal MouseDown As Boolean, Optional ByVal SendAsynchronously As Boolean = False) Dim hWnd As IntPtr = NativeMethods.WindowFromPoint(New NativeMethods.NATIVEPOINT(Location.X, Location.Y)) 'Get the window at the specified click point. - Dim ButtonMessage As NativeMethods.MouseButtonMessage 'A variable holding which Window Message to use. + Dim ButtonMessage As NativeMethods.MouseMessage 'A variable holding which Window Message to use. Select Case Button 'Set the appropriate mouse button Window Message. - Case MouseButtons.Left : ButtonMessage = If(MouseDown, NativeMethods.MouseButtonMessage.WM_LBUTTONDOWN, NativeMethods.MouseButtonMessage.WM_LBUTTONUP) - Case MouseButtons.Right : ButtonMessage = If(MouseDown, NativeMethods.MouseButtonMessage.WM_RBUTTONDOWN, NativeMethods.MouseButtonMessage.WM_RBUTTONUP) - Case MouseButtons.Middle : ButtonMessage = If(MouseDown, NativeMethods.MouseButtonMessage.WM_MBUTTONDOWN, NativeMethods.MouseButtonMessage.WM_MBUTTONUP) + Case MouseButtons.Left : ButtonMessage = If(MouseDown, NativeMethods.MouseMessage.WM_LBUTTONDOWN, NativeMethods.MouseMessage.WM_LBUTTONUP) + Case MouseButtons.Right : ButtonMessage = If(MouseDown, NativeMethods.MouseMessage.WM_RBUTTONDOWN, NativeMethods.MouseMessage.WM_RBUTTONUP) + Case MouseButtons.Middle : ButtonMessage = If(MouseDown, NativeMethods.MouseMessage.WM_MBUTTONDOWN, NativeMethods.MouseMessage.WM_MBUTTONUP) Case MouseButtons.XButton1, MouseButtons.XButton2 - ButtonMessage = NativeMethods.MouseButtonMessage.WM_XBUTTONDOWN + ButtonMessage = NativeMethods.MouseMessage.WM_XBUTTONDOWN Case Else Throw New ArgumentException("Invalid mouse button " & Button.ToString() & "!", "Button") End Select @@ -605,17 +1171,56 @@ Public NotInheritable Class InputHelper End If Dim wParam As IntPtr = IntPtr.Zero 'Used to specify which X button was clicked (if any). - Dim lParam As IntPtr = WindowMessages.CreateLWParam(ClickPoint.X, ClickPoint.Y) 'Click point. + Dim lParam As IntPtr = New NativeMethods.DWORD(ClickPoint.x, ClickPoint.y) 'Click point. If Button = MouseButtons.XButton1 OrElse _ Button = MouseButtons.XButton2 Then - wParam = WindowMessages.CreateLWParam(0, Button / MouseButtons.XButton1) 'Set the correct XButton. + wParam = New NativeMethods.DWORD(0, Button / MouseButtons.XButton1) 'Set the correct XButton. End If InputHelper.WindowMessages.SendMessage(hWnd, ButtonMessage, wParam, lParam, SendAsynchronously) End Sub #End Region +#Region "GetActiveWindow()" + ''' + ''' Gets the active top-level window. + ''' + ''' + Public Shared Function GetActiveWindow() As IntPtr + Return NativeMethods.GetForegroundWindow() + End Function +#End Region + +#Region "GetAbsoluteActiveWindow()" + ''' + ''' Gets the (absolute) active top-level window or child window (apart from GetActiveWindow() which will only get the active top-level window). + ''' + ''' + Public Shared Function GetAbsoluteActiveWindow() As IntPtr + Dim CurrentThreadID As UInteger = NativeMethods.GetCurrentThreadId() + Dim ActiveWindow As IntPtr = NativeMethods.GetForegroundWindow() + Dim ActiveThread As UInteger = NativeMethods.GetWindowThreadProcessId(ActiveWindow, Nothing) + + If ActiveThread = 0 Then _ + Return ActiveWindow + + If NativeMethods.AttachThreadInput(CurrentThreadID, ActiveThread, True) = False Then _ + Return ActiveWindow + + Dim AbsoluteActiveWindow As IntPtr = NativeMethods.GetFocus() + Dim DetachAttempts As Integer = 0 + + While NativeMethods.AttachThreadInput(CurrentThreadID, ActiveThread, False) = False + DetachAttempts += 1 + If DetachAttempts >= 10 Then Exit While + Threading.Thread.Sleep(1) + End While + + Return AbsoluteActiveWindow + End Function +#End Region + #End Region #Region "Internal methods" @@ -642,8 +1247,8 @@ Public NotInheritable Class InputHelper Dim CharacterConversionResult As Integer = -1 Dim IsDeadChar As Boolean = (CharCode And (1 << 31)) = (CType(1, UInteger) << 31) 'If the most significant bit is set the key is a dead char. - Dim IsAltDown As Boolean = WindowMessages.IsModifierDown(ModifierKey.Alt) - Dim IsAlt As Boolean = InputHelper.IsModifier(Key, ModifierKey.Alt) + Dim IsAltDown As Boolean = WindowMessages.IsModifierDown(ModifierKeys.Alt) + Dim IsAlt As Boolean = InputHelper.IsModifier(Key, ModifierKeys.Alt) Dim AltUp As Boolean = (IsAlt = True AndAlso IsAltDown = True AndAlso KeyDown = False) Dim IsNumpadNumber As Boolean = (KeyCode >= CType(Keys.NumPad0, UInteger) AndAlso KeyCode <= CType(Keys.NumPad9, UInteger)) @@ -855,47 +1460,6 @@ Public NotInheritable Class InputHelper End Function #End Region -#Region "GetAbsoluteActiveWindow()" - ''' - ''' Gets the absolute active window or child window. - ''' - ''' - Private Shared Function GetAbsoluteActiveWindow() As IntPtr - Dim CurrentThreadID As UInteger = NativeMethods.GetCurrentThreadId() - Dim ActiveWindow As IntPtr = NativeMethods.GetForegroundWindow() - Dim ActiveThread As UInteger = NativeMethods.GetWindowThreadProcessId(ActiveWindow, Nothing) - - If ActiveThread = 0 Then _ - Return ActiveWindow - - If NativeMethods.AttachThreadInput(CurrentThreadID, ActiveThread, True) = False Then _ - Return ActiveWindow - - Dim AbsoluteActiveWindow As IntPtr = NativeMethods.GetFocus() - Dim DetachAttempts As Integer = 0 - - While NativeMethods.AttachThreadInput(CurrentThreadID, ActiveThread, False) = False - DetachAttempts += 1 - If DetachAttempts >= 10 Then Exit While - Threading.Thread.Sleep(1) - End While - - Return AbsoluteActiveWindow - End Function -#End Region - -#Region "CreateLWParam()" - ''' - ''' Creates a wParam or lParam value. - ''' - ''' The low word (UShort) of the lParam/wParam. - ''' The high word (UShort) of the lParam/wParam. - ''' - Private Shared Function CreateLWParam(ByVal LoWord As Integer, ByVal HiWord As Integer) As IntPtr - Return New IntPtr((HiWord << 16) Or (LoWord And &HFFFF)) - End Function -#End Region - #End Region End Class @@ -940,26 +1504,32 @@ Public NotInheritable Class InputHelper ''' The key to check. ''' The modifier to check for. ''' - Private Shared Function IsModifier(ByVal Key As Keys, ByVal Modifier As ModifierKey) As Boolean + Private Shared Function IsModifier(ByVal Key As Keys, ByVal Modifier As ModifierKeys) As Boolean Select Case Modifier - Case ModifierKey.Control + Case ModifierKeys.Control Return _ Key = Keys.Control OrElse _ Key = Keys.ControlKey OrElse _ Key = Keys.LControlKey OrElse _ Key = Keys.RControlKey - Case ModifierKey.Shift + Case ModifierKeys.Shift Return _ Key = Keys.Shift OrElse _ Key = Keys.ShiftKey OrElse _ Key = Keys.LShiftKey OrElse _ Key = Keys.RShiftKey + Case ModifierKeys.Alt + Return _ + Key = Keys.Alt OrElse _ + Key = Keys.Menu OrElse _ + Key = Keys.LMenu OrElse _ + Key = Keys.RMenu + Case ModifierKeys.Windows + Return _ + Key = Keys.LWin OrElse _ + Key = Keys.RWin End Select - Return _ - Key = Keys.Alt OrElse _ - Key = Keys.Menu OrElse _ - Key = Keys.LMenu OrElse _ - Key = Keys.RMenu + Throw New ArgumentOutOfRangeException("Modifier", CType(Modifier, Integer) & " is not a valid modifier key!") End Function #End Region @@ -970,8 +1540,31 @@ Public NotInheritable Class InputHelper Private Sub New() End Sub +#Region "Delegates" + Public Delegate Function LowLevelKeyboardProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr + Public Delegate Function LowLevelMouseProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr +#End Region + #Region "Methods" +#Region "Hook methods" + _ + Public Shared Function SetWindowsHookEx(ByVal idHook As HookType, ByVal lpfn As LowLevelKeyboardProc, ByVal hMod As IntPtr, ByVal dwThreadId As UInteger) As IntPtr + End Function + + _ + Public Shared Function SetWindowsHookEx(ByVal idHook As HookType, ByVal lpfn As LowLevelMouseProc, ByVal hMod As IntPtr, ByVal dwThreadId As UInteger) As IntPtr + End Function + + _ + Public Shared Function UnhookWindowsHookEx(ByVal hhk As IntPtr) As Boolean + End Function + + _ + Public Shared Function CallNextHookEx(ByVal hhk As IntPtr, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr + End Function +#End Region + #Region "Input methods" Public Shared Function SendInput(ByVal nInputs As UInteger, ByVal pInputs() As INPUT, ByVal cbSize As Integer) As UInteger @@ -1035,11 +1628,57 @@ Public NotInheritable Class InputHelper _ Public Shared Function ScreenToClient(ByVal hWnd As IntPtr, ByRef lpPoint As NATIVEPOINT) As Boolean End Function + + + Public Shared Function GetModuleHandle(ByVal lpModuleName As String) As IntPtr + End Function +#End Region + +#Region "System information methods" + _ + Public Shared Function GetDoubleClickTime() As UInteger + End Function #End Region #End Region #Region "Enumerations" + Public Enum HookType As Integer + WH_CALLWNDPROC = 4 + WH_CALLWNDPROCRET = 12 + WH_CBT = 5 + WH_DEBUG = 9 + WH_FOREGROUNDIDLE = 11 + WH_GETMESSAGE = 3 + WH_JOURNALPLAYBACK = 1 + WH_JOURNALRECORD = 0 + WH_KEYBOARD = 2 + WH_KEYBOARD_LL = 13 + WH_MOUSE = 7 + WH_MOUSE_LL = 14 + WH_MSGFILTER = -1 + WH_SHELL = 10 + WH_SYSMSGFILTER = 6 + End Enum + + Public Enum HookCode As Integer + HC_ACTION = 0 + HC_NOREMOVE = 3 + End Enum + + Public Enum LowLevelKeyboardHookFlags As UInteger + LLKHF_EXTENDED = &H1 + LLKHF_LOWER_IL_INJECTED = &H2 + LLKHF_INJECTED = &H10 + LLKHF_ALTDOWN = &H20 + LLKHF_UP = &H80 + End Enum + + Public Enum LowLevelMouseHookFlags As UInteger + LLMHF_INJECTED = &H1 + LLMHF_LOWER_IL_INJECTED = &H2 + End Enum + Public Enum INPUTTYPE As UInteger MOUSE = 0 KEYBOARD = 1 @@ -1177,21 +1816,92 @@ Public NotInheritable Class InputHelper WM_UNICHAR = &H109 End Enum - Public Enum MouseButtonMessage As Integer + Public Enum MouseMessage As Integer + WM_MOUSEMOVE = &H200 WM_LBUTTONDOWN = &H201 WM_LBUTTONUP = &H202 + WM_LBUTTONDBLCLK = &H203 WM_MBUTTONDOWN = &H207 WM_MBUTTONUP = &H208 + WM_MBUTTONDBLCLK = &H209 WM_RBUTTONDOWN = &H204 WM_RBUTTONUP = &H205 + WM_RBUTTONDBLCLK = &H206 + WM_MOUSEWHEEL = &H20A + WM_MOUSEHWHEEL = &H20E WM_XBUTTONDOWN = &H20B WM_XBUTTONUP = &H20C - XBUTTON1 = &H1 - XBUTTON2 = &H2 + WM_XBUTTONDBLCLK = &H20D End Enum #End Region #Region "Structures" + _ + Public Structure KBDLLHOOKSTRUCT + Public vkCode As UInteger + Public scanCode As UInteger + Public flags As LowLevelKeyboardHookFlags + Public time As UInteger + Public dwExtraInfo As UIntPtr + End Structure + + _ + Public Structure MSLLHOOKSTRUCT + Public pt As NATIVEPOINT + Public mouseData As UInteger + Public flags As UInteger + Public time As UInteger + Public dwExtraInfo As UIntPtr + End Structure + + + Public Structure DWORD + Public Value As UInteger + Public Low As UShort + Public High As UShort + + Public Shared Widening Operator CType(ByVal DWORD As DWORD) As IntPtr + Return New IntPtr(DWORD.Value And Integer.MaxValue) + End Operator + + Public Shared Widening Operator CType(ByVal DWORD As DWORD) As UInteger + Return DWORD.Value + End Operator + + Public Sub New(ByVal Value As UInteger) + Me.Value = Value + End Sub + + Public Sub New(ByVal Low As UShort, ByVal High As UShort) + Me.Low = Low + Me.High = High + End Sub + End Structure + + + Public Structure SignedDWORD + Public Value As UInteger + Public Low As Short + Public High As Short + + Public Shared Widening Operator CType(ByVal DWORD As SignedDWORD) As IntPtr + Return New IntPtr(DWORD.Value And Integer.MaxValue) + End Operator + + Public Shared Widening Operator CType(ByVal DWORD As SignedDWORD) As UInteger + Return DWORD.Value + End Operator + + Public Sub New(ByVal Value As UInteger) + Me.Value = Value + End Sub + + Public Sub New(ByVal Low As UShort, ByVal High As UShort) + Me.Low = Low + Me.High = High + End Sub + End Structure + _ Public Structure INPUTUNION Public mi As MOUSEINPUT @@ -1229,8 +1939,8 @@ Public NotInheritable Class InputHelper _ Public Structure NATIVEPOINT - Public X As Integer - Public Y As Integer + Public x As Integer + Public y As Integer Public Sub New(ByVal X As Integer, ByVal Y As Integer) Me.X = X diff --git a/InputHelper Library/InputHelper Library/My Project/Resources.Designer.vb b/InputHelper Library/InputHelper Library/My Project/Resources.Designer.vb index c639eab..04ca74c 100644 --- a/InputHelper Library/InputHelper Library/My Project/Resources.Designer.vb +++ b/InputHelper Library/InputHelper Library/My Project/Resources.Designer.vb @@ -39,7 +39,7 @@ Namespace My.Resources Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager Get If Object.ReferenceEquals(resourceMan, Nothing) Then - Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("InputHelperLibrary.Resources", GetType(Resources).Assembly) + Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("InputHelperLib.Resources", GetType(Resources).Assembly) resourceMan = temp End If Return resourceMan diff --git a/InputHelper Library/InputHelper Library/My Project/Settings.Designer.vb b/InputHelper Library/InputHelper Library/My Project/Settings.Designer.vb index 325e7ba..421362b 100644 --- a/InputHelper Library/InputHelper Library/My Project/Settings.Designer.vb +++ b/InputHelper Library/InputHelper Library/My Project/Settings.Designer.vb @@ -64,9 +64,9 @@ Namespace My Friend Module MySettingsProperty _ - Friend ReadOnly Property Settings() As Global.InputHelperLibrary.My.MySettings + Friend ReadOnly Property Settings() As Global.InputHelperLib.My.MySettings Get - Return Global.InputHelperLibrary.My.MySettings.Default + Return Global.InputHelperLib.My.MySettings.Default End Get End Property End Module