@@ -9,46 +9,39 @@ namespace RT.Util
99 /// <summary>Manages a global low-level keyboard hook.</summary>
1010 public sealed class GlobalKeyboardListener
1111 {
12- /// <summary>
13- /// The collections of keys to watch for. This is ignored if <see cref="HookAllKeys"/> is set to true.
14- /// </summary>
12+ /// <summary>The collections of keys to watch for. This is ignored if <see cref="HookAllKeys" /> is set to true.</summary>
1513 public List < Keys > HookedKeys { get { return _hookedKeys ; } }
1614 private List < Keys > _hookedKeys = new List < Keys > ( ) ;
1715
1816 /// <summary>
19- /// Gets or sets a value indicating whether all keys are listened for. If this is set to true, <see cref="HookedKeys"/> is ignored.
20- /// </summary>
17+ /// Gets or sets a value indicating whether all keys are listened for. If this is set to true, <see
18+ /// cref="HookedKeys" /> is ignored. </summary>
2119 public bool HookAllKeys { get ; set ; }
2220
23- /// <summary>
24- /// Handle to the hook, need this to unhook and call the next hook
25- /// </summary>
21+ /// <summary>Handle to the hook, need this to unhook and call the next hook</summary>
2622 private IntPtr _hHook = IntPtr . Zero ;
2723
24+ /// <summary>Current state of each modifier key.</summary>
25+ private bool _ctrl , _alt , _shift , _win ;
26+
2827 #region Events
29- /// <summary>
30- /// Occurs when one of the hooked keys is pressed.
31- /// </summary>
28+ /// <summary>Occurs when one of the hooked keys is pressed.</summary>
3229 public event GlobalKeyEventHandler KeyDown ;
33- /// <summary>
34- /// Occurs when one of the hooked keys is released.
35- /// </summary>
30+ /// <summary>Occurs when one of the hooked keys is released.</summary>
3631 public event GlobalKeyEventHandler KeyUp ;
3732 #endregion
3833
3934 #region Constructors and Destructors
4035 /// <summary>
41- /// Initializes a new instance of the <see cref="GlobalKeyboardListener"/> class and installs the keyboard hook.
42- /// </summary>
36+ /// Initializes a new instance of the <see cref="GlobalKeyboardListener" /> class and installs the keyboard hook.</summary>
4337 public GlobalKeyboardListener ( )
4438 {
4539 hook ( ) ;
4640 }
4741
4842 /// <summary>
49- /// Releases unmanaged resources and performs other cleanup operations before the
50- /// <see cref="GlobalKeyboardListener"/> is reclaimed by garbage collection and uninstalls the keyboard hook.
51- /// </summary>
43+ /// Releases unmanaged resources and performs other cleanup operations before the <see
44+ /// cref="GlobalKeyboardListener" /> is reclaimed by garbage collection and uninstalls the keyboard hook.</summary>
5245 ~ GlobalKeyboardListener ( )
5346 {
5447 unhook ( ) ;
@@ -58,28 +51,28 @@ public GlobalKeyboardListener()
5851 private WinAPI . KeyboardHookProc _hook ;
5952
6053 #region Public Methods
61- /// <summary>
62- /// Installs the global hook
63- /// </summary>
54+ /// <summary>Installs the global hook</summary>
6455 private void hook ( )
6556 {
6657 IntPtr hInstance = WinAPI . LoadLibrary ( "User32" ) ;
6758 _hook = new WinAPI . KeyboardHookProc ( hookProc ) ;
6859 _hHook = WinAPI . SetWindowsHookEx ( WinAPI . WH_KEYBOARD_LL , _hook , hInstance , 0 ) ;
6960 }
7061
71- /// <summary>
72- /// Uninstalls the global hook
73- /// </summary>
62+ /// <summary>Uninstalls the global hook</summary>
7463 private void unhook ( )
7564 {
7665 WinAPI . UnhookWindowsHookEx ( _hHook ) ;
7766 }
7867
79- /// <summary>The callback for the keyboard hook.</summary>
80- /// <param name="code">The hook code. If this is < 0, the callback shouldn’t do anyting.</param>
81- /// <param name="wParam">The event type. Only <c>WM_(SYS)?KEY(DOWN|UP)</c> events are handled.</param>
82- /// <param name="lParam">Information about the key pressed/released.</param>
68+ /// <summary>
69+ /// The callback for the keyboard hook.</summary>
70+ /// <param name="code">
71+ /// The hook code. If this is < 0, the callback shouldn’t do anyting.</param>
72+ /// <param name="wParam">
73+ /// The event type. Only <c>WM_(SYS)?KEY(DOWN|UP)</c> events are handled.</param>
74+ /// <param name="lParam">
75+ /// Information about the key pressed/released.</param>
8376 private int hookProc ( int code , int wParam , ref WinAPI . KeyboardHookStruct lParam )
8477 {
8578 if ( code >= 0 )
@@ -88,41 +81,119 @@ private int hookProc(int code, int wParam, ref WinAPI.KeyboardHookStruct lParam)
8881
8982 if ( HookAllKeys || _hookedKeys . Contains ( key ) )
9083 {
91- var kea = new GlobalKeyEventArgs ( key , lParam . scanCode ) ;
92-
93- if ( ( wParam == WinAPI . WM_KEYDOWN || wParam == WinAPI . WM_SYSKEYDOWN ) && ( KeyDown != null ) )
94- KeyDown ( this , kea ) ;
95- else if ( ( wParam == WinAPI . WM_KEYUP || wParam == WinAPI . WM_SYSKEYUP ) && ( KeyUp != null ) )
96- KeyUp ( this , kea ) ;
97-
98- if ( kea . Handled )
99- return 1 ;
84+ if ( ( wParam == WinAPI . WM_KEYDOWN || wParam == WinAPI . WM_SYSKEYDOWN ) )
85+ {
86+ switch ( key )
87+ {
88+ case Keys . ControlKey :
89+ case Keys . LControlKey :
90+ case Keys . RControlKey : _ctrl = true ; break ;
91+
92+ case Keys . Menu :
93+ case Keys . LMenu :
94+ case Keys . RMenu : _alt = true ; break ;
95+
96+ case Keys . ShiftKey :
97+ case Keys . LShiftKey :
98+ case Keys . RShiftKey : _shift = true ; break ;
99+
100+ case Keys . LWin :
101+ case Keys . RWin : _win = true ; break ;
102+ }
103+ if ( KeyDown != null )
104+ {
105+ var kea = new GlobalKeyEventArgs ( key , lParam . scanCode , new ModifierKeysState ( _ctrl , _alt , _shift , _win ) ) ;
106+ KeyDown ( this , kea ) ;
107+ if ( kea . Handled )
108+ return 1 ;
109+ }
110+ }
111+ else if ( ( wParam == WinAPI . WM_KEYUP || wParam == WinAPI . WM_SYSKEYUP ) )
112+ {
113+ switch ( key )
114+ {
115+ case Keys . ControlKey :
116+ case Keys . LControlKey :
117+ case Keys . RControlKey : _ctrl = false ; break ;
118+
119+ case Keys . Menu :
120+ case Keys . LMenu :
121+ case Keys . RMenu : _alt = false ; break ;
122+
123+ case Keys . ShiftKey :
124+ case Keys . LShiftKey :
125+ case Keys . RShiftKey : _shift = false ; break ;
126+
127+ case Keys . LWin :
128+ case Keys . RWin : _win = false ; break ;
129+ }
130+ if ( KeyUp != null )
131+ {
132+ var kea = new GlobalKeyEventArgs ( key , lParam . scanCode , new ModifierKeysState ( _ctrl , _alt , _shift , _win ) ) ;
133+ KeyUp ( this , kea ) ;
134+ if ( kea . Handled )
135+ return 1 ;
136+ }
137+ }
100138 }
101139 }
102140 return WinAPI . CallNextHookEx ( _hHook , code , wParam , ref lParam ) ;
103141 }
104142 #endregion
105143 }
106144
107- /// <summary>Contains arguments for the KeyUp/KeyDown event in a <see cref="GlobalKeyboardListener"/>.</summary>
145+ /// <summary>Encapsulates the current state of modifier keys.</summary>
146+ public struct ModifierKeysState
147+ {
148+ private int _state ;
149+
150+ /// <summary>Constructor.</summary>
151+ public ModifierKeysState ( bool ctrl = false , bool alt = false , bool shift = false , bool win = false )
152+ {
153+ _state = ( ctrl ? 1 : 0 ) | ( alt ? 2 : 0 ) | ( shift ? 4 : 0 ) | ( win ? 8 : 0 ) ;
154+ }
155+
156+ /// <summary>Gets the state of the Control key (true if left OR right is down).</summary>
157+ public bool Ctrl { get { return ( _state & 1 ) > 0 ; } }
158+ /// <summary>Gets the state of the Alt key (true if left OR right is down).</summary>
159+ public bool Alt { get { return ( _state & 2 ) > 0 ; } }
160+ /// <summary>Gets the state of the Shift key (true if left OR right is down).</summary>
161+ public bool Shift { get { return ( _state & 4 ) > 0 ; } }
162+ /// <summary>Gets the state of the Windows key (true if left OR right is down).</summary>
163+ public bool Win { get { return ( _state & 8 ) > 0 ; } }
164+
165+ /// <summary>Compares the modifiers and returns true iff the two are equal.</summary>
166+ public static bool operator == ( ModifierKeysState k1 , ModifierKeysState k2 ) { return k1 . _state == k2 . _state ; }
167+ /// <summary>Compares the modifiers and returns true iff the two are not equal.</summary>
168+ public static bool operator != ( ModifierKeysState k1 , ModifierKeysState k2 ) { return k1 . _state != k2 . _state ; }
169+ /// <summary>Override; see base.</summary>
170+ public override bool Equals ( object obj ) { return ( obj is ModifierKeysState ) && ( this == ( ModifierKeysState ) obj ) ; }
171+ /// <summary>Override; see base.</summary>
172+ public override int GetHashCode ( ) { return _state ; }
173+ }
174+
175+ /// <summary>Contains arguments for the KeyUp/KeyDown event in a <see cref="GlobalKeyboardListener" />.</summary>
108176 public sealed class GlobalKeyEventArgs : EventArgs
109177 {
110178 /// <summary>The virtual-key code of the key being pressed or released.</summary>
111179 public Keys VirtualKeyCode { get ; private set ; }
112180 /// <summary>The scancode of the key being pressed or released.</summary>
113181 public int ScanCode { get ; private set ; }
182+ /// <summary>Current state of the modifier keys</summary>
183+ public ModifierKeysState ModifierKeys { get ; private set ; }
114184 /// <summary>Set this to ‘true’ to prevent further processing of the keystroke (i.e. to ‘swallow’ it).</summary>
115185 public bool Handled { get ; set ; }
116186
117187 /// <summary>Constructor.</summary>
118- public GlobalKeyEventArgs ( Keys virtualKeyCode , int scanCode )
188+ public GlobalKeyEventArgs ( Keys virtualKeyCode , int scanCode , ModifierKeysState modifierKeys )
119189 {
120190 VirtualKeyCode = virtualKeyCode ;
121191 ScanCode = scanCode ;
192+ ModifierKeys = modifierKeys ;
122193 Handled = false ;
123194 }
124195 }
125196
126- /// <summary>Used to trigger the KeyUp/KeyDown events in <see cref="GlobalKeyboardListener"/>.</summary>
197+ /// <summary>Used to trigger the KeyUp/KeyDown events in <see cref="GlobalKeyboardListener" />.</summary>
127198 public delegate void GlobalKeyEventHandler ( object sender , GlobalKeyEventArgs e ) ;
128199}
0 commit comments