Windows API 教程(七) hook 钩子监听

简介:

如何创建一个窗口

另外一个再录的 Windows SDK教程 里面有讲到快捷创建窗口的方式,不过这样的话要分好几个文件,感觉有点混所以这里就用原始的方式创建一个窗口。

那么,为什么讲到 hook(钩子)的时候要去创建窗口呢?其实这个问题说起来也不复杂,简单点说,按博主这样写不用写DLL也不用资源文件,实际上是把问题简化了一些。通 常 hook 是用来监听自己窗口上的键盘和鼠标输入的,监听全局的通常是设置一些全局的热键(如QQ的 Ctrl+Alt+Z 调出QQ窗口),这些常见的功能也都是要依托窗口才能存在。所以我们先来简单说下手动建立一个窗口的流程。

手动创建窗口的流程

  1. 设置注册窗口结构体
  2. 使用【窗口结构体】注册窗口
  3. 创建窗口
  4. 显示窗口
  5. 窗口过程处理
  6. 消息循环

实际代码

这里不会详细讲这个,有感兴趣的可以去追博主的 SDK教程 或者去搜 杨中科的《C语言也能干大事》

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)
{
     WNDCLASSEX wc;  // 更多详细都可以去百度的 http://baike.baidu.com/view/1750396.htm
     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;
}

因为是比较死的形式,以上代码大家混个脸熟,大概知道各个部分的作用就行了,博主也从来没有专门记过。

安装钩子 (Install hook)

简介

窗口建好了之后就要开始转到我们的正题了,首先我们需要明确的是,这个钩子(hook)到底是什么,那么博主这里也不做太书面的解释留下几个链接:

百度百科:hook
MSDN: Hooks
博客园: Beginning HOOK

各位可以多多参考,那么博主说下自己的理解:

windows 系统中的【hook 机制】,就类似于一个【消息过滤网】,如果我们向操作系统申请并成功对某个窗口安装了一个【hook】指定了【回调函数】,那么这个【回调函数】也就相当 于我们人为对这个窗口添加了一个【消息过滤网】。此时当 windows 操作系统要对这个窗口发送任何消息的时候(例如按键、鼠标点击等消息)操作系统会先调用我们在【消息过滤网】中设置的【回调函数】去接受、处理、过滤等 等,当然如果你在【回调函数】中拿到了数据却没有继续传递给窗口的话,就相当于拦截了这些消息。

打个简单的比方,如果你在系统全局安装了一个【键盘消息】的钩子,并且在其指定的【回调函数】中没有把这个键盘消息继续传递给系统上的窗口,那么你 的所有【键盘消息】都被这个【hook】也就我们挂在这个【消息过滤网】上的【回调函数】给拦截了,这也意味着你的键盘会失灵。

SetWindowsHookEx 函数

那么 SetWindowsHookEx 函数就是我们用来在 windows 操作系统上安装钩子的函数,我们简单来看一下这个函数的原型:

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)
{
     WNDCLASSEX wc;  // http://baike.baidu.com/view/1750396.htm
     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)
{
     WNDCLASSEX wc;  // http://baike.baidu.com/view/1750396.htm
     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;
}

MouseHook

 

PS http://www.lellansin.com/windows-api-%E6%95%99%E7%A8%8B%EF%BC%88%E4%B8%83%EF%BC%89-hook-%E9%92%A9%E5%AD%90%E7%9B%91%E5%90%AC%EF%BC%88%E7%BC%96%E5%86%99%E4%B8%AD%EF%BC%89.html



本文转自莫水千流博客园博客,原文链接:http://www.cnblogs.com/zhoug2020/p/5743243.html,如需转载请自行联系原作者

目录
打赏
0
0
0
0
49
分享
相关文章
Python 中调用 DeepSeek-R1 API的方法介绍,图文教程
本教程详细介绍了如何使用 Python 调用 DeepSeek 的 R1 大模型 API,适合编程新手。首先登录 DeepSeek 控制台获取 API Key,安装 Python 和 requests 库后,编写基础调用代码并运行。文末包含常见问题解答和更简单的可视化调用方法,建议收藏备用。 原文链接:[如何使用 Python 调用 DeepSeek-R1 API?](https://apifox.com/apiskills/how-to-call-the-deepseek-r1-api-using-python/)
【重磅发布】 免费领取阿里云百炼AI大模型100万Tokens教程出炉,API接口实战操作,DeepSeek-R1满血版即刻体验!
阿里云百炼是一站式大模型开发及应用构建平台,支持开发者和业务人员轻松设计、构建大模型应用。通过简单操作,用户可在5分钟内开发出大模型应用或在几小时内训练专属模型,专注于创新。
1223 89
【重磅发布】 免费领取阿里云百炼AI大模型100万Tokens教程出炉,API接口实战操作,DeepSeek-R1满血版即刻体验!
Windows 环境下 Odoo 安装保姆级教程
本教程详细介绍了在 Windows 系统上通过虚拟机部署 Odoo 的完整流程。首先确认硬件需求,确保 CPU、内存和磁盘空间满足最低配置;接着安装 VMware Workstation Pro 并创建 Ubuntu 虚拟机,配置桥接网络以实现主机与虚拟机的通信;随后借助微聚云快速安装预配置好的 Odoo 环境,简化复杂环境搭建;最后通过浏览器访问虚拟机 IP,完成 Odoo 数据库初始化及基础设置。整个过程清晰易懂,适合新手快速上手 Odoo 部署。
90 4
Python 原生爬虫教程:京东商品列表页面数据API
京东商品列表API是电商大数据分析的重要工具,支持开发者、商家和研究人员获取京东平台商品数据。通过关键词搜索、分类筛选、价格区间等条件,可返回多维度商品信息(如名称、价格、销量等),适用于市场调研与推荐系统开发。本文介绍其功能并提供Python请求示例。接口采用HTTP GET/POST方式,支持分页、排序等功能,满足多样化数据需求。
👉「免费满血DeepSeek实战-联网搜索×Prompt秘籍|暨6平台横评」
满血 DeepSeek 免费用!支持联网搜索!创作声明:真人攥写-非AI生成,Written-By-Human-Not-By-AI
3193 10
👉「免费满血DeepSeek实战-联网搜索×Prompt秘籍|暨6平台横评」
Python 原生爬虫教程:京东商品详情页面数据API
本文介绍京东商品详情API在电商领域的应用价值及功能。该API通过商品ID获取详细信息,如基本信息、价格、库存、描述和用户评价等,支持HTTP请求(GET/POST),返回JSON或XML格式数据。对于商家优化策略、开发者构建应用(如比价网站)以及消费者快速了解商品均有重要意义。研究此API有助于推动电商业务创新与发展。
快速调用 Deepseek API!【超详细教程】
Deepseek 强大的功能,在本教程中,将指导您如何获取 DeepSeek API 密钥,并演示如何使用该密钥调用 DeepSeek API 以进行调试。
Windows平台GIMP 2.10下载教程:零基础入门高级图像编辑
GIMP(GNU Image Manipulation Program)是一款开源跨平台图像编辑工具,支持图层管理、高级修图、色彩校正等功能,广泛应用于平面设计和照片修复。其优势包括全功能免费、插件生态丰富(600+扩展插件)、硬件要求低(1GB内存即可流畅运行)。本文详细介绍GIMP的软件定位、安装流程、首次配置及常见问题解答,帮助用户快速上手并充分利用其强大功能。
Windows 7纯净版重装教程|附微软原版镜像下载+驱动安装避坑技巧
本文详细介绍如何安全、高效地重装电脑系统,解决蓝屏、崩溃等问题。基于10年经验,涵盖从官方镜像获取、启动盘制作、数据备份到系统部署的全流程,并针对老旧机型优化。提供驱动一键安装工具和系统激活指南,确保无后门风险。文中还列出常见问题解决方案及操作禁忌,帮助用户顺利完成系统重装,让电脑重获新生。建议收藏并转发给有需要的朋友,欢迎留言咨询疑难问题。
最新Typora1.9.5破解版下载与使用教程(Windows+Mac)
Typora是一款Markdown编辑器和阅读器。下载地址:[夸克网盘](https://pan.quark.cn/s/9d9d668fb859)。Windows版需替换`app.asart`文件并输入序列号激活;Mac版安装简单,下载后按提示操作,允许任何来源即可。具体步骤见文中图片说明。
297 1

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等