Leaked source code of windows server 2003
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.

609 lines
16 KiB

  1. #include "precomp.h"
  2. //
  3. // IM.CPP
  4. // Input Manager, NT specific code
  5. //
  6. #define MLZ_FILE_ZONE ZONE_INPUT
  7. //
  8. // OSI_InstallControlledHooks()
  9. //
  10. // Installs/removes input hooks for control
  11. //
  12. BOOL WINAPI OSI_InstallControlledHooks(BOOL fEnable, BOOL fDesktop)
  13. {
  14. BOOL rc = FALSE;
  15. DebugEntry(OSI_InstallControlledHooks);
  16. if (fEnable)
  17. {
  18. //
  19. // Create the service thread, it will install the hooks.
  20. //
  21. ASSERT(!g_imNTData.imLowLevelInputThread);
  22. if (!DCS_StartThread(IMLowLevelInputProcessor))
  23. {
  24. ERROR_OUT(( "Failed to create LL IM thread"));
  25. DC_QUIT;
  26. }
  27. }
  28. else
  29. {
  30. if (g_imNTData.imLowLevelInputThread != 0)
  31. {
  32. PostThreadMessage( g_imNTData.imLowLevelInputThread, WM_QUIT, 0, 0);
  33. g_imNTData.imLowLevelInputThread = 0;
  34. }
  35. }
  36. if (fDesktop)
  37. {
  38. rc = TRUE;
  39. }
  40. else
  41. {
  42. rc = OSI_InstallHighLevelMouseHook(fEnable);
  43. }
  44. DC_EXIT_POINT:
  45. DebugExitBOOL(OSI_InstallControlledHooks, rc);
  46. return(rc);
  47. }
  48. // Name: IMLowLevelInputProcessor
  49. //
  50. // Purpose: Main function for the low-level input handler thread.
  51. //
  52. // Returns: wParam of the WM_QUIT message.
  53. //
  54. // Params: syncObject - sync object that allows this thread to signal
  55. // the creating thread via COM_SignalThreadStarted.
  56. //
  57. // Operation: This function is the start point for the low-level input
  58. // handler thread.
  59. //
  60. // We raise the priority of this thread to:
  61. // (a) ensure that we avoid hitting the low-level callback
  62. // timeout - which would cause us to miss events.
  63. // (b) minimize visible mouse movement lag on the screen.
  64. //
  65. // The thread installs the low-level hooks and enters a
  66. // GetMessage/DispatchMessage loop which handles the low-level
  67. // callbacks.
  68. //
  69. // The Share Core sends the thread a WM_QUIT message to
  70. // terminate it, which causes it to exit the message loop and
  71. // removes the low-level hooks before it terminates.
  72. //
  73. DWORD WINAPI IMLowLevelInputProcessor(LPVOID hEventWait)
  74. {
  75. MSG msg;
  76. UINT rc = 0;
  77. DebugEntry(IMLowLevelInputProcessor);
  78. TRACE_OUT(( "Thread started..."));
  79. //
  80. // Give ourseleves the highest possible priority (within our process
  81. // priority class) to ensure that the low-level events are serviced as
  82. // soon as possible.
  83. //
  84. SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
  85. g_imNTData.imLowLevelInputThread = GetCurrentThreadId();
  86. //
  87. // Install low-level input hooks.
  88. //
  89. g_imNTData.imhLowLevelMouseHook = SetWindowsHookEx(
  90. WH_MOUSE_LL,
  91. IMLowLevelMouseProc,
  92. g_asInstance,
  93. 0 );
  94. g_imNTData.imhLowLevelKeyboardHook = SetWindowsHookEx(
  95. WH_KEYBOARD_LL,
  96. IMLowLevelKeyboardProc,
  97. g_asInstance,
  98. 0 );
  99. //
  100. // We're done with our init code, for better or for worse. Let the
  101. // calling thread continue.
  102. //
  103. SetEvent((HANDLE)hEventWait);
  104. if ( (g_imNTData.imhLowLevelMouseHook == NULL) ||
  105. (g_imNTData.imhLowLevelKeyboardHook == NULL) )
  106. {
  107. ERROR_OUT(( "SetWindowsHookEx failed: hMouse(%u) hKeyboard(%u)",
  108. g_imNTData.imhLowLevelMouseHook, g_imNTData.imhLowLevelKeyboardHook ));
  109. DC_QUIT;
  110. }
  111. //
  112. // Do our message loop to get events
  113. //
  114. while (GetMessage(&msg, NULL, 0, 0))
  115. {
  116. TranslateMessage(&msg);
  117. DispatchMessage(&msg);
  118. }
  119. //
  120. // Remove hooks
  121. //
  122. if (g_imNTData.imhLowLevelMouseHook != NULL)
  123. {
  124. UnhookWindowsHookEx(g_imNTData.imhLowLevelMouseHook);
  125. g_imNTData.imhLowLevelMouseHook = NULL;
  126. }
  127. if (g_imNTData.imhLowLevelKeyboardHook != NULL)
  128. {
  129. UnhookWindowsHookEx(g_imNTData.imhLowLevelKeyboardHook);
  130. g_imNTData.imhLowLevelKeyboardHook = NULL;
  131. }
  132. DC_EXIT_POINT:
  133. DebugExitDWORD(IMLowLevelInputProcessor, rc);
  134. return(rc);
  135. }
  136. //
  137. // Name: IMOtherDesktopProc()
  138. //
  139. // This allows us to inject (but not block) input into other desktops
  140. // besides default, where the user's desktop resides. Specifically, the
  141. // winlogon desktop and/or the screensaver desktop.
  142. //
  143. // This is trickier than it might seem, because the winlogon desktop is
  144. // always around, but the screen saver one is transitory.
  145. //
  146. // The periodic SWL_ code, called when hosting, checks for the current
  147. // desktop and if it's switched posts us a message so we can change our
  148. // desktop and our hooks.
  149. //
  150. DWORD WINAPI IMOtherDesktopProc(LPVOID hEventWait)
  151. {
  152. MSG msg;
  153. UINT rc = 0;
  154. HDESK hDesktop;
  155. GUIEFFECTS effects;
  156. DebugEntry(IMOtherDesktopProc);
  157. TRACE_OUT(("Other desktop thread started..."));
  158. g_imNTData.imOtherDesktopThread = GetCurrentThreadId();
  159. //
  160. // Start out attached to the WinLogon desktop because it's always
  161. // around.
  162. //
  163. // Set our desktop to the winlogon desktop
  164. hDesktop = OpenDesktop(NAME_DESKTOP_WINLOGON,
  165. 0,
  166. FALSE,
  167. DESKTOP_JOURNALPLAYBACK);
  168. if ( !hDesktop )
  169. {
  170. WARNING_OUT(("OpenDesktop failed: %ld", GetLastError()));
  171. DC_QUIT;
  172. }
  173. else if (!SetThreadDesktop (hDesktop))
  174. {
  175. WARNING_OUT(("SetThreadDesktop failed: %ld", GetLastError()));
  176. DC_QUIT;
  177. }
  178. //
  179. // Attempt to load the driver dynamically on this thread also.
  180. //
  181. if (g_asNT5)
  182. {
  183. OSI_InitDriver50(TRUE);
  184. }
  185. // Let the calling thread continue.
  186. SetEvent((HANDLE)hEventWait);
  187. ZeroMemory(&effects, sizeof(effects));
  188. while (GetMessage(&msg, NULL, 0, 0))
  189. {
  190. switch(msg.message)
  191. {
  192. case OSI_WM_MOUSEINJECT:
  193. mouse_event(
  194. LOWORD(msg.wParam), // flags
  195. HIWORD(msg.lParam), // x
  196. LOWORD(msg.lParam), // y
  197. HIWORD(msg.wParam), // mouseData
  198. 0); // dwExtraInfo
  199. break;
  200. case OSI_WM_KEYBDINJECT:
  201. keybd_event(
  202. (BYTE)(LOWORD(msg.lParam)), // vkCode
  203. (BYTE)(HIWORD(msg.lParam)), // scanCode
  204. (DWORD)msg.wParam, // flags
  205. 0); // dwExtraInfo
  206. break;
  207. case OSI_WM_DESKTOPREPAINT:
  208. USR_RepaintWindow(NULL);
  209. break;
  210. case OSI_WM_INJECTSAS:
  211. {
  212. HWND hwndSAS;
  213. if ( hwndSAS = FindWindow("SAS window class",NULL))
  214. {
  215. PostMessage(hwndSAS,WM_HOTKEY,0,
  216. MAKELONG(0x8000|MOD_ALT|MOD_CONTROL,VK_DELETE));
  217. }
  218. else
  219. {
  220. WARNING_OUT(("SAS window not found, on screensaver desktop"));
  221. }
  222. break;
  223. }
  224. case OSI_WM_DESKTOPSWITCH:
  225. {
  226. HDESK hDesktopNew;
  227. TRACE_OUT(("OSI_WM_DESKTOPSWITCH: switching desktop from %d to %d",
  228. msg.wParam, msg.lParam));
  229. if (msg.lParam == DESKTOP_SCREENSAVER)
  230. {
  231. // We're switching TO the screensaver, attach to it.
  232. TRACE_OUT(("Switching TO screensaver"));
  233. hDesktopNew = OpenDesktop(NAME_DESKTOP_SCREENSAVER,
  234. 0, FALSE, DESKTOP_JOURNALPLAYBACK);
  235. }
  236. else if (msg.wParam == DESKTOP_SCREENSAVER)
  237. {
  238. //
  239. // We're switching FROM the screensaver, reattach to
  240. // winlogon
  241. //
  242. TRACE_OUT(("Switching FROM screensaver"));
  243. hDesktopNew = OpenDesktop(NAME_DESKTOP_WINLOGON,
  244. 0, FALSE, DESKTOP_JOURNALPLAYBACK);
  245. }
  246. else
  247. {
  248. hDesktopNew = NULL;
  249. }
  250. if (hDesktopNew != NULL)
  251. {
  252. if (!SetThreadDesktop(hDesktopNew))
  253. {
  254. WARNING_OUT(("SetThreadDesktop to 0x%08x, type %d failed",
  255. hDesktopNew, msg.lParam));
  256. }
  257. else
  258. {
  259. CloseHandle(hDesktop);
  260. hDesktop = hDesktopNew;
  261. }
  262. }
  263. break;
  264. }
  265. case OSI_WM_SETGUIEFFECTS:
  266. {
  267. HET_SetGUIEffects((msg.wParam != 0), &effects);
  268. break;
  269. }
  270. }
  271. }
  272. DC_EXIT_POINT:
  273. if (g_asNT5)
  274. {
  275. OSI_InitDriver50(FALSE);
  276. }
  277. if (hDesktop)
  278. {
  279. CloseHandle(hDesktop);
  280. }
  281. g_imNTData.imOtherDesktopThread = 0;
  282. DebugExitDWORD(IMOtherDesktopProc, rc);
  283. return(rc);
  284. }
  285. //
  286. // IMLowLevelMouseProc()
  287. // NT callback for low-level mouse events.
  288. //
  289. // It is installed and called on a secondary thread with high priority to
  290. // service the APC call outs. It follows the windows hook conventions for
  291. // parameters and return values--zero to accept the event, non-zero to
  292. // discard.
  293. //
  294. //
  295. LRESULT CALLBACK IMLowLevelMouseProc
  296. (
  297. int nCode,
  298. WPARAM wParam,
  299. LPARAM lParam
  300. )
  301. {
  302. LRESULT rc = 0;
  303. PMSLLHOOKSTRUCT pMouseEvent;
  304. DebugEntry(IMLowLevelMouseProc);
  305. pMouseEvent = (PMSLLHOOKSTRUCT)lParam;
  306. //
  307. // If this isn't for an event that is happening or it's one we
  308. // injected ourself, pass it through and no need for processing.
  309. //
  310. if ((nCode != HC_ACTION) || (pMouseEvent->flags & LLMHF_INJECTED))
  311. {
  312. DC_QUIT;
  313. }
  314. //
  315. // This is a local user event. If controlled, throw it away. Unless
  316. // it's a click, in that case post a REVOKECONTROL message.
  317. //
  318. if (g_imSharedData.imControlled)
  319. {
  320. //
  321. // If this is a button click, take control back
  322. //
  323. if ((wParam == WM_LBUTTONDOWN) ||
  324. (wParam == WM_RBUTTONDOWN) ||
  325. (wParam == WM_MBUTTONDOWN))
  326. {
  327. //
  328. // Don't take control back if this is unattended.
  329. //
  330. if (!g_imSharedData.imUnattended)
  331. {
  332. PostMessage(g_asMainWindow, DCS_REVOKECONTROL_MSG, 0, 0);
  333. }
  334. }
  335. // Swallow event.
  336. rc = 1;
  337. }
  338. DC_EXIT_POINT:
  339. //
  340. // Don't pass on to the next hook (if there is one) if we are
  341. // discarding the event.
  342. //
  343. if (!rc)
  344. {
  345. rc = CallNextHookEx(g_imNTData.imhLowLevelMouseHook, nCode,
  346. wParam, lParam);
  347. }
  348. DebugExitDWORD(IMLowLevelMouseProc, rc);
  349. return(rc);
  350. }
  351. // Name: IMLowLevelKeyboardProc
  352. //
  353. // Purpose: Windows callback function for low-level keyboard events.
  354. //
  355. // Returns: 0 if event is to be passed on to USER.
  356. // 1 if event is to be discarded.
  357. //
  358. // Params: Low-level callback params (see Windows documentation).
  359. //
  360. // Operation: Determines whether to allow the given event into USER.
  361. //
  362. // We always pass on injected events.
  363. // The Control Arbitrator determines whether local events are
  364. // passed on.
  365. //
  366. LRESULT CALLBACK IMLowLevelKeyboardProc
  367. (
  368. int nCode,
  369. WPARAM wParam,
  370. LPARAM lParam
  371. )
  372. {
  373. LRESULT rc = 0;
  374. PKBDLLHOOKSTRUCT pKbdEvent;
  375. DebugEntry(IMLowLevelKeyboardProc);
  376. pKbdEvent = (PKBDLLHOOKSTRUCT)lParam;
  377. //
  378. // If this isn't for an action or it's an event we ourself originated,
  379. // let it through, and do no processing.
  380. //
  381. if ((nCode != HC_ACTION) || (pKbdEvent->flags & LLKHF_INJECTED))
  382. {
  383. DC_QUIT;
  384. }
  385. if (g_imSharedData.imControlled)
  386. {
  387. if (!(pKbdEvent->flags & LLKHF_UP))
  388. {
  389. //
  390. // This is a key down. Take control back, and kill control
  391. // allowability if it's the ESC key.
  392. //
  393. if ((pKbdEvent->vkCode & 0x00FF) == VK_ESCAPE || g_imSharedData.imUnattended)
  394. {
  395. // ESC key always disallows control, even in unattended mode
  396. PostMessage(g_asMainWindow, DCS_ALLOWCONTROL_MSG, FALSE, 0);
  397. }
  398. else if (!g_imSharedData.imUnattended)
  399. {
  400. PostMessage(g_asMainWindow, DCS_REVOKECONTROL_MSG, 0, 0);
  401. }
  402. }
  403. //
  404. // Don't discard toggle keys. The enabled/disabled function
  405. // is already set before we see the keystroke. If we discard,
  406. // the lights are incorrect.
  407. //
  408. // LAURABU: How do we fix this in new model? Post a toggle-key
  409. // message and undo it (fake press)?
  410. //
  411. if (!IM_KEY_IS_TOGGLE(pKbdEvent->vkCode & 0x00FF))
  412. rc = 1;
  413. }
  414. DC_EXIT_POINT:
  415. //
  416. // Don't pass on to the next hook if we are swallowing the event.
  417. //
  418. if (!rc)
  419. {
  420. rc = CallNextHookEx(g_imNTData.imhLowLevelKeyboardHook,
  421. nCode, wParam, lParam);
  422. }
  423. DebugExitDWORD(IMLowLevelKeyboardProc, rc);
  424. return(rc);
  425. }
  426. //
  427. // IMInjectMouseEvent()
  428. // NT-specific version to inject mouse events into the local system
  429. //
  430. void WINAPI OSI_InjectMouseEvent
  431. (
  432. DWORD flags,
  433. LONG x,
  434. LONG y,
  435. DWORD mouseData,
  436. DWORD dwExtraInfo
  437. )
  438. {
  439. TRACE_OUT(("Before MOUSE inject: %08lx, %08lx %08lx",
  440. flags, mouseData, dwExtraInfo));
  441. mouse_event(flags, (DWORD)x, (DWORD)y, mouseData, dwExtraInfo);
  442. if ( g_imNTData.imOtherDesktopThread )
  443. {
  444. // Stuff these dword parameters through WORDS
  445. // need to make sure we don't clip anything
  446. ASSERT(!(flags & 0xffff0000));
  447. //ASSERT(!(mouseData & 0xffff0000)); BUGBUG possible loss
  448. ASSERT(!(x & 0xffff0000));
  449. ASSERT(!(y & 0xffff0000));
  450. PostThreadMessage(
  451. g_imNTData.imOtherDesktopThread,
  452. OSI_WM_MOUSEINJECT,
  453. MAKEWPARAM((WORD)flags,(WORD)mouseData),
  454. MAKELPARAM((WORD)y, (WORD)x ));
  455. }
  456. TRACE_OUT(("After MOUSE inject"));
  457. }
  458. //
  459. // OSI_InjectSAS()
  460. // NT-specific version to inject ctrl+alt+del into the local system
  461. //
  462. void WINAPI OSI_InjectCtrlAltDel(void)
  463. {
  464. if ( g_imNTData.imOtherDesktopThread )
  465. {
  466. PostThreadMessage(
  467. g_imNTData.imOtherDesktopThread,
  468. OSI_WM_INJECTSAS,
  469. 0,
  470. 0 );
  471. }
  472. else
  473. {
  474. WARNING_OUT(("Ignoring SAS Injection attempt"));
  475. }
  476. }
  477. //
  478. // OSI_InjectKeyboardEvent()
  479. // NT-specific version to inject keyboard events into the local system
  480. //
  481. void WINAPI OSI_InjectKeyboardEvent
  482. (
  483. DWORD flags,
  484. WORD vkCode,
  485. WORD scanCode,
  486. DWORD dwExtraInfo
  487. )
  488. {
  489. TRACE_OUT(("Before KEY inject: %04lx, {%04x, %04x}, %04lx",
  490. flags, vkCode, scanCode, dwExtraInfo));
  491. keybd_event((BYTE)vkCode, (BYTE)scanCode, flags, dwExtraInfo);
  492. if ( g_imNTData.imOtherDesktopThread )
  493. {
  494. PostThreadMessage(
  495. g_imNTData.imOtherDesktopThread,
  496. OSI_WM_KEYBDINJECT,
  497. (WPARAM)flags,
  498. MAKELPARAM(vkCode, scanCode));
  499. }
  500. TRACE_OUT(("After KEY inject"));
  501. }
  502. //
  503. // OSI_DesktopSwitch()
  504. // NT-specific, called when we think the current desktop has changed.
  505. //
  506. void WINAPI OSI_DesktopSwitch
  507. (
  508. UINT desktopFrom,
  509. UINT desktopTo
  510. )
  511. {
  512. DebugEntry(OSI_DesktopSwitch);
  513. if (g_imNTData.imOtherDesktopThread)
  514. {
  515. PostThreadMessage(
  516. g_imNTData.imOtherDesktopThread,
  517. OSI_WM_DESKTOPSWITCH,
  518. desktopFrom,
  519. desktopTo);
  520. }
  521. DebugExitVOID(OSI_DesktopSwitch);
  522. }
  523.