blob: 9b7dae43bcb143c474532a06b57438d42887cdcf [file] [log] [blame]
Constantin Kaplinsky7d86cd12006-05-11 04:39:38 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 *
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00003 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19// -=- wm_hooks.cxx
20//
21// Window Message Hooks Dynamic Link library
22
23#define _WIN32_WINNT 0x0400
24#include <tchar.h>
25
26#include <wm_hooks/wm_hooks.h>
27
28UINT WM_HK_PingThread = RegisterWindowMessage(_T("RFB.WM_Hooks.PingThread"));
29
30UINT WM_HK_WindowChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowChanged"));
31UINT WM_Hooks_WindowChanged() {
32 return WM_HK_WindowChanged;
33}
34
35UINT WM_HK_WindowClientAreaChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowClientAreaChanged"));
36UINT WM_Hooks_WindowClientAreaChanged() {
37 return WM_HK_WindowClientAreaChanged;
38}
39
40UINT WM_HK_WindowBorderChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowBorderChanged"));
41UINT WM_Hooks_WindowBorderChanged() {
42 return WM_HK_WindowBorderChanged;
43}
44
45UINT WM_HK_RectangleChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.RectangleChanged"));
46UINT WM_Hooks_RectangleChanged() {
47 return WM_HK_RectangleChanged;
48}
49
50UINT WM_HK_CursorChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.CursorChanged"));
51UINT WM_Hooks_CursorChanged() {
52 return WM_HK_CursorChanged;
53}
54
55#ifdef _DEBUG
56UINT WM_HK_Diagnostic = RegisterWindowMessage(_T("RFB.WM_Hooks.Diagnostic"));
57UINT WM_Hooks_Diagnostic() {
58 return WM_HK_Diagnostic;
59}
60#endif
61
62ATOM ATOM_Popup_Selection = GlobalAddAtom(_T("RFB.WM_Hooks.PopupSelectionAtom"));
63
64//
65// -=- DLL entry point
66//
67
68HINSTANCE dll_instance = 0;
69
70BOOL WINAPI DllMain(HANDLE instance, ULONG reason, LPVOID reserved) {
71 switch (reason) {
72 case DLL_PROCESS_ATTACH:
73 dll_instance = (HINSTANCE)instance;
74 return TRUE;
75 case DLL_PROCESS_DETACH:
76 return TRUE;
77 case DLL_THREAD_DETACH:
78 WM_Hooks_Remove(GetCurrentThreadId());
79 return TRUE;
80 default:
81 return TRUE;
82 };
83}
84
85//
86// -=- Display update hooks
87//
88
89#pragma data_seg(".WM_Hooks_Shared")
90DWORD hook_owner = 0;
91DWORD hook_target = 0;
92HHOOK hook_CallWndProc = 0;
93HHOOK hook_CallWndProcRet = 0;
94HHOOK hook_GetMessage = 0;
95HHOOK hook_DialogMessage = 0;
96BOOL enable_cursor_shape = FALSE;
97HCURSOR cursor = 0;
98#ifdef _DEBUG
99UINT diagnostic_min=1;
100UINT diagnostic_max=0;
101#endif
102#pragma data_seg()
103
104#ifdef _DEBUG
105DLLEXPORT void WM_Hooks_SetDiagnosticRange(UINT min, UINT max) {
106 diagnostic_min = min; diagnostic_max=max;
107}
108#endif
109
110bool NotifyHookOwner(UINT event, WPARAM wParam, LPARAM lParam) {
111 if (hook_owner) {
112 return PostThreadMessage(hook_owner, event, wParam, lParam)!=0;
113 /*
114 if (last_event)
115 return PostThreadMessage(hook_owner, last_event, last_wParam, last_lParam);
116 last_event = event;
117 last_wParam = wParam;
118 last_lParam = lParam;
119 return true;
120 */
121 }
122 return false;
123}
124
125bool NotifyWindow(HWND hwnd, UINT msg) {
126 return NotifyHookOwner(WM_HK_WindowChanged, msg, (LPARAM)hwnd);
127}
128bool NotifyWindowBorder(HWND hwnd, UINT msg) {
129 return NotifyHookOwner(WM_HK_WindowBorderChanged, msg, (LPARAM)hwnd);
130}
131bool NotifyWindowClientArea(HWND hwnd, UINT msg) {
132 return NotifyHookOwner(WM_HK_WindowClientAreaChanged, msg, (LPARAM)hwnd);
133}
134bool NotifyRectangle(RECT* rect) {
135 WPARAM w = MAKELONG((SHORT)rect->left, (SHORT)rect->top);
136 LPARAM l = MAKELONG((SHORT)rect->right, (SHORT)rect->bottom);
137 return NotifyHookOwner(WM_HK_RectangleChanged, w, l);
138}
139bool NotifyCursor(HCURSOR cursor) {
140 return NotifyHookOwner(WM_HK_CursorChanged, 0, (LPARAM)cursor);
141}
142
143void ProcessWindowMessage(UINT msg, HWND wnd, WPARAM wParam, LPARAM lParam) {
144#ifdef _DEBUG
145 if ((msg >= diagnostic_min) && (msg <= diagnostic_max))
146 PostThreadMessage(hook_owner, WM_HK_Diagnostic, msg, (LPARAM)wnd);
147#endif
148 if (!IsWindowVisible(wnd)) return;
149 switch (msg) {
150
151 // -=- Border update events
152 case WM_NCPAINT:
153 case WM_NCACTIVATE:
154 NotifyWindowBorder(wnd, msg);
155 break;
156
157 // -=- Client area update events
158 case BM_SETCHECK:
159 case BM_SETSTATE:
160 case EM_SETSEL:
161 case WM_CHAR:
162 case WM_ENABLE:
163 case WM_KEYUP:
164 case WM_LBUTTONUP:
165 case WM_MBUTTONUP:
166 case WM_PALETTECHANGED:
167 case WM_RBUTTONUP:
168 case WM_SYSCOLORCHANGE:
169 case WM_SETTEXT:
170 case WM_SETFOCUS:
171 //case WM_TIMER:
172 NotifyWindowClientArea(wnd, msg);
173 break;
174 case WM_HSCROLL:
175 case WM_VSCROLL:
176 if (((int) LOWORD(wParam) == SB_THUMBTRACK) || ((int) LOWORD(wParam) == SB_ENDSCROLL))
177 NotifyWindow(wnd, msg);
178 break;
179
180 case WM_WINDOWPOSCHANGING:
181 case WM_DESTROY:
182 {
183 RECT wrect;
184 if (GetWindowRect(wnd, &wrect)) {
185 NotifyRectangle(&wrect);
186 }
187 }
188 break;
189
Constantin Kaplinsky7d86cd12006-05-11 04:39:38 +0000190 case WM_WINDOWPOSCHANGED:
191 NotifyWindow(wnd, msg);
192 break;
193
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000194 case WM_PAINT:
195 // *** could improve this
196 NotifyWindowClientArea(wnd, msg);
197 break;
198
199 // Handle pop-up menus appearing
200 case 482:
201 NotifyWindow(wnd, 482);
202 break;
203
204 // Handle pop-up menus having items selected
205 case 485:
206 {
207 HANDLE prop = GetProp(wnd, (LPCTSTR) MAKELONG(ATOM_Popup_Selection, 0));
208 if (prop != (HANDLE) wParam) {
209 NotifyWindow(wnd, 485);
210 SetProp(wnd,
211 (LPCTSTR) MAKELONG(ATOM_Popup_Selection, 0),
212 (HANDLE) wParam);
213 }
214 }
215 break;
216
217 case WM_NCMOUSEMOVE:
218 case WM_MOUSEMOVE:
219 if (enable_cursor_shape) {
220 HCURSOR new_cursor = GetCursor();
221 if (new_cursor != cursor) {
222 cursor = new_cursor;
223 NotifyCursor(cursor);
224 }
225 }
226 break;
227
228 /* ***
229 if (prf_use_GetUpdateRect)
230 {
231 HRGN region;
232 region = CreateRectRgn(0, 0, 0, 0);
233
234 // Get the affected region
235 if (GetUpdateRgn(hWnd, region, FALSE) != ERROR)
236 {
237 int buffsize;
238 UINT x;
239 RGNDATA *buff;
240 POINT TopLeft;
241
242 // Get the top-left point of the client area
243 TopLeft.x = 0;
244 TopLeft.y = 0;
245 if (!ClientToScreen(hWnd, &TopLeft))
246 break;
247
248 // Get the size of buffer required
249 buffsize = GetRegionData(region, 0, 0);
250 if (buffsize != 0)
251 {
252 buff = (RGNDATA *) new BYTE [buffsize];
253 if (buff == NULL)
254 break;
255
256 // Now get the region data
257 if(GetRegionData(region, buffsize, buff))
258 {
259 for (x=0; x<(buff->rdh.nCount); x++)
260 {
261 // Obtain the rectangles from the list
262 RECT *urect = (RECT *) (((BYTE *) buff) + sizeof(RGNDATAHEADER) + (x * sizeof(RECT)));
263 SendDeferredUpdateRect(
264 hWnd,
265 (SHORT) (TopLeft.x + urect->left),
266 (SHORT) (TopLeft.y + urect->top),
267 (SHORT) (TopLeft.x + urect->right),
268 (SHORT) (TopLeft.y + urect->bottom)
269 );
270 }
271 }
272
273 delete [] buff;
274 }
275 }
276
277 // Now free the region
278 if (region != NULL)
279 DeleteObject(region);
280 }
281 */
282 };
283}
284
285LRESULT CALLBACK HookCallWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
286 if (nCode == HC_ACTION) {
287 CWPSTRUCT* info = (CWPSTRUCT*) lParam;
288 ProcessWindowMessage(info->message, info->hwnd, info->wParam, info->lParam);
289 }
290 return CallNextHookEx(hook_CallWndProc, nCode, wParam, lParam);
291}
292
293LRESULT CALLBACK HookCallWndProcRet(int nCode, WPARAM wParam, LPARAM lParam) {
294 if (nCode == HC_ACTION) {
295 CWPRETSTRUCT* info = (CWPRETSTRUCT*) lParam;
296 ProcessWindowMessage(info->message, info->hwnd, info->wParam, info->lParam);
297 }
298 return CallNextHookEx(hook_CallWndProcRet, nCode, wParam, lParam);
299}
300
301LRESULT CALLBACK HookGetMessage(int nCode, WPARAM wParam, LPARAM lParam) {
302 if (nCode == HC_ACTION) {
303 if (wParam & PM_REMOVE) {
304 MSG* msg = (MSG*) lParam;
305 ProcessWindowMessage(msg->message, msg->hwnd, msg->wParam, msg->lParam);
306 }
307 }
308 return CallNextHookEx(hook_GetMessage, nCode, wParam, lParam);
309}
310
311LRESULT CALLBACK HookDialogMessage(int nCode, WPARAM wParam, LPARAM lParam) {
312 if (nCode == HC_ACTION) {
313 MSG* msg = (MSG*) lParam;
314 ProcessWindowMessage(msg->message, msg->hwnd, msg->wParam, msg->lParam);
315 }
316 return CallNextHookEx(hook_DialogMessage, nCode, wParam, lParam);
317}
318
319// - WM_Hooks_Install
320
321BOOL WM_Hooks_Install(DWORD owner, DWORD thread) {
322 // - Are there already hooks set?
323 if (hook_owner) {
324 if (!PostThreadMessage(hook_owner, WM_HK_PingThread, 0, 0)) {
325 WM_Hooks_Remove(hook_owner);
326 } else {
327 return FALSE;
328 }
329 }
330
331 // - Initialise the hooks
332 hook_owner = owner;
333 hook_target = thread;
334
335 hook_CallWndProc = SetWindowsHookEx(WH_CALLWNDPROC, HookCallWndProc, dll_instance, thread);
Constantin Kaplinsky7d86cd12006-05-11 04:39:38 +0000336 hook_CallWndProcRet = SetWindowsHookEx(WH_CALLWNDPROCRET, HookCallWndProcRet, dll_instance, thread);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000337 hook_GetMessage = SetWindowsHookEx(WH_GETMESSAGE, HookGetMessage, dll_instance, thread);
338 hook_DialogMessage = SetWindowsHookEx(WH_SYSMSGFILTER, HookDialogMessage, dll_instance, thread);
339
340 if (!hook_CallWndProc /*|| !hook_CallWndProcRet*/ || !hook_GetMessage || !hook_DialogMessage) {
341 WM_Hooks_Remove(owner);
342 return FALSE;
343 }
344
345 return TRUE;
346}
347
348// - WM_Hooks_Remove
349
350BOOL WM_Hooks_Remove(DWORD owner) {
351 if (owner != hook_owner) return FALSE;
352 if (hook_CallWndProc) {
353 UnhookWindowsHookEx(hook_CallWndProc);
354 hook_CallWndProc = 0;
355 }
356 if (hook_CallWndProcRet) {
357 UnhookWindowsHookEx(hook_CallWndProcRet);
358 hook_CallWndProcRet = 0;
359 }
360 if (hook_GetMessage) {
361 UnhookWindowsHookEx(hook_GetMessage);
362 hook_GetMessage = 0;
363 }
364 if (hook_DialogMessage) {
365 UnhookWindowsHookEx(hook_DialogMessage);
366 hook_DialogMessage = 0;
367 }
368 hook_owner = 0;
369 hook_target = 0;
370 return TRUE;
371}
372
373//
374// -=- User input hooks
375//
376
377#pragma data_seg(".WM_Hooks_Shared")
378HHOOK hook_keyboard = 0;
379HHOOK hook_pointer = 0;
380bool enable_real_ptr = true;
381bool enable_synth_ptr = true;
382bool enable_real_kbd = true;
383bool enable_synth_kbd = true;
384#pragma data_seg()
385
386#ifdef WH_KEYBOARD_LL
387LRESULT CALLBACK HookKeyboardHook(int nCode, WPARAM wParam, LPARAM lParam) {
388 if (nCode >= 0) {
389 KBDLLHOOKSTRUCT* info = (KBDLLHOOKSTRUCT*) lParam;
390 bool real_event = (info->flags & LLKHF_INJECTED) == 0;
391 if ((real_event && !enable_real_kbd) ||
392 (!real_event && !enable_synth_kbd)) {
393 return 1;
394 }
395 }
396 return CallNextHookEx(hook_keyboard, nCode, wParam, lParam);
397}
398
399LRESULT CALLBACK HookPointerHook(int nCode, WPARAM wParam, LPARAM lParam) {
400 if (nCode >= 0) {
401 MSLLHOOKSTRUCT* info = (MSLLHOOKSTRUCT*) lParam;
402 bool real_event = (info->flags & LLMHF_INJECTED) == 0;
403 if ((real_event && !enable_real_ptr) ||
404 (!real_event && !enable_synth_ptr)) {
405 return 1;
406 }
407 }
408 return CallNextHookEx(hook_keyboard, nCode, wParam, lParam);
409}
410
411bool RefreshInputHooks() {
412 bool success = true;
413 bool set_ptr_hook = !enable_real_ptr || !enable_synth_ptr;
414 bool set_kbd_hook = !enable_real_kbd || !enable_synth_kbd;
415 if (hook_keyboard && !set_kbd_hook) {
416 UnhookWindowsHookEx(hook_keyboard);
417 hook_keyboard = 0;
418 }
419 if (hook_pointer && !set_ptr_hook) {
420 UnhookWindowsHookEx(hook_pointer);
421 hook_pointer = 0;
422 }
423 if (!hook_keyboard && set_kbd_hook) {
424 hook_keyboard = SetWindowsHookEx(WH_KEYBOARD_LL, HookKeyboardHook, dll_instance, 0);
425 if (!hook_keyboard) success = false;
426 }
427 if (!hook_pointer && set_ptr_hook) {
428 hook_pointer = SetWindowsHookEx(WH_MOUSE_LL, HookPointerHook, dll_instance, 0);
429 if (!hook_pointer) success = false;
430 }
431 return success;
432}
433#else
Constantin Kaplinsky7d86cd12006-05-11 04:39:38 +0000434#pragma message(" NOTE: low-level mouse and keyboard hooks not supported")
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000435#endif
436
437// - WM_Hooks_EnableRealInputs
438
439BOOL WM_Hooks_EnableRealInputs(BOOL pointer, BOOL keyboard) {
440#ifdef WH_KEYBOARD_LL
441 enable_real_ptr = pointer!=0;
442 enable_real_kbd = keyboard!=0;
443 return RefreshInputHooks();
444#else
445 return FALSE;
446#endif
447}
448
449// - WM_Hooks_EnableSynthInputs
450
451BOOL WM_Hooks_EnableSynthInputs(BOOL pointer, BOOL keyboard) {
452#ifdef WH_KEYBOARD_LL
453 enable_synth_ptr = pointer!=0;
454 enable_synth_kbd = keyboard!=0;
455 return RefreshInputHooks();
456#else
457 return FALSE;
458#endif
459}
460
461// - WM_Hooks_EnableCursorShape
462
463BOOL WM_Hooks_EnableCursorShape(BOOL enable) {
464 enable_cursor_shape = enable;
Constantin Kaplinsky7d86cd12006-05-11 04:39:38 +0000465 return TRUE;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000466}