Source code of Windows XP (NT5)
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.

570 lines
13 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. IgnoreAltTab.cpp
  5. Abstract:
  6. This DLL installs a low level keyboard hook to eat Alt-Tab, Left Win,
  7. Right Win and Apps combinations.
  8. This is accomplished by creating a seperate thread, installing a WH_KEYBOARD_LL hook
  9. and starting a message loop in that thread.
  10. This shim needs to force DInput to use windows hooks instead of WM_INPUT,
  11. since WM_INPUT messages force all WH_KEYBOARD_LL to be ignored.
  12. Notes:
  13. We intentionally try to stay at the *end* of the hook chain
  14. by hooking as early as we can. If we are at the end of the
  15. hook chain, we allow all previous hookers (DInput especially)
  16. their chance at the key event before we toss it out.
  17. History:
  18. 05/25/2001 robkenny Created
  19. 11/27/2001 mnikkel Added sticky and filter keys to shim.
  20. --*/
  21. #include "precomp.h"
  22. #include "CharVector.h"
  23. IMPLEMENT_SHIM_BEGIN(IgnoreAltTab)
  24. #include "ShimHookMacro.h"
  25. APIHOOK_ENUM_BEGIN
  26. APIHOOK_ENUM_ENTRY(RegisterRawInputDevices)
  27. APIHOOK_ENUM_END
  28. // Forward declarations
  29. LRESULT CALLBACK KeyboardProcLL(int nCode, WPARAM wParam, LPARAM lParam);
  30. class CThreadKeyboardHook;
  31. // Global variables
  32. CThreadKeyboardHook *g_cKeyboardHook = NULL;
  33. LPSTICKYKEYS g_lpOldStickyKeyValue;
  34. LPFILTERKEYS g_lpOldFilterKeyValue;
  35. BOOL g_bInitialize = FALSE;
  36. BOOL g_bInitialize2= FALSE;
  37. BOOL g_bCatchKeys = TRUE;
  38. class CThreadKeyboardHook
  39. {
  40. protected:
  41. HHOOK hKeyboardHook;
  42. HANDLE hMessageThread;
  43. DWORD dwMessageThreadId;
  44. public:
  45. CThreadKeyboardHook();
  46. ~CThreadKeyboardHook();
  47. void AddHook();
  48. void RemoveHook();
  49. LRESULT HandleKeyLL(int code, WPARAM wParam, LPARAM lParam);
  50. static DWORD WINAPI MessageLoopThread(LPVOID lpParameter);
  51. };
  52. /*++
  53. This routine runs in a seperate thread.
  54. MSDN says: "This hook is called in the context of the thread that installed it.
  55. The call is made by sending a message to the thread that installed
  56. the hook. Therefore, the thread that installed the hook must have a message loop."
  57. --*/
  58. DWORD WINAPI CThreadKeyboardHook::MessageLoopThread(LPVOID lpParameter)
  59. {
  60. CThreadKeyboardHook * pThreadHookList = (CThreadKeyboardHook *)lpParameter;
  61. pThreadHookList->AddHook();
  62. DPFN(eDbgLevelSpew, "Starting message loop");
  63. BOOL bRet;
  64. MSG msg;
  65. while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
  66. {
  67. DPFN(eDbgLevelSpew, "MessageLoopThread: Msg(0x%08x) wParam(0x%08x) lParam(0x%08x)",
  68. msg.message, msg.wParam, msg.lParam);
  69. if (bRet == -1)
  70. {
  71. // handle the error and possibly exit
  72. }
  73. else
  74. {
  75. TranslateMessage(&msg);
  76. DispatchMessage(&msg);
  77. }
  78. }
  79. // We are exiting the thread
  80. pThreadHookList->hMessageThread = 0;
  81. pThreadHookList->dwMessageThreadId = 0;
  82. return 0;
  83. }
  84. CThreadKeyboardHook::CThreadKeyboardHook()
  85. {
  86. hMessageThread = CreateThread(NULL, 0, MessageLoopThread, this, 0, &dwMessageThreadId);
  87. }
  88. CThreadKeyboardHook::~CThreadKeyboardHook()
  89. {
  90. if (hMessageThread)
  91. {
  92. TerminateThread(hMessageThread, 0);
  93. }
  94. }
  95. void CThreadKeyboardHook::AddHook()
  96. {
  97. // Do not add duplicates to the list
  98. if (!hKeyboardHook)
  99. {
  100. hKeyboardHook = SetWindowsHookExA(WH_KEYBOARD_LL, KeyboardProcLL, g_hinstDll, 0);
  101. if (hKeyboardHook)
  102. {
  103. DPFN(eDbgLevelSpew, "Adding WH_KEYBOARD_LL hook(0x%08x)", hKeyboardHook);
  104. }
  105. }
  106. }
  107. void CThreadKeyboardHook::RemoveHook()
  108. {
  109. if (hKeyboardHook)
  110. {
  111. UnhookWindowsHookEx(hKeyboardHook);
  112. DPFN(eDbgLevelSpew, "Removing hook(0x%08x)", hKeyboardHook);
  113. hKeyboardHook = NULL;
  114. }
  115. }
  116. LRESULT CThreadKeyboardHook::HandleKeyLL(int code, WPARAM wParam, LPARAM lParam)
  117. {
  118. LRESULT retval = 1;
  119. DWORD dwKey = 0;
  120. BOOL bAltDown = 0;
  121. BOOL bCtlDown = GetKeyState(VK_CONTROL) < 0;
  122. if (lParam)
  123. {
  124. KBDLLHOOKSTRUCT * pllhs = (KBDLLHOOKSTRUCT*)lParam;
  125. dwKey = pllhs->vkCode;
  126. bAltDown = (pllhs->flags & LLKHF_ALTDOWN) != 0;
  127. }
  128. //if (code >= 0) // Despite what MSDN says, we need to muck with the values even if nCode == 0
  129. {
  130. if (bAltDown && dwKey == VK_TAB) // Alt-Tab
  131. {
  132. // Do not process this event
  133. LOGN(eDbgLevelInfo, "Eating Key: Alt-Tab");
  134. return TRUE;
  135. }
  136. else if (bAltDown && dwKey == VK_ESCAPE) // Alt-Escape
  137. {
  138. // Do not process this event
  139. LOGN(eDbgLevelInfo, "Eating Key: Alt-Escape");
  140. return TRUE;
  141. }
  142. else if (bCtlDown && dwKey == VK_ESCAPE) // Ctrl-Escape
  143. {
  144. // Do not process this event
  145. LOGN(eDbgLevelInfo, "Eating Key: Ctrl-Escape");
  146. return TRUE;
  147. }
  148. else if (dwKey == VK_RWIN || dwKey == VK_LWIN) // Windows key
  149. {
  150. // Do not process this event
  151. LOGN(eDbgLevelInfo, "Eating Key: Windows Key");
  152. return TRUE;
  153. }
  154. else if (dwKey == VK_APPS) // Menu key
  155. {
  156. // Do not process this event
  157. LOGN(eDbgLevelInfo, "Eating Key: Apps key");
  158. return TRUE;
  159. }
  160. }
  161. DPFN(eDbgLevelSpew, "LL Key event: code(0x%08x) dwKey(0x%08x) bits(0x%08x) Alt(%d) Ctrl(%d)",
  162. code, dwKey, lParam, bAltDown, bCtlDown);
  163. return CallNextHookEx(hKeyboardHook, code, wParam, lParam);
  164. }
  165. /*++
  166. This function intercepts special codes and eats them so that
  167. the app is not switched out of.
  168. --*/
  169. LRESULT CALLBACK
  170. KeyboardProcLL(
  171. int nCode,
  172. WPARAM wParam,
  173. LPARAM lParam
  174. )
  175. {
  176. if (g_cKeyboardHook)
  177. {
  178. return g_cKeyboardHook->HandleKeyLL(nCode, wParam, lParam);
  179. }
  180. return 1; // this is an error...
  181. }
  182. /*++
  183. Determine if there are any accelerated pixel formats available. This is done
  184. by enumerating the pixel formats and testing for acceleration.
  185. --*/
  186. BOOL
  187. IsGLAccelerated()
  188. {
  189. HMODULE hMod = NULL;
  190. HDC hdc = NULL;
  191. int i;
  192. PIXELFORMATDESCRIPTOR pfd;
  193. _pfn_wglDescribePixelFormat pfnDescribePixelFormat;
  194. int iFormat = -1;
  195. //
  196. // Load original opengl
  197. //
  198. hMod = LoadLibraryA("opengl32");
  199. if (!hMod)
  200. {
  201. LOGN(eDbgLevelError, "Failed to load OpenGL32");
  202. goto Exit;
  203. }
  204. //
  205. // Get wglDescribePixelFormat so we can enumerate pixel formats
  206. //
  207. pfnDescribePixelFormat = (_pfn_wglDescribePixelFormat) GetProcAddress(
  208. hMod, "wglDescribePixelFormat");
  209. if (!pfnDescribePixelFormat)
  210. {
  211. LOGN(eDbgLevelError, "API wglDescribePixelFormat not found in OpenGL32");
  212. goto Exit;
  213. }
  214. //
  215. // Get a Display DC for enumeration
  216. //
  217. hdc = GetDC(NULL);
  218. if (!hdc)
  219. {
  220. LOGN(eDbgLevelError, "GetDC(NULL) Failed");
  221. goto Exit;
  222. }
  223. //
  224. // Run the list of pixel formats looking for any that are non-generic,
  225. // i.e. accelerated by an ICD
  226. //
  227. i = 1;
  228. iFormat = 0;
  229. while ((*pfnDescribePixelFormat)(hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd))
  230. {
  231. if ((pfd.dwFlags & PFD_DRAW_TO_WINDOW) &&
  232. (pfd.dwFlags & PFD_SUPPORT_OPENGL) &&
  233. (!(pfd.dwFlags & PFD_GENERIC_FORMAT)))
  234. {
  235. iFormat = i;
  236. break;
  237. }
  238. i++;
  239. }
  240. Exit:
  241. if (hdc)
  242. {
  243. ReleaseDC(NULL, hdc);
  244. }
  245. if (hMod)
  246. {
  247. FreeLibrary(hMod);
  248. }
  249. return (iFormat > 0);
  250. }
  251. /*++
  252. WM_INPUT messages force WH_KEYBOARD_LL hooks to be ignored, therefore
  253. we need to fail this call.
  254. --*/
  255. BOOL
  256. APIHOOK(RegisterRawInputDevices)(
  257. PCRAWINPUTDEVICE pRawInputDevices,
  258. UINT uiNumDevices,
  259. UINT cbSize
  260. )
  261. {
  262. LOGN(eDbgLevelError, "RegisterRawInputDevices: failing API with bogus ERROR_INVALID_PARAMETER");
  263. SetLastError(ERROR_INVALID_PARAMETER);
  264. return FALSE;
  265. }
  266. /*++
  267. DisableStickyKeys saves the current value for LPSTICKYKEYS and then disables the option.
  268. --*/
  269. VOID
  270. DisableStickyKeys()
  271. {
  272. if (!g_bInitialize2) {
  273. //
  274. // Disable sticky key support
  275. //
  276. g_lpOldStickyKeyValue = (LPSTICKYKEYS) malloc(sizeof(STICKYKEYS));
  277. LPSTICKYKEYS pvParam = (LPSTICKYKEYS) malloc(sizeof(STICKYKEYS));
  278. if ( g_lpOldStickyKeyValue == NULL || pvParam == NULL )
  279. {
  280. // return if out of memory
  281. LOGN( eDbgLevelInfo, "[IgnoreAltTab] Out of Memory, unable to disable sticky keys.");
  282. return;
  283. }
  284. g_lpOldStickyKeyValue->cbSize = sizeof(STICKYKEYS);
  285. pvParam->cbSize = sizeof(STICKYKEYS);
  286. pvParam->dwFlags = 0;
  287. SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS),
  288. g_lpOldStickyKeyValue, 0);
  289. SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), pvParam,
  290. SPIF_SENDCHANGE);
  291. g_bInitialize2 = TRUE;
  292. LOGN( eDbgLevelInfo, "[IgnoreAltTab] Sticky keys disabled");
  293. }
  294. }
  295. /*++
  296. EnableStickyKeys uses the save value for STICKYKEYS and resets the option to the original setting.
  297. --*/
  298. VOID
  299. EnableStickyKeys()
  300. {
  301. if (g_bInitialize2)
  302. {
  303. g_bInitialize2 = FALSE;
  304. //
  305. // Restore sticky key state
  306. //
  307. SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS),
  308. g_lpOldStickyKeyValue, SPIF_SENDCHANGE);
  309. LOGN( eDbgLevelInfo, "[EnableStickyKeys] Sticky key state restored");
  310. }
  311. }
  312. /*++
  313. DisableFilterKeys saves the current value for LPFILTERKEYS and then disables the option.
  314. --*/
  315. VOID
  316. DisableFilterKeys()
  317. {
  318. if (!g_bInitialize)
  319. {
  320. //
  321. // Disable filter key support
  322. //
  323. g_lpOldFilterKeyValue = (LPFILTERKEYS) malloc(sizeof(FILTERKEYS));
  324. LPFILTERKEYS pvParam = (LPFILTERKEYS) malloc(sizeof(FILTERKEYS));
  325. if ( g_lpOldFilterKeyValue == NULL || pvParam == NULL )
  326. {
  327. // return if out of memory
  328. LOGN( eDbgLevelInfo, "[IgnoreAltTab] Out of Memory, unable to disable filter keys.");
  329. return;
  330. }
  331. g_lpOldFilterKeyValue->cbSize = sizeof(FILTERKEYS);
  332. pvParam->cbSize = sizeof(FILTERKEYS);
  333. pvParam->dwFlags = 0;
  334. SystemParametersInfo(SPI_GETFILTERKEYS, sizeof(FILTERKEYS),
  335. g_lpOldFilterKeyValue, 0);
  336. SystemParametersInfo(SPI_SETFILTERKEYS, sizeof(FILTERKEYS), pvParam,
  337. SPIF_SENDCHANGE);
  338. g_bInitialize = TRUE;
  339. LOGN( eDbgLevelInfo, "[DisableFilterKeys] Filter keys disabled");
  340. }
  341. }
  342. /*++
  343. EnableFilterKeys uses the save value for FILTERKEYS and resets the option to the original setting.
  344. --*/
  345. VOID
  346. EnableFilterKeys()
  347. {
  348. if (g_bInitialize)
  349. {
  350. g_bInitialize = FALSE;
  351. //
  352. // Restore filter key state
  353. //
  354. SystemParametersInfo(SPI_SETFILTERKEYS, sizeof(FILTERKEYS),
  355. g_lpOldFilterKeyValue, SPIF_SENDCHANGE);
  356. LOGN( eDbgLevelInfo, "[EnableFilterKeys] Filter key state restored");
  357. }
  358. }
  359. /*++
  360. Handle Shim notifications.
  361. --*/
  362. BOOL
  363. NOTIFY_FUNCTION(
  364. DWORD fdwReason
  365. )
  366. {
  367. if (fdwReason == DLL_PROCESS_ATTACH)
  368. {
  369. g_cKeyboardHook = new CThreadKeyboardHook;
  370. if (!g_cKeyboardHook)
  371. {
  372. return FALSE;
  373. }
  374. CSTRING_TRY
  375. {
  376. CString csCl(COMMAND_LINE);
  377. if (csCl.CompareNoCase(L"NOKEYS") == 0)
  378. {
  379. g_bCatchKeys = FALSE;
  380. }
  381. }
  382. CSTRING_CATCH
  383. {
  384. // no action
  385. }
  386. }
  387. else if (fdwReason == SHIM_STATIC_DLLS_INITIALIZED)
  388. {
  389. if (g_bCatchKeys)
  390. {
  391. DisableStickyKeys();
  392. DisableFilterKeys();
  393. }
  394. #if DBG
  395. static bool bTest = FALSE;
  396. if (bTest)
  397. {
  398. delete g_cKeyboardHook;
  399. g_cKeyboardHook = NULL;
  400. return TRUE;
  401. }
  402. #endif
  403. CSTRING_TRY
  404. {
  405. CString csCl(COMMAND_LINE);
  406. if (csCl.CompareNoCase(L"OPENGL") == 0)
  407. {
  408. // This must be called *after* the dll's have been initialized
  409. if (IsGLAccelerated())
  410. {
  411. delete g_cKeyboardHook;
  412. g_cKeyboardHook = NULL;
  413. return TRUE;
  414. }
  415. }
  416. }
  417. CSTRING_CATCH
  418. {
  419. // Do nothing
  420. }
  421. }
  422. else if (fdwReason == DLL_PROCESS_DETACH)
  423. {
  424. if (g_bCatchKeys)
  425. {
  426. EnableFilterKeys();
  427. EnableStickyKeys();
  428. }
  429. }
  430. return TRUE;
  431. }
  432. HOOK_BEGIN
  433. CALL_NOTIFY_FUNCTION
  434. APIHOOK_ENTRY(USER32.DLL, RegisterRawInputDevices)
  435. HOOK_END
  436. IMPLEMENT_SHIM_END