/*++ Copyright (c) 2001 Microsoft Corporation Module Name: IgnoreAltTab.cpp Abstract: This DLL installs a low level keyboard hook to eat Alt-Tab, Left Win, Right Win and Apps combinations. This is accomplished by creating a seperate thread, installing a WH_KEYBOARD_LL hook and starting a message loop in that thread. This shim needs to force DInput to use windows hooks instead of WM_INPUT, since WM_INPUT messages force all WH_KEYBOARD_LL to be ignored. Notes: We intentionally try to stay at the *end* of the hook chain by hooking as early as we can. If we are at the end of the hook chain, we allow all previous hookers (DInput especially) their chance at the key event before we toss it out. History: 05/25/2001 robkenny Created 11/27/2001 mnikkel Added sticky and filter keys to shim. --*/ #include "precomp.h" #include "CharVector.h" IMPLEMENT_SHIM_BEGIN(IgnoreAltTab) #include "ShimHookMacro.h" APIHOOK_ENUM_BEGIN APIHOOK_ENUM_ENTRY(RegisterRawInputDevices) APIHOOK_ENUM_END // Forward declarations LRESULT CALLBACK KeyboardProcLL(int nCode, WPARAM wParam, LPARAM lParam); class CThreadKeyboardHook; // Global variables CThreadKeyboardHook *g_cKeyboardHook = NULL; BOOL g_bFilterKeyInit = FALSE; BOOL g_bStickyKeyInit = FALSE; BOOL g_bCatchKeys = TRUE; STICKYKEYS g_OldStickyKeyValue; FILTERKEYS g_OldFilterKeyValue; class CThreadKeyboardHook { protected: HHOOK hKeyboardHook; HANDLE hMessageThread; DWORD dwMessageThreadId; public: CThreadKeyboardHook(); void AddHook(); void RemoveHook(); LRESULT HandleKeyLL(int code, WPARAM wParam, LPARAM lParam); static DWORD WINAPI MessageLoopThread(LPVOID lpParameter); }; /*++ This routine runs in a seperate thread. MSDN says: "This hook is called in the context of the thread that installed it. The call is made by sending a message to the thread that installed the hook. Therefore, the thread that installed the hook must have a message loop." --*/ DWORD WINAPI CThreadKeyboardHook::MessageLoopThread(LPVOID lpParameter) { CThreadKeyboardHook * pThreadHookList = (CThreadKeyboardHook *)lpParameter; pThreadHookList->AddHook(); DPFN(eDbgLevelSpew, "Starting message loop"); BOOL bRet; MSG msg; while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) { DPFN(eDbgLevelSpew, "MessageLoopThread: Msg(0x%08x) wParam(0x%08x) lParam(0x%08x)", msg.message, msg.wParam, msg.lParam); if (bRet == -1) { // handle the error and possibly exit } else { TranslateMessage(&msg); DispatchMessage(&msg); } } // We are exiting the thread pThreadHookList->hMessageThread = 0; pThreadHookList->dwMessageThreadId = 0; return 0; } CThreadKeyboardHook::CThreadKeyboardHook() { hMessageThread = CreateThread(NULL, 0, MessageLoopThread, this, 0, &dwMessageThreadId); } void CThreadKeyboardHook::AddHook() { // Do not add duplicates to the list if (!hKeyboardHook) { hKeyboardHook = SetWindowsHookExA(WH_KEYBOARD_LL, KeyboardProcLL, g_hinstDll, 0); if (hKeyboardHook) { DPFN(eDbgLevelSpew, "Adding WH_KEYBOARD_LL hook(0x%08x)", hKeyboardHook); } } } void CThreadKeyboardHook::RemoveHook() { if (hKeyboardHook) { UnhookWindowsHookEx(hKeyboardHook); DPFN(eDbgLevelSpew, "Removing hook(0x%08x)", hKeyboardHook); hKeyboardHook = NULL; } } LRESULT CThreadKeyboardHook::HandleKeyLL(int code, WPARAM wParam, LPARAM lParam) { DWORD dwKey = 0; BOOL bAltDown = 0; BOOL bCtlDown = GetKeyState(VK_CONTROL) < 0; if (lParam) { KBDLLHOOKSTRUCT * pllhs = (KBDLLHOOKSTRUCT*)lParam; dwKey = pllhs->vkCode; bAltDown = (pllhs->flags & LLKHF_ALTDOWN) != 0; } //if (code >= 0) // Despite what MSDN says, we need to muck with the values even if nCode == 0 { if (bAltDown && dwKey == VK_TAB) // Alt-Tab { // Do not process this event LOGN(eDbgLevelInfo, "Eating Key: Alt-Tab"); return TRUE; } else if (bAltDown && dwKey == VK_ESCAPE) // Alt-Escape { // Do not process this event LOGN(eDbgLevelInfo, "Eating Key: Alt-Escape"); return TRUE; } else if (bCtlDown && dwKey == VK_ESCAPE) // Ctrl-Escape { // Do not process this event LOGN(eDbgLevelInfo, "Eating Key: Ctrl-Escape"); return TRUE; } else if (dwKey == VK_RWIN || dwKey == VK_LWIN) // Windows key { // Do not process this event LOGN(eDbgLevelInfo, "Eating Key: Windows Key"); return TRUE; } else if (dwKey == VK_APPS) // Menu key { // Do not process this event LOGN(eDbgLevelInfo, "Eating Key: Apps key"); return TRUE; } } DPFN(eDbgLevelSpew, "LL Key event: code(0x%08x) dwKey(0x%08x) bits(0x%08x) Alt(%d) Ctrl(%d)", code, dwKey, lParam, bAltDown, bCtlDown); return CallNextHookEx(hKeyboardHook, code, wParam, lParam); } /*++ This function intercepts special codes and eats them so that the app is not switched out of. --*/ LRESULT CALLBACK KeyboardProcLL( int nCode, WPARAM wParam, LPARAM lParam ) { if (g_cKeyboardHook) { return g_cKeyboardHook->HandleKeyLL(nCode, wParam, lParam); } return 1; // this is an error... } /*++ Determine if there are any accelerated pixel formats available. This is done by enumerating the pixel formats and testing for acceleration. --*/ BOOL IsGLAccelerated() { HMODULE hMod = NULL; HDC hdc = NULL; int i; PIXELFORMATDESCRIPTOR pfd; _pfn_wglDescribePixelFormat pfnDescribePixelFormat; int iFormat = -1; // // Load original opengl // hMod = LoadLibraryA("opengl32"); if (!hMod) { LOGN(eDbgLevelError, "Failed to load OpenGL32"); goto Exit; } // // Get wglDescribePixelFormat so we can enumerate pixel formats // pfnDescribePixelFormat = (_pfn_wglDescribePixelFormat) GetProcAddress( hMod, "wglDescribePixelFormat"); if (!pfnDescribePixelFormat) { LOGN(eDbgLevelError, "API wglDescribePixelFormat not found in OpenGL32"); goto Exit; } // // Get a Display DC for enumeration // hdc = GetDC(NULL); if (!hdc) { LOGN(eDbgLevelError, "GetDC(NULL) Failed"); goto Exit; } // // Run the list of pixel formats looking for any that are non-generic, // i.e. accelerated by an ICD // i = 1; iFormat = 0; while ((*pfnDescribePixelFormat)(hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) { if ((pfd.dwFlags & PFD_DRAW_TO_WINDOW) && (pfd.dwFlags & PFD_SUPPORT_OPENGL) && (!(pfd.dwFlags & PFD_GENERIC_FORMAT))) { iFormat = i; break; } i++; } Exit: if (hdc) { ReleaseDC(NULL, hdc); } if (hMod) { FreeLibrary(hMod); } return (iFormat > 0); } /*++ WM_INPUT messages force WH_KEYBOARD_LL hooks to be ignored, therefore we need to fail this call. --*/ BOOL APIHOOK(RegisterRawInputDevices)( PCRAWINPUTDEVICE /*pRawInputDevices*/, UINT /*uiNumDevices*/, UINT /*cbSize*/ ) { LOGN(eDbgLevelError, "RegisterRawInputDevices: failing API with bogus ERROR_INVALID_PARAMETER"); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } /*++ DisableStickyKeys saves the current value for LPSTICKYKEYS and then disables the option. --*/ VOID DisableStickyKeys() { if (!g_bStickyKeyInit ) { STICKYKEYS NewStickyKeyValue; // Initialize the current and new Stickykey structures g_OldStickyKeyValue.cbSize = sizeof(STICKYKEYS); NewStickyKeyValue.cbSize = sizeof(STICKYKEYS); NewStickyKeyValue.dwFlags = 0; // retrieve the current Stickykey structure if (SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &g_OldStickyKeyValue, 0)) { // if retrieval of current Stickykey structure was successful then change the settings // with the new structure. if (SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &NewStickyKeyValue, SPIF_SENDCHANGE)) { g_bStickyKeyInit = TRUE; LOGN( eDbgLevelInfo, "[DisableStickyKeys] Stickykeys disabled."); } else { LOGN( eDbgLevelError, "[DisableStickyKeys] Unable to change Stickykey settings!"); } } else { LOGN( eDbgLevelError, "[DisableStickyKeys] Unable to retrieve current Stickykey settings!"); } } } /*++ EnableStickyKeys uses the save value for STICKYKEYS and resets the option to the original setting. --*/ VOID EnableStickyKeys() { if (g_bStickyKeyInit ) { g_bStickyKeyInit = FALSE; // Restore Stickykey original state if (SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &g_OldStickyKeyValue, SPIF_SENDCHANGE)) { LOGN( eDbgLevelInfo, "[DisableStickyKeys] Sticky key state restored"); } else { LOGN( eDbgLevelError, "[DisableStickyKeys] Unable to restore Sticky key settings!"); } } } /*++ DisableFilterKeys saves the current value for LPFILTERKEYS and then disables the option. --*/ VOID DisableFilterKeys() { if (!g_bFilterKeyInit) { FILTERKEYS NewFilterKeyValue; // Initialize the current and new Filterkey structures g_OldFilterKeyValue.cbSize = sizeof(FILTERKEYS); NewFilterKeyValue.cbSize = sizeof(FILTERKEYS); NewFilterKeyValue.dwFlags = 0; // retrieve the current stickykey structure if (SystemParametersInfo(SPI_GETFILTERKEYS, sizeof(FILTERKEYS), &g_OldFilterKeyValue, 0)) { // if retrieval of current Filterkey structure was successful then change the settings // with the new structure. if (SystemParametersInfo(SPI_SETFILTERKEYS, sizeof(FILTERKEYS), &NewFilterKeyValue, SPIF_SENDCHANGE)) { g_bFilterKeyInit = TRUE; LOGN( eDbgLevelInfo, "[DisableFilterKeys] Filterkeys disabled."); } else { LOGN( eDbgLevelError, "[DisableFilterKeys] Unable to change Filterkey settings!"); } } else { LOGN( eDbgLevelError, "[DisableFilterKeys] Unable to retrieve current Filterkey settings!"); } } } /*++ EnableFilterKeys uses the save value for FILTERKEYS and resets the option to the original setting. --*/ VOID EnableFilterKeys() { if (g_bFilterKeyInit) { g_bFilterKeyInit = FALSE; // Restore Filterkey original state if (SystemParametersInfo(SPI_SETFILTERKEYS, sizeof(FILTERKEYS), &g_OldFilterKeyValue, SPIF_SENDCHANGE)) { LOGN( eDbgLevelInfo, "[DisableStickyKeys] Filterkey state restored"); } else { LOGN( eDbgLevelError, "[DisableStickyKeys] Unable to restore Filterkey settings!"); } } } /*++ Handle Shim notifications. --*/ BOOL NOTIFY_FUNCTION( DWORD fdwReason ) { if (fdwReason == DLL_PROCESS_ATTACH) { CSTRING_TRY { CString csCl(COMMAND_LINE); if (csCl.CompareNoCase(L"NOKEYS") == 0) { g_bCatchKeys = FALSE; } } CSTRING_CATCH { // no action } } else if (fdwReason == SHIM_STATIC_DLLS_INITIALIZED) { if (g_bCatchKeys) { DisableStickyKeys(); DisableFilterKeys(); } #if DBG static bool bTest = FALSE; if (bTest) { delete g_cKeyboardHook; g_cKeyboardHook = NULL; return TRUE; } #endif CSTRING_TRY { CString csCl(COMMAND_LINE); if (csCl.CompareNoCase(L"OPENGL") == 0) { // This must be called *after* the dll's have been initialized if (IsGLAccelerated()) { return TRUE; } } g_cKeyboardHook = new CThreadKeyboardHook; if (!g_cKeyboardHook) { return FALSE; } } CSTRING_CATCH { // Do nothing } } else if (fdwReason == DLL_PROCESS_DETACH) { if (g_bCatchKeys) { EnableFilterKeys(); EnableStickyKeys(); } } return TRUE; } HOOK_BEGIN CALL_NOTIFY_FUNCTION APIHOOK_ENTRY(USER32.DLL, RegisterRawInputDevices) HOOK_END IMPLEMENT_SHIM_END