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

上一講: Windows API 教程(六) 動態鏈接庫
下一講: Windows API 教程(八) 注冊快捷鍵


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM