You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

wm_hooks.cxx 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. *
  3. * 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. // -=- wm_hooks.cxx
  19. //
  20. // Window Message Hooks Dynamic Link library
  21. #ifdef HAVE_CONFIG_H
  22. #include <config.h>
  23. #endif
  24. #include <tchar.h>
  25. #include <wm_hooks/wm_hooks.h>
  26. #include <os/os.h>
  27. #define SHARED __attribute__((section ("shared"), shared))
  28. UINT WM_HK_PingThread = RegisterWindowMessage(_T("RFB.WM_Hooks.PingThread"));
  29. UINT WM_HK_WindowChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowChanged"));
  30. UINT WM_Hooks_WindowChanged() {
  31. return WM_HK_WindowChanged;
  32. }
  33. UINT WM_HK_WindowClientAreaChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowClientAreaChanged"));
  34. UINT WM_Hooks_WindowClientAreaChanged() {
  35. return WM_HK_WindowClientAreaChanged;
  36. }
  37. UINT WM_HK_WindowBorderChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowBorderChanged"));
  38. UINT WM_Hooks_WindowBorderChanged() {
  39. return WM_HK_WindowBorderChanged;
  40. }
  41. UINT WM_HK_RectangleChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.RectangleChanged"));
  42. UINT WM_Hooks_RectangleChanged() {
  43. return WM_HK_RectangleChanged;
  44. }
  45. UINT WM_HK_CursorChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.CursorChanged"));
  46. UINT WM_Hooks_CursorChanged() {
  47. return WM_HK_CursorChanged;
  48. }
  49. #ifdef _DEBUG
  50. UINT WM_HK_Diagnostic = RegisterWindowMessage(_T("RFB.WM_Hooks.Diagnostic"));
  51. UINT WM_Hooks_Diagnostic() {
  52. return WM_HK_Diagnostic;
  53. }
  54. #endif
  55. ATOM ATOM_Popup_Selection = GlobalAddAtom(_T("RFB.WM_Hooks.PopupSelectionAtom"));
  56. //
  57. // -=- DLL entry point
  58. //
  59. HINSTANCE dll_instance = 0;
  60. BOOL WINAPI DllMain(HANDLE instance, ULONG reason, LPVOID /*reserved*/) {
  61. switch (reason) {
  62. case DLL_PROCESS_ATTACH:
  63. dll_instance = (HINSTANCE)instance;
  64. return TRUE;
  65. case DLL_PROCESS_DETACH:
  66. return TRUE;
  67. case DLL_THREAD_DETACH:
  68. WM_Hooks_Remove(GetCurrentThreadId());
  69. return TRUE;
  70. default:
  71. return TRUE;
  72. };
  73. }
  74. //
  75. // -=- Display update hooks
  76. //
  77. DWORD hook_owner SHARED = 0;
  78. DWORD hook_target SHARED = 0;
  79. HHOOK hook_CallWndProc SHARED = 0;
  80. HHOOK hook_CallWndProcRet SHARED = 0;
  81. HHOOK hook_GetMessage SHARED = 0;
  82. HHOOK hook_DialogMessage SHARED = 0;
  83. BOOL enable_cursor_shape SHARED = FALSE;
  84. HCURSOR cursor SHARED = 0;
  85. #ifdef _DEBUG
  86. UINT diagnostic_min SHARED =1;
  87. UINT diagnostic_max SHARED =0;
  88. #endif
  89. #ifdef _DEBUG
  90. DLLEXPORT void WM_Hooks_SetDiagnosticRange(UINT min, UINT max) {
  91. diagnostic_min = min; diagnostic_max=max;
  92. }
  93. #endif
  94. bool NotifyHookOwner(UINT event, WPARAM wParam, LPARAM lParam) {
  95. if (hook_owner) {
  96. return PostThreadMessage(hook_owner, event, wParam, lParam)!=0;
  97. /*
  98. if (last_event)
  99. return PostThreadMessage(hook_owner, last_event, last_wParam, last_lParam);
  100. last_event = event;
  101. last_wParam = wParam;
  102. last_lParam = lParam;
  103. return true;
  104. */
  105. }
  106. return false;
  107. }
  108. bool NotifyWindow(HWND hwnd, UINT msg) {
  109. return NotifyHookOwner(WM_HK_WindowChanged, msg, (LPARAM)hwnd);
  110. }
  111. bool NotifyWindowBorder(HWND hwnd, UINT msg) {
  112. return NotifyHookOwner(WM_HK_WindowBorderChanged, msg, (LPARAM)hwnd);
  113. }
  114. bool NotifyWindowClientArea(HWND hwnd, UINT msg) {
  115. return NotifyHookOwner(WM_HK_WindowClientAreaChanged, msg, (LPARAM)hwnd);
  116. }
  117. bool NotifyRectangle(RECT* rect) {
  118. WPARAM w = MAKELONG((SHORT)rect->left, (SHORT)rect->top);
  119. LPARAM l = MAKELONG((SHORT)rect->right, (SHORT)rect->bottom);
  120. return NotifyHookOwner(WM_HK_RectangleChanged, w, l);
  121. }
  122. bool NotifyCursor(HCURSOR cursor) {
  123. return NotifyHookOwner(WM_HK_CursorChanged, 0, (LPARAM)cursor);
  124. }
  125. void ProcessWindowMessage(UINT msg, HWND wnd, WPARAM wParam, LPARAM /*lParam*/) {
  126. #ifdef _DEBUG
  127. if ((msg >= diagnostic_min) && (msg <= diagnostic_max))
  128. PostThreadMessage(hook_owner, WM_HK_Diagnostic, msg, (LPARAM)wnd);
  129. #endif
  130. if (!IsWindowVisible(wnd)) return;
  131. switch (msg) {
  132. // -=- Border update events
  133. case WM_NCPAINT:
  134. case WM_NCACTIVATE:
  135. NotifyWindowBorder(wnd, msg);
  136. break;
  137. // -=- Client area update events
  138. case BM_SETCHECK:
  139. case BM_SETSTATE:
  140. case EM_SETSEL:
  141. case WM_CHAR:
  142. case WM_ENABLE:
  143. case WM_KEYUP:
  144. case WM_LBUTTONUP:
  145. case WM_MBUTTONUP:
  146. case WM_PALETTECHANGED:
  147. case WM_RBUTTONUP:
  148. case WM_SYSCOLORCHANGE:
  149. case WM_SETTEXT:
  150. case WM_SETFOCUS:
  151. //case WM_TIMER:
  152. NotifyWindowClientArea(wnd, msg);
  153. break;
  154. case WM_HSCROLL:
  155. case WM_VSCROLL:
  156. if (((int) LOWORD(wParam) == SB_THUMBTRACK) || ((int) LOWORD(wParam) == SB_ENDSCROLL))
  157. NotifyWindow(wnd, msg);
  158. break;
  159. case WM_WINDOWPOSCHANGING:
  160. case WM_DESTROY:
  161. {
  162. RECT wrect;
  163. if (GetWindowRect(wnd, &wrect)) {
  164. NotifyRectangle(&wrect);
  165. }
  166. }
  167. break;
  168. case WM_WINDOWPOSCHANGED:
  169. NotifyWindow(wnd, msg);
  170. break;
  171. case WM_PAINT:
  172. // *** could improve this
  173. NotifyWindowClientArea(wnd, msg);
  174. break;
  175. // Handle pop-up menus appearing
  176. case 482:
  177. NotifyWindow(wnd, 482);
  178. break;
  179. // Handle pop-up menus having items selected
  180. case 485:
  181. {
  182. HANDLE prop = GetProp(wnd, (LPCTSTR) (intptr_t) ATOM_Popup_Selection);
  183. if (prop != (HANDLE) wParam) {
  184. NotifyWindow(wnd, 485);
  185. SetProp(wnd,
  186. (LPCTSTR) (intptr_t) ATOM_Popup_Selection,
  187. (HANDLE) wParam);
  188. }
  189. }
  190. break;
  191. case WM_NCMOUSEMOVE:
  192. case WM_MOUSEMOVE:
  193. if (enable_cursor_shape) {
  194. HCURSOR new_cursor = GetCursor();
  195. if (new_cursor != cursor) {
  196. cursor = new_cursor;
  197. NotifyCursor(cursor);
  198. }
  199. }
  200. break;
  201. /* ***
  202. if (prf_use_GetUpdateRect)
  203. {
  204. HRGN region;
  205. region = CreateRectRgn(0, 0, 0, 0);
  206. // Get the affected region
  207. if (GetUpdateRgn(hWnd, region, FALSE) != ERROR)
  208. {
  209. int buffsize;
  210. UINT x;
  211. RGNDATA *buff;
  212. POINT TopLeft;
  213. // Get the top-left point of the client area
  214. TopLeft.x = 0;
  215. TopLeft.y = 0;
  216. if (!ClientToScreen(hWnd, &TopLeft))
  217. break;
  218. // Get the size of buffer required
  219. buffsize = GetRegionData(region, 0, 0);
  220. if (buffsize != 0)
  221. {
  222. buff = (RGNDATA *) new BYTE [buffsize];
  223. if (buff == NULL)
  224. break;
  225. // Now get the region data
  226. if(GetRegionData(region, buffsize, buff))
  227. {
  228. for (x=0; x<(buff->rdh.nCount); x++)
  229. {
  230. // Obtain the rectangles from the list
  231. RECT *urect = (RECT *) (((BYTE *) buff) + sizeof(RGNDATAHEADER) + (x * sizeof(RECT)));
  232. SendDeferredUpdateRect(
  233. hWnd,
  234. (SHORT) (TopLeft.x + urect->left),
  235. (SHORT) (TopLeft.y + urect->top),
  236. (SHORT) (TopLeft.x + urect->right),
  237. (SHORT) (TopLeft.y + urect->bottom)
  238. );
  239. }
  240. }
  241. delete [] buff;
  242. }
  243. }
  244. // Now free the region
  245. if (region != NULL)
  246. DeleteObject(region);
  247. }
  248. */
  249. };
  250. }
  251. LRESULT CALLBACK HookCallWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
  252. if (nCode == HC_ACTION) {
  253. CWPSTRUCT* info = (CWPSTRUCT*) lParam;
  254. ProcessWindowMessage(info->message, info->hwnd, info->wParam, info->lParam);
  255. }
  256. return CallNextHookEx(hook_CallWndProc, nCode, wParam, lParam);
  257. }
  258. LRESULT CALLBACK HookCallWndProcRet(int nCode, WPARAM wParam, LPARAM lParam) {
  259. if (nCode == HC_ACTION) {
  260. CWPRETSTRUCT* info = (CWPRETSTRUCT*) lParam;
  261. ProcessWindowMessage(info->message, info->hwnd, info->wParam, info->lParam);
  262. }
  263. return CallNextHookEx(hook_CallWndProcRet, nCode, wParam, lParam);
  264. }
  265. LRESULT CALLBACK HookGetMessage(int nCode, WPARAM wParam, LPARAM lParam) {
  266. if (nCode == HC_ACTION) {
  267. if (wParam & PM_REMOVE) {
  268. MSG* msg = (MSG*) lParam;
  269. ProcessWindowMessage(msg->message, msg->hwnd, msg->wParam, msg->lParam);
  270. }
  271. }
  272. return CallNextHookEx(hook_GetMessage, nCode, wParam, lParam);
  273. }
  274. LRESULT CALLBACK HookDialogMessage(int nCode, WPARAM wParam, LPARAM lParam) {
  275. if (nCode == HC_ACTION) {
  276. MSG* msg = (MSG*) lParam;
  277. ProcessWindowMessage(msg->message, msg->hwnd, msg->wParam, msg->lParam);
  278. }
  279. return CallNextHookEx(hook_DialogMessage, nCode, wParam, lParam);
  280. }
  281. // - WM_Hooks_Install
  282. BOOL WM_Hooks_Install(DWORD owner, DWORD thread) {
  283. // - Are there already hooks set?
  284. if (hook_owner) {
  285. if (!PostThreadMessage(hook_owner, WM_HK_PingThread, 0, 0)) {
  286. WM_Hooks_Remove(hook_owner);
  287. } else {
  288. return FALSE;
  289. }
  290. }
  291. // - Initialise the hooks
  292. hook_owner = owner;
  293. hook_target = thread;
  294. hook_CallWndProc = SetWindowsHookEx(WH_CALLWNDPROC, HookCallWndProc, dll_instance, thread);
  295. hook_CallWndProcRet = SetWindowsHookEx(WH_CALLWNDPROCRET, HookCallWndProcRet, dll_instance, thread);
  296. hook_GetMessage = SetWindowsHookEx(WH_GETMESSAGE, HookGetMessage, dll_instance, thread);
  297. hook_DialogMessage = SetWindowsHookEx(WH_SYSMSGFILTER, HookDialogMessage, dll_instance, thread);
  298. if (!hook_CallWndProc /*|| !hook_CallWndProcRet*/ || !hook_GetMessage || !hook_DialogMessage) {
  299. WM_Hooks_Remove(owner);
  300. return FALSE;
  301. }
  302. return TRUE;
  303. }
  304. // - WM_Hooks_Remove
  305. BOOL WM_Hooks_Remove(DWORD owner) {
  306. if (owner != hook_owner) return FALSE;
  307. if (hook_CallWndProc) {
  308. UnhookWindowsHookEx(hook_CallWndProc);
  309. hook_CallWndProc = 0;
  310. }
  311. if (hook_CallWndProcRet) {
  312. UnhookWindowsHookEx(hook_CallWndProcRet);
  313. hook_CallWndProcRet = 0;
  314. }
  315. if (hook_GetMessage) {
  316. UnhookWindowsHookEx(hook_GetMessage);
  317. hook_GetMessage = 0;
  318. }
  319. if (hook_DialogMessage) {
  320. UnhookWindowsHookEx(hook_DialogMessage);
  321. hook_DialogMessage = 0;
  322. }
  323. hook_owner = 0;
  324. hook_target = 0;
  325. return TRUE;
  326. }
  327. //
  328. // -=- User input hooks
  329. //
  330. HHOOK hook_keyboard SHARED = 0;
  331. HHOOK hook_pointer SHARED = 0;
  332. bool enable_real_ptr SHARED = true;
  333. bool enable_synth_ptr SHARED = true;
  334. bool enable_real_kbd SHARED = true;
  335. bool enable_synth_kbd SHARED = true;
  336. #ifdef WH_KEYBOARD_LL
  337. LRESULT CALLBACK HookKeyboardHook(int nCode, WPARAM wParam, LPARAM lParam) {
  338. if (nCode >= 0) {
  339. KBDLLHOOKSTRUCT* info = (KBDLLHOOKSTRUCT*) lParam;
  340. bool real_event = (info->flags & LLKHF_INJECTED) == 0;
  341. if ((real_event && !enable_real_kbd) ||
  342. (!real_event && !enable_synth_kbd)) {
  343. return 1;
  344. }
  345. }
  346. return CallNextHookEx(hook_keyboard, nCode, wParam, lParam);
  347. }
  348. LRESULT CALLBACK HookPointerHook(int nCode, WPARAM wParam, LPARAM lParam) {
  349. if (nCode >= 0) {
  350. MSLLHOOKSTRUCT* info = (MSLLHOOKSTRUCT*) lParam;
  351. bool real_event = (info->flags & LLMHF_INJECTED) == 0;
  352. if ((real_event && !enable_real_ptr) ||
  353. (!real_event && !enable_synth_ptr)) {
  354. return 1;
  355. }
  356. }
  357. return CallNextHookEx(hook_keyboard, nCode, wParam, lParam);
  358. }
  359. bool RefreshInputHooks() {
  360. bool success = true;
  361. bool set_ptr_hook = !enable_real_ptr || !enable_synth_ptr;
  362. bool set_kbd_hook = !enable_real_kbd || !enable_synth_kbd;
  363. if (hook_keyboard && !set_kbd_hook) {
  364. UnhookWindowsHookEx(hook_keyboard);
  365. hook_keyboard = 0;
  366. }
  367. if (hook_pointer && !set_ptr_hook) {
  368. UnhookWindowsHookEx(hook_pointer);
  369. hook_pointer = 0;
  370. }
  371. if (!hook_keyboard && set_kbd_hook) {
  372. hook_keyboard = SetWindowsHookEx(WH_KEYBOARD_LL, HookKeyboardHook, dll_instance, 0);
  373. if (!hook_keyboard) success = false;
  374. }
  375. if (!hook_pointer && set_ptr_hook) {
  376. hook_pointer = SetWindowsHookEx(WH_MOUSE_LL, HookPointerHook, dll_instance, 0);
  377. if (!hook_pointer) success = false;
  378. }
  379. return success;
  380. }
  381. #else
  382. #pragma message(" NOTE: low-level mouse and keyboard hooks not supported")
  383. #endif
  384. // - WM_Hooks_EnableRealInputs
  385. BOOL WM_Hooks_EnableRealInputs(BOOL pointer, BOOL keyboard) {
  386. #ifdef WH_KEYBOARD_LL
  387. enable_real_ptr = pointer!=0;
  388. enable_real_kbd = keyboard!=0;
  389. return RefreshInputHooks();
  390. #else
  391. return FALSE;
  392. #endif
  393. }
  394. // - WM_Hooks_EnableSynthInputs
  395. BOOL WM_Hooks_EnableSynthInputs(BOOL pointer, BOOL keyboard) {
  396. #ifdef WH_KEYBOARD_LL
  397. enable_synth_ptr = pointer!=0;
  398. enable_synth_kbd = keyboard!=0;
  399. return RefreshInputHooks();
  400. #else
  401. return FALSE;
  402. #endif
  403. }
  404. // - WM_Hooks_EnableCursorShape
  405. BOOL WM_Hooks_EnableCursorShape(BOOL enable) {
  406. enable_cursor_shape = enable;
  407. return TRUE;
  408. }