-
Notifications
You must be signed in to change notification settings - Fork 0
/
Keyboard.cs
169 lines (141 loc) · 5.74 KB
/
Keyboard.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Text;
using System.Collections.Generic;
public class KeyboardHook : IDisposable
{
public bool IsStarted { get { return hookID != IntPtr.Zero; } }
internal delegate IntPtr LowLevelKeyboardProcDelegate(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
private LowLevelKeyboardProcDelegate proc;
private IntPtr hookID = IntPtr.Zero;
public event KeyboardHookEventHandler KeysChanged;
public KeyboardHook()
{
proc = new LowLevelKeyboardProcDelegate(LowLevelKeyboardProc);
}
public void Start()
{
if (IsStarted) return;
keysCurrentlyDown.Clear();
using (Process curProcess = Process.GetCurrentProcess())
{
using (ProcessModule curModule = curProcess.MainModule)
{
hookID = NativeMethods.SetWindowsHookEx(WH_KEYBOARD_LL, proc, NativeMethods.GetModuleHandle(curModule.ModuleName), 0);
}
}
}
public void Stop()
{
if (!IsStarted) return;
NativeMethods.UnhookWindowsHookEx(hookID);
hookID = IntPtr.Zero;
keysCurrentlyDown.Clear();
}
private List<Keys> keysCurrentlyDown = new List<Keys>();
public enum KeyDirection
{
Down,
Up
}
private IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam)
{
if (nCode >= 0)
{
//Console.WriteLine($"LowLevelKeyboardProc: lParam={lParam}");
Keys key = (Keys)lParam.vkCode;
//Console.WriteLine($"LowLevelKeyboardProc: key={key}");
if (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN)
{
if (!keysCurrentlyDown.Contains(key))
{
keysCurrentlyDown.Add(key);
OnKeysChanged(new KeysChangedEventArgs(KeyDirection.Down, keysCurrentlyDown));
}
}
else if (wParam == (IntPtr)WM_KEYUP || wParam == (IntPtr)WM_SYSKEYUP)
{
if (keysCurrentlyDown.Remove(key))
{
OnKeysChanged(new KeysChangedEventArgs(KeyDirection.Up, keysCurrentlyDown));
}
}
}
return NativeMethods.CallNextHookEx(hookID, nCode, wParam, ref lParam);
}
public void OnKeysChanged(KeysChangedEventArgs e)
{
KeysChanged?.Invoke(e);
}
public delegate void KeyboardHookEventHandler(KeysChangedEventArgs e);
public class KeysChangedEventArgs : EventArgs
{
public KeyDirection MostRecentKeyDirection { get; private set; }
public Keys[] KeysCurrentlyDown { get; private set; }
public bool IsCapsLockOn { get { return (NativeMethods.GetKeyState((int)Keys.Capital) & 0x0001) == 0x0001; } }
public KeysChangedEventArgs(KeyDirection mostRecentKeyDirection, List<Keys> keysCurrentlyDown)
{
MostRecentKeyDirection = mostRecentKeyDirection;
KeysCurrentlyDown = keysCurrentlyDown.ToArray();
}
public override string ToString()
{
return $"{{ MostRecentKeyDirection={MostRecentKeyDirection}, KeysCurrentlyDown=[{KeysCurrentlyDown}] }}";
}
}
#region IDisposable Members
/// <summary>
/// Releases the keyboard hook.
/// </summary>
public void Dispose()
{
Stop();
}
#endregion
#region Native methods
private const int WH_KEYBOARD_LL = 0x0D;
private const int WM_KEYDOWN = 0x0100;
private const int WM_KEYUP = 0x0101;
private const int WM_SYSKEYDOWN = 0x0104;
private const int WM_SYSKEYUP = 0x0105;
internal struct KBDLLHOOKSTRUCT
{
public int vkCode;
int scanCode;
public int flags;
int time;
int dwExtraInfo;
public override string ToString()
{
return $"{{ vkCode={vkCode}, scanCode={scanCode}, flags={flags}, time={time}, dwExtraInfo={dwExtraInfo} }}";
}
}
[ComVisibleAttribute(false),
System.Security.SuppressUnmanagedCodeSecurity()]
internal class NativeMethods
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProcDelegate lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
/// <summary>
/// Return value
/// Type: SHORT
/// The return value specifies the status of the specified virtual key, as follows:
/// * If the high-order bit is 1, the key is down; otherwise, it is up.
/// * If the low-order bit is 1, the key is toggled. A key, such as the CAPS LOCK key, is toggled if it is turned on. The key is off and untoggled if the low-order bit is 0. A toggle key's indicator light (if any) on the keyboard will be on when the key is toggled, and off when the key is untoggled.
/// </summary>
/// <param name="keyCode"></param>
/// <returns></returns>
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
public static extern short GetKeyState(int keyCode);
}
#endregion
}