茵蒂克丝
如何建立一个窗口
另一个再录的 Windows SDK教程 里面有讲到快捷建立窗口的方式,不过这样的话要分好几个文件,感受有点混因此这里就用原始的方式建立一个窗口。html
那么,为何讲到 hook(钩子)的时候要去建立窗口呢?其实这个问题提及来也不复杂,简单点说,按博主这样写不用写DLL也不用资源文件,其实是把问题简化了一些。一般 hook 是用来监听本身窗口上的键盘和鼠标输入的,监听全局的一般是设置一些全局的热键(如QQ的 Ctrl+Alt+Z 调出QQ窗口),这些常见的功能也都是要依托窗口才能存在。因此咱们先来简单说下手动创建一个窗口的流程。编程
手动建立窗口的流程
- 设置注册窗口结构体
- 使用【窗口结构体】注册窗口
- 建立窗口
- 显示窗口
- 窗口过程处理
- 消息循环
实际代码
这里不会详细讲这个,有感兴趣的能够去追博主的 SDK教程 或者去搜 杨中科的《C语言也能干大事》windows
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
|
#include <windows.h>
// 5. 窗口过程处理
LRESULT
CALLBACK WndProc(
HWND
hwnd,
UINT
msg,
WPARAM
wParam,
LPARAM
lParam)
{
switch
(msg)
{
case
WM_CLOSE:
DestroyWindow(hwnd);
break
;
case
WM_DESTROY:
PostQuitMessage(0);
break
;
default
:
return
DefWindowProc(hwnd, msg, wParam, lParam);
}
return
0;
}
int
WINAPI WinMain(
HINSTANCE
hInstance,
HINSTANCE
hPrevInstance,
LPSTR
lpCmdLine,
int
nCmdShow)
{
HWND
hwnd;
MSG Msg;
char
text[30];
const
char
szClassName[] =
"myWindowClass"
;
// 1. 设置注册窗口结构体
wc.cbSize =
sizeof
(WNDCLASSEX);
// 注册窗口结构体的大小
wc.style = 0;
// 窗口的样式
wc.lpfnWndProc = WndProc;
// 指向窗口处理过程的函数指针
wc.cbClsExtra = 0;
// 指定紧跟在窗口类结构后的附加字节数
wc.cbWndExtra = 0;
// 指定紧跟在窗口事例后的附加字节数
wc.hInstance = hInstance;
// 本模块的实例句柄
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
// 图标的句柄
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
// 光标的句柄
wc.hbrBackground = (
HBRUSH
)(COLOR_WINDOW+1);
// 背景画刷的句柄
wc.lpszMenuName = NULL;
// 指向菜单的指针
wc.lpszClassName = szClassName;
// 指向类名称的指针
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
// 和窗口类关联的小图标
// 2. 使用【窗口结构体】注册窗口
if
(!RegisterClassEx(&wc))
{
MessageBox(NULL, TEXT(
"窗口注册失败!"
), TEXT(
"错误"
), MB_ICONEXCLAMATION | MB_OK);
return
0;
}
// 3. 建立窗口
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
// 窗口的扩展风格
szClassName,
// 指向注册类名的指针
TEXT(
"窗口标题"
),
// 指向窗口名称的指针
WS_OVERLAPPEDWINDOW,
// 窗口风格
CW_USEDEFAULT, CW_USEDEFAULT, 350, 200,
// 窗口的 x,y 坐标以及宽高
NULL,
// 父窗口的句柄
NULL,
// 菜单的句柄
hInstance,
// 应用程序实例的句柄
NULL
// 指向窗口的建立数据
);
if
(hwnd == NULL)
{
MessageBox(NULL, TEXT(
"窗口建立失败"
), TEXT(
"错误"
),MB_ICONEXCLAMATION | MB_OK);
return
0;
}
// 4. 显示窗口
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// 6. 消息循环
while
(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return
Msg.wParam;
}
|
由于是比较死的形式,以上代码你们混个脸熟,大概知道各个部分的做用就好了,博主也历来没有专门记过。api
安装钩子 (Install hook)
简介
窗口建好了以后就要开始转到咱们的正题了,首先咱们须要明确的是,这个钩子(hook)究竟是什么,那么博主这里也不作太书面的解释留下几个连接:数组
百度百科:hook
MSDN: Hooks
博客园: Beginning HOOK网络
各位能够多多参考,那么博主说下本身的理解:app
windows 系统中的【hook 机制】,就相似于一个【消息过滤网】,若是咱们向操做系统申请并成功对某个窗口安装了一个【hook】指定了【回调函数】,那么这个【回调函数】也就至关于咱们人为对这个窗口添加了一个【消息过滤网】。此时当 windows 操做系统要对这个窗口发送任何消息的时候(例如按键、鼠标点击等消息)操做系统会先调用咱们在【消息过滤网】中设置的【回调函数】去接受、处理、过滤等等,固然若是你在【回调函数】中拿到了数据却没有继续传递给窗口的话,就至关于拦截了这些消息。wordpress
打个简单的比方,若是你在系统全局安装了一个【键盘消息】的钩子,而且在其指定的【回调函数】中没有把这个键盘消息继续传递给系统上的窗口,那么你的全部【键盘消息】都被这个【hook】也就咱们挂在这个【消息过滤网】上的【回调函数】给拦截了,这也意味着你的键盘会失灵。函数
SetWindowsHookEx 函数
那么 SetWindowsHookEx 函数就是咱们用来在 windows 操做系统上安装钩子的函数,咱们简单来看一下这个函数的原型:post
1
2
3
4
5
6
|
HHOOK
WINAPI SetWindowsHookEx(
_In_
int
idHook,
// 安装的钩子类型
_In_ HOOKPROC lpfn,
// 处理消息的回调函数
_In_
HINSTANCE
hMod,
// 当前实例句柄
_In_
DWORD
dwThreadId
// 线程ID
);
|
钩子类型有不少种,本页中留的大部分连接上都有讲到这里就不废话了,关于 hMod(当前实例句柄)和 dwThreadId(线程ID)之间的一些小九九博主这里也很少说,各位能够到下方的连接中去看看,博主这里就举一个容易实现的实例。
百度百科: SetWindowsHookEx
MSDN: SetWindowsHookEx
设置监听【键盘】消息
PKBDLLHOOKSTRUCT 是 WH_KEYBOARD_LL 方式中用来接收消息的结构体,你们能够到 WindUser.h 中多逛逛。
咱们监听键盘的时候主要用的是该结构体的 vkCode(value code)和 scanCode 这两个字段。即键盘的【值码】和【扫描码】那么为何判断一个按键要分红两个部分呢,缘由是由于世界上的键盘有不少种,不一样国家、不一样厂商生产的键盘甚,至同一个键盘上【一样的键】不一样的地方按下均可能会有差别。vkCode 是常见的通常都是公用的键盘值,而 scanCode 扫描码则是用来辅助区分的一个一个参数,例如一样是按下 ctrl 键,他们的 vkCode 是相同的可是 scanCode 却不一样。
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
|
#include <windows.h>
HHOOK
myhook;
// 保存当前钩子句柄
/****************************************************************
WH_KEYBOARD hook procedure
鍵盤钩子处理过程
****************************************************************/
LRESULT
CALLBACK KeyboardProc(
int
nCode,
WPARAM
wParam,
LPARAM
lParam)
{
char
text[50], data[20];
// 输出字符串
const
char
*info = NULL;
// 类型字符指针
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam;
// 获取按键消息
HDC
hdc;
// 画图设备句柄
// 判断是否收到键盘消息
if
(nCode >= 0)
{
// 判断消息类型
if
(wParam == WM_KEYDOWN) info =
"普通按鍵抬起"
;
else
if
(wParam == WM_KEYUP) info =
"普通按鍵按下"
;
else
if
(wParam == WM_SYSKEYDOWN) info =
"系統按鍵抬起"
;
else
if
(wParam == WM_SYSKEYUP) info =
"系統按鍵按下"
;
// 初始化数组
ZeroMemory(text,
sizeof
(text));
ZeroMemory(data,
sizeof
(data));
// 拼装字符串
wsprintf(text,
"%s - 键盘码 [%04d], 扫描码 [%04d] "
, info, p->vkCode, p->scanCode);
wsprintf(data,
"按鍵目测为: %c "
, p->vkCode);
// 此处调用 GDI 画图函数来将截取到的内容画在窗口上
hdc = GetDC(画图的窗口句柄);
// 获取要画图的设备句柄
TextOut(hdc, 10, 10, text,
strlen
(text));
// 在窗口上画文字
TextOut(hdc, 10, 30, data,
strlen
(data));
// 参数分别是 目标设备, x坐标, y坐标, 字符串内容, 字符串长度
ReleaseDC(画图的窗口句柄, hdc);
// 释放设备句柄
}
// 将消息继续往下传递
return
CallNextHookEx(myhook, nCode, wParam, lParam);
}
int
WINAPI WinMain(
HINSTANCE
hInstance,
HINSTANCE
hPrevInstance,
LPSTR
lpCmdLine,
int
nCmdShow)
{
/* 其余代码 */
// 设置键盘全局监听
myhook = SetWindowsHookEx(
WH_KEYBOARD_LL,
// 监听类型【键盘消息】
KeyboardProc,
// 处理函数
hInstance,
// 当前实例句柄
0
// 监听线程ID(NULL为全局监听)
);
// 判断是否成功
if
(myhook == NULL)
{
wsprintf(text,
"键盘监听失败!error : %d n"
, GetLastError());
MessageBox(hwnd, text, TEXT(
"错误"
), MB_OK);
}
/* 其余代码 */
}
|
注:其中在输出按键的时候,直接用 %c 输出了 p->vkCode 部分,这个实际上不是很准确。
顺便提一句,各位也不要用这个功能去作什么坏事,好比去监听QQ窗口的键盘消息而后偷到密码盗别人号之类的, 博主已经试过了 这个自己有带防御的,用户在输密码的时候,会有干扰的键盘消息也一块儿冒出来因此没那么简单能到到别人输的密码。至于写个程序去拦截别人的键盘还有鼠标消息让别人的电脑不能用的状况,这个确实很容易作到,并且貌似杀毒软件都没办法防,只能本身在本身的电脑上留个后门,怎么写后门?后面的网络编程会说这个。
键盘监听完整代码:
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
|
#include <windows.h>
HWND
hgWnd;
HHOOK
myhook;
/****************************************************************
WH_KEYBOARD hook procedure
鍵盤钩子处理过程
****************************************************************/
LRESULT
CALLBACK KeyboardProc(
int
nCode,
WPARAM
wParam,
LPARAM
lParam)
{
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam;
const
char
*info = NULL;
char
text[50], data[20];
PAINTSTRUCT ps;
HDC
hdc;
if
(nCode >= 0)
{
if
(wParam == WM_KEYDOWN) info =
"普通按鍵抬起"
;
else
if
(wParam == WM_KEYUP) info =
"普通按鍵按下"
;
else
if
(wParam == WM_SYSKEYDOWN) info =
"系統按鍵抬起"
;
else
if
(wParam == WM_SYSKEYUP) info =
"系統按鍵按下"
;
ZeroMemory(text,
sizeof
(text));
ZeroMemory(data,
sizeof
(data));
wsprintf(text,
"%s - 键盘码 [%04d], 扫描码 [%04d] "
, info, p->vkCode, p->scanCode);
wsprintf(data,
"按鍵目測為: %c "
, p->vkCode);
hdc = GetDC(hgWnd);
TextOut(hdc, 10, 10, text,
strlen
(text));
TextOut(hdc, 10, 30, data,
strlen
(data));
ReleaseDC(hgWnd,hdc);
}
return
CallNextHookEx(myhook, nCode, wParam, lParam);
}
// 5. 窗口过程处理
LRESULT
CALLBACK WndProc(
HWND
hwnd,
UINT
msg,
WPARAM
wParam,
LPARAM
lParam)
{
hgWnd = hwnd;
switch
(msg)
{
case
WM_CLOSE:
DestroyWindow(hwnd);
break
;
case
WM_DESTROY:
PostQuitMessage(0);
break
;
default
:
return
DefWindowProc(hwnd, msg, wParam, lParam);
}
return
0;
}
int
WINAPI WinMain(
HINSTANCE
hInstance,
HINSTANCE
hPrevInstance,
LPSTR
lpCmdLine,
int
nCmdShow)
{
HWND
hwnd;
MSG Msg;
char
text[30];
const
char
szClassName[] =
"myWindowClass"
;
// 1. 设置注册窗口结构体
wc.cbSize =
sizeof
(WNDCLASSEX);
// 注册窗口结构体的大小
wc.style = 0;
// 窗口的样式
wc.lpfnWndProc = WndProc;
// 指向窗口处理过程的函数指针
wc.cbClsExtra = 0;
// 指定紧跟在窗口类结构后的附加字节数
wc.cbWndExtra = 0;
// 指定紧跟在窗口事例后的附加字节数
wc.hInstance = hInstance;
// 本模块的实例句柄
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
// 图标的句柄
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
// 光标的句柄
wc.hbrBackground = (
HBRUSH
)(COLOR_WINDOW+1);
// 背景画刷的句柄
wc.lpszMenuName = NULL;
// 指向菜单的指针
wc.lpszClassName = szClassName;
// 指向类名称的指针
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
// 和窗口类关联的小图标
// 2. 使用【窗口结构体】注册窗口
if
(!RegisterClassEx(&wc))
{
MessageBox(NULL, TEXT(
"窗口注册失败!"
), TEXT(
"错误"
), MB_ICONEXCLAMATION | MB_OK);
return
0;
}
// 3. 建立窗口
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
// 窗口的扩展风格
szClassName,
// 指向注册类名的指针
TEXT(
"窗口标题"
),
// 指向窗口名称的指针
WS_OVERLAPPEDWINDOW,
// 窗口风格
CW_USEDEFAULT, CW_USEDEFAULT, 350, 200,
// 窗口的 x,y 坐标以及宽高
NULL,
// 父窗口的句柄
NULL,
// 菜单的句柄
hInstance,
// 应用程序实例的句柄
NULL
// 指向窗口的建立数据
);
if
(hwnd == NULL)
{
MessageBox(NULL, TEXT(
"窗口建立失败"
), TEXT(
"错误"
),MB_ICONEXCLAMATION | MB_OK);
return
0;
}
// 4. 显示窗口
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// 设置键盘全局监听
myhook = SetWindowsHookEx(
WH_KEYBOARD_LL,
// 监听类型【键盘】
KeyboardProc,
// 处理函数
hInstance,
// 当前实例句柄
0
// 监听窗口句柄(NULL为全局监听)
);
if
(myhook == NULL)
{
wsprintf(text,
"键盘监听失败!error : %d n"
, GetLastError());
MessageBox(hwnd, text, TEXT(
"错误"
), MB_OK);
}
// 5. 消息循环
while
(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return
Msg.wParam;
}
|
运行截图
设置监听【鼠标】消息
与键盘监听相似,各位直接看代码:
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
|
#include <windows.h>
HWND
hgWnd;
HHOOK
myhook;
/****************************************************************
WH_KEYBOARD hook procedure
鍵盤钩子处理过程
****************************************************************/
LRESULT
CALLBACK MouseProc(
int
nCode,
WPARAM
wParam,
LPARAM
lParam)
{
LPMSLLHOOKSTRUCT p = (LPMSLLHOOKSTRUCT)lParam;
POINT pt = p->pt;
DWORD
mouseData = p->mouseData;
const
char
*info = NULL;
char
text[60], pData[50], mData[50];
PAINTSTRUCT ps;
HDC
hdc;
if
(nCode >= 0)
{
if
(wParam == WM_MOUSEMOVE) info =
"鼠标移动 "
;
else
if
(wParam == WM_LBUTTONDOWN) info =
"鼠标【左键】按下"
;
else
if
(wParam == WM_LBUTTONUP) info =
"鼠标【左键】抬起"
;
else
if
(wParam == WM_LBUTTONDBLCLK) info =
"鼠标【左键】双击"
;
else
if
(wParam == WM_RBUTTONDOWN) info =
"鼠标【右键】按下"
;
else
if
(wParam == WM_RBUTTONUP) info =
"鼠标【右键】抬起"
;
else
if
(wParam == WM_RBUTTONDBLCLK) info =
"鼠标【右键】双击"
;
else
if
(wParam == WM_MBUTTONDOWN) info =
"鼠标【滚轮】按下"
;
else
if
(wParam == WM_MBUTTONUP) info =
"鼠标【滚轮】抬起"
;
else
if
(wParam == WM_MBUTTONDBLCLK) info =
"鼠标【滚轮】双击"
;
else
if
(wParam == WM_MOUSEWHEEL) info =
"鼠标【滚轮】滚动"
;
ZeroMemory(text,
sizeof
(text));
ZeroMemory(pData,
sizeof
(pData));
ZeroMemory(mData,
sizeof
(mData));
wsprintf( text,
"当前状态: %10s "
, info);
wsprintf(pData,
"0x%x - X: [%04d], Y: [%04d] "
, wParam, pt.x, pt.y);
wsprintf(mData,
"附带数据: %16u "
, mouseData);
hdc = GetDC(hgWnd);
TextOut(hdc, 10, 10, text,
strlen
(text));
TextOut(hdc, 10, 30, pData,
strlen
(pData));
TextOut(hdc, 10, 50, mData,
strlen
(mData));
ReleaseDC(hgWnd,hdc);
}
return
CallNextHookEx(myhook, nCode, wParam, lParam);
}
// 5. 窗口过程处理
LRESULT
CALLBACK WndProc(
HWND
hwnd,
UINT
msg,
WPARAM
wParam,
LPARAM
lParam)
{
hgWnd = hwnd;
switch
(msg)
{
case
WM_CLOSE:
DestroyWindow(hwnd);
break
;
case
WM_DESTROY:
PostQuitMessage(0);
break
;
default
:
return
DefWindowProc(hwnd, msg, wParam, lParam);
}
return
0;
}
int
WINAPI WinMain(
HINSTANCE
hInstance,
HINSTANCE
hPrevInstance,
LPSTR
lpCmdLine,
int
nCmdShow)
{
HWND
hwnd;
MSG Msg;
char
text[30];
const
char
szClassName[] =
"myWindowClass"
;
// 1. 设置注册窗口结构体
wc.cbSize =
sizeof
(WNDCLASSEX);
// 注册窗口结构体的大小
wc.style = 0;
// 窗口的样式
wc.lpfnWndProc = WndProc;
// 指向窗口处理过程的函数指针
wc.cbClsExtra = 0;
// 指定紧跟在窗口类结构后的附加字节数
wc.cbWndExtra = 0;
// 指定紧跟在窗口事例后的附加字节数
wc.hInstance = hInstance;
// 本模块的实例句柄
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
// 图标的句柄
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
// 光标的句柄
wc.hbrBackground = (
HBRUSH
)(COLOR_WINDOW+1);
// 背景画刷的句柄
wc.lpszMenuName = NULL;
// 指向菜单的指针
wc.lpszClassName = szClassName;
// 指向类名称的指针
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
// 和窗口类关联的小图标
// 2. 使用【窗口结构体】注册窗口
if
(!RegisterClassEx(&wc))
{
MessageBox(NULL, TEXT(
"窗口注册失败!"
), TEXT(
"错误"
), MB_ICONEXCLAMATION | MB_OK);
return
0;
}
// 3. 建立窗口
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
// 窗口的扩展风格
szClassName,
// 指向注册类名的指针
TEXT(
"窗口标题"
),
// 指向窗口名称的指针
WS_OVERLAPPEDWINDOW,
// 窗口风格
CW_USEDEFAULT, CW_USEDEFAULT, 350, 200,
// 窗口的 x,y 坐标以及宽高
NULL,
// 父窗口的句柄
NULL,
// 菜单的句柄
hInstance,
// 应用程序实例的句柄
NULL
// 指向窗口的建立数据
);
if
(hwnd == NULL)
{
MessageBox(NULL, TEXT(
"窗口建立失败"
), TEXT(
"错误"
),MB_ICONEXCLAMATION | MB_OK);
return
0;
}
// 4. 显示窗口
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// 设置鼠标全局监听
myhook = SetWindowsHookEx(
WH_MOUSE_LL,
// 监听类型【鼠标】
MouseProc,
// 处理函数
hInstance,
// 当前实例句柄
0
// 监听窗口句柄(NULL为全局监听)
);
if
(myhook == NULL)
{
wsprintf(text,
"键盘监听失败!error : %d n"
, GetLastError());
MessageBox(hwnd, text, TEXT(
"错误"
), MB_OK);
}
// 5. 消息循环
while
(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return
Msg.wParam;
}
|
上一讲: Windows API 教程(六) 动态连接库
下一讲: Windows API 教程(八) 注册快捷键