WPF取色器开发(获取鼠标坐标、获取坐标颜色、键盘钩子、鼠标钩子)

前言

这里全局的键盘钩子和全局鼠标钩子是为了定义快捷键操作。

获取鼠标坐标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Runtime.InteropServices;

namespace ColorPicker.Utils
{
internal class ZPoint
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool GetCursorPos(out POINT pt);

[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;

public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
}
}
}

调用方式

1
2
ZPoint.POINT point;
ZPoint.GetCursorPos(out point);

获取坐标颜色

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
using System;
using System.Drawing;
using System.Runtime.InteropServices;

namespace ColorPicker.Utils
{
internal class ScreenColor
{
public static Color GetPixelColor(int x, int y)
{
IntPtr hdc = GetDC(IntPtr.Zero);
uint pixel = GetPixel(hdc, x, y);
ReleaseDC(IntPtr.Zero, hdc);
Color color = Color.FromArgb((int)(pixel & 0x000000FF),
(int)(pixel & 0x0000FF00) >> 8,
(int)(pixel & 0x00FF0000) >> 16);
return color;
}

[DllImport("user32.dll")]
private static extern IntPtr GetDC(IntPtr hwnd);

[DllImport("gdi32.dll")]
private static extern uint GetPixel(IntPtr hdc, int nXPos, int nYPos);

[DllImport("user32.dll")]
private static extern Int32 ReleaseDC(IntPtr hwnd, IntPtr hdc);
}
}

调用方式

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
private static bool isStart = false;        
/// <summary>
/// 上次的坐标点
/// </summary>
private ZPoint.POINT lastPoint;

/// <summary>
/// 取色
/// </summary>
private async void startTakeColor()
{
if (!isStart)
{
isStart = true;
while (isStart)
{
await Task.Run(
() =>
{
ZPoint.POINT point;
ZPoint.GetCursorPos(out point);
if (lastPoint.X != point.X || lastPoint.Y != point.Y)
{
lastPoint = point;
System.Drawing.Color c = ScreenColor.GetPixelColor(point.X, point.Y);
Dispatcher.Invoke(
() =>
{
string html_color = System.Drawing.ColorTranslator.ToHtml(c);
html_color_btn.Content = html_color;
rgb_color_btn.Content = "RGBA(" + c.R + "," + c.G + "," + c.B + ",1)";
color_tb.Background = (Brush)new BrushConverter().ConvertFrom(html_color);
ClipboardSave(html_color);
}
);
}
Thread.Sleep(100);
}
);
}
}
}

private void ClipboardSave(string txt)
{
try
{
// 如果剪贴板被占用会报异常
Clipboard.SetText(txt);
}
catch (Exception)
{
}
}

/// <summary>
/// 停止取色
/// </summary>
private void stopTakeColor()
{
isStart = false;
}

剪贴板保存

1
2
3
4
5
6
7
8
9
10
11
private void ClipboardSave(string txt)
{
try
{
// 如果剪贴板被占用会报异常
Clipboard.SetText(txt);
}
catch (Exception)
{
}
}

注意

这里一定要进行异常捕获,否则剪贴板被占用时会导致程序崩溃。

全局键盘钩子

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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
namespace z_screen_recorder.Utils
{
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

/// <summary>
/// 键盘全局钩子
/// </summary>
public class KeyboardHook
{
#region 常数和结构

#region wParam对应的按钮事件

public const int WmKeydown = 0x100; // 键盘被按下
public const int WmKeyup = 0x101; // 键盘被松开
public const int WmSyskeydown = 0x104; // 键盘被按下,这个是系统键被按下,例如Alt、Ctrl等键
public const int WmSyskeyup = 0x105; // 键盘被松开,这个是系统键被松开,例如Alt、Ctrl等键

#endregion wParam对应的按钮事件

public const int WhKeyboardLl = 13;

[StructLayout(LayoutKind.Sequential)] //声明键盘钩子的封送结构类型
public class KeyboardHookStruct

{
public int vkCode; //表示一个在1到254间的虚似键盘码
public int scanCode; //表示硬件扫描码
public int flags;
public int time;
public int dwExtraInfo;
}

#endregion 常数和结构

#region 成员变量、委托、事件

private static int _hHook;
private static HookProc _keyboardHookDelegate;

// 锁
private readonly object _lockObject = new object();

// 当前状态,是否已经启动
private volatile bool _isStart;

// 键盘回调委托
public delegate void KeyboardHandler
(
Int32 wParam,
KeyboardHookStruct keyboardHookStruct
);

// 键盘回调事件
private static event KeyboardHandler Handlers;

#endregion 成员变量、委托、事件

#region Win32的Api

private delegate int HookProc
(
int nCode,
Int32 wParam,
IntPtr lParam
);

//下一个钩挂的函数
[DllImport(
"user32.dll",
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall
)]
private static extern int CallNextHookEx
(
int idHook,
int nCode,
Int32 wParam,
IntPtr lParam
);

[DllImport(
"kernel32.dll",
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall
)]
private static extern IntPtr GetModuleHandle(string lpModuleName);

//安装钩子的函数
[DllImport(
"user32.dll",
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall
)]
private static extern int SetWindowsHookEx
(
int idHook,
HookProc lpfn,
IntPtr hInstance,
int threadId
);

//卸下钩子的函数
[DllImport(
"user32.dll",
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall
)]
private static extern bool UnhookWindowsHookEx(int idHook);

#endregion Win32的Api

#region 单例模式

private static readonly object CreateLock = new object();
private static volatile KeyboardHook _myKeyboard;

private KeyboardHook()
{
}

public static KeyboardHook GetKeyboardHook()
{
if (_myKeyboard == null)
{
lock (CreateLock)
{
if (_myKeyboard == null)
{
_myKeyboard = new KeyboardHook();
}
}
}
return _myKeyboard;
}

#endregion 单例模式

/// <summary>
/// 添加按键的回调函数
/// </summary>
/// <param name="handler">
/// </param>
public void AddKeyboardHandler(KeyboardHandler handler)
{
Handlers += handler;
}

/// <summary>
/// 删除指定按键的回调函数
/// </summary>
/// <param name="handler">
/// </param>
public void RemoveKeyboardHandler(KeyboardHandler handler)
{
if (Handlers != null)
{
Handlers -= handler;
}
}

/// <summary>
/// 安装钩子
/// </summary>
public void Start()
{
if (_isStart)
{
return;
}
lock (_lockObject)
{
if (_isStart)
{
return;
}
if (Handlers == null)
{
throw new Exception("Please set handler first!Then run Start");
}
_keyboardHookDelegate = KeyboardHookProc;
Process cProcess = Process.GetCurrentProcess();
ProcessModule cModule = cProcess.MainModule;
if (cModule != null)
{
var mh = GetModuleHandle(cModule.ModuleName);
_hHook = SetWindowsHookEx(
WhKeyboardLl,
_keyboardHookDelegate,
mh,
0
);
}
_isStart = true;
}
}

/// <summary>
/// 卸载钩子
/// </summary>
public void Stop()
{
if (!_isStart)
{
return;
}
lock (_lockObject)
{
if (!_isStart)
{
return;
}
UnhookWindowsHookEx(_hHook);
// 清除所有事件
Handlers = null;
_isStart = false;
}
}

/// <summary>
/// 键盘的系统回调函数
/// </summary>
/// <param name="nCode">
/// </param>
/// <param name="wParam">
/// </param>
/// <param name="lParam">
/// </param>
/// <returns>
/// </returns>
private static int KeyboardHookProc
(
int nCode,
Int32 wParam,
IntPtr lParam
)
{
//如果该消息被丢弃(nCode<0)或者没有事件绑定处理程序则不会触发事件
if ((nCode >= 0) && Handlers != null)
{
KeyboardHookStruct keyDataFromHook = (KeyboardHookStruct)Marshal.PtrToStructure(
lParam,
typeof(KeyboardHookStruct)
);
Handlers(
wParam,
keyDataFromHook
);
}
return CallNextHookEx(
_hHook,
nCode,
wParam,
lParam
);
}
}
}

调用方式

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
namespace z_screen_recorder
{
using System.Windows;
using Utils;

/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
{
public static MainWindow MainWin = null;
private readonly KeyboardHook _kHook = KeyboardHook.GetKeyboardHook();

private void Application_Startup
(
object sender,
StartupEventArgs e
)
{
StartKeyboardHook();
}

public void StartKeyboardHook()
{
_kHook.AddKeyboardHandler(HookKeyboardEvent);
_kHook.Start();
}

public void StopKeyboardHook()
{
_kHook.Stop();
}

private static void HookKeyboardEvent
(
int wParam,
KeyboardHook.KeyboardHookStruct keyboardHookStruct
)
{
//按了F8
if (keyboardHookStruct.vkCode == 119 && wParam == 0x101)
{
MainWin.Play();
}
}

private void Application_Exit
(
object sender,
ExitEventArgs e
)
{
StopKeyboardHook();
}
}
}

SetWindowsHookEx参数说明

https://docs.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-setwindowshookexa

全局鼠标钩子

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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace ColorPicker.Utils
{
/// <summary>
/// 鼠标全局钩子
/// </summary>
public class MouseHook
{
private const int WM_MOUSEMOVE = 0x200;
private const int WM_LBUTTONDOWN = 0x201;
private const int WM_RBUTTONDOWN = 0x204;
private const int WM_MBUTTONDOWN = 0x207;
private const int WM_LBUTTONUP = 0x202;
private const int WM_RBUTTONUP = 0x205;
private const int WM_MBUTTONUP = 0x208;
private const int WM_LBUTTONDBLCLK = 0x203;
private const int WM_RBUTTONDBLCLK = 0x206;
private const int WM_MBUTTONDBLCLK = 0x209;

/// <summary>
///
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public class POINT
{
public int x;
public int y;
}

/// <summary>
/// 钩子结构体
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public class MouseHookStruct
{
public POINT pt;
public int hWnd;
public int wHitTestCode;
public int dwExtraInfo;
}

public const int WH_MOUSE_LL = 14; // mouse hook constant

// 装置钩子的函数
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

// 卸下钩子的函数
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);

// 下一个钩挂的函数
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);

// 键盘回调委托
public delegate void MouseHandler(object sender, MouseEventArgs e, int downup);

// 键盘回调事件
private static event MouseHandler Handlers;

// 钩子回调函数
public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);

// 声明鼠标钩子事件类型
private HookProc _mouseHookProcedure;

private static int _hMouseHook = 0; // 鼠标钩子句柄

/// <summary>
/// 构造函数
/// </summary>
public MouseHook()
{
}

/// <summary>
/// 析构函数
/// </summary>
~MouseHook()
{
Stop();
}

/// <summary>
/// 启动全局钩子
/// </summary>
public void Start()
{
// 安装鼠标钩子
if (_hMouseHook == 0)
{
if (Handlers == null)
{
throw new Exception("Please set handler first!Then run Start");
}
// 生成一个HookProc的实例.
_mouseHookProcedure = new HookProc(MouseHookProc);

_hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, _mouseHookProcedure, Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]), 0);

//假设装置失败停止钩子
if (_hMouseHook == 0)
{
Stop();
throw new Exception("SetWindowsHookEx failed.");
}
}
}

/// <summary>
/// 停止全局钩子
/// </summary>
public void Stop()
{
bool retMouse = true;

if (_hMouseHook != 0)
{
retMouse = UnhookWindowsHookEx(_hMouseHook);
_hMouseHook = 0;
}

// 假设卸下钩子失败
if (!retMouse)
throw new Exception("UnhookWindowsHookEx failed.");
}

/// <summary>
/// 添加按键的回调函数
/// </summary>
/// <param name="handler">
/// </param>
public void AddMouseHandler(MouseHandler handler)
{
Handlers += handler;
}

/// <summary>
/// 鼠标钩子回调函数
/// </summary>
private int MouseHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
// 假设正常执行而且用户要监听鼠标的消息
if ((nCode >= 0) && (Handlers != null))
{
MouseButtons button = MouseButtons.None;
int clickCount = 0;
int downup = 0;//0 down 1 up
bool mouseclick = false;

switch (wParam)
{
case WM_LBUTTONDOWN:
button = MouseButtons.Left;
clickCount = 1;
mouseclick = true;
break;

case WM_LBUTTONUP:
button = MouseButtons.Left;
clickCount = 1;
downup = 1;
mouseclick = true;
break;

case WM_LBUTTONDBLCLK:
button = MouseButtons.Left;
clickCount = 2;
mouseclick = true;
break;

case WM_RBUTTONDOWN:
button = MouseButtons.Right;
clickCount = 1;
mouseclick = true;
break;

case WM_RBUTTONUP:
button = MouseButtons.Right;
clickCount = 1;
downup = 1;
mouseclick = true;
break;

case WM_RBUTTONDBLCLK:
button = MouseButtons.Right;
clickCount = 2;
mouseclick = true;
break;
}

if (mouseclick)
{
// 从回调函数中得到鼠标的信息
MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
MouseEventArgs e = new MouseEventArgs(button, clickCount, MyMouseHookStruct.pt.x, MyMouseHookStruct.pt.y, 0);

Handlers(this, e, downup);
}
}

// 启动下一次钩子
return CallNextHookEx(_hMouseHook, nCode, wParam, lParam);
}
}
}

调用方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private static MainWindow mainWindow = null;
private MouseHook m_hook = new MouseHook();

public void StartMouseHook()
{
m_hook.AddMouseHandler(new MouseHook.MouseHandler(hookMouseEvent));
m_hook.Start();
}

public void StopMouseHook()
{
m_hook.Stop();
}

private static void hookMouseEvent(object sender, System.Windows.Forms.MouseEventArgs e, int downup)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left && downup == 1)
{
mainWindow.stopTakeColor();
}
}