#include "pch.h" #include "centralized_kb_hook.h" #include #include namespace CentralizedKeyboardHook { struct HotkeyDescriptor { Hotkey hotkey; std::wstring moduleName; std::function action; bool operator<(const HotkeyDescriptor& other) const { return hotkey < other.hotkey; }; }; std::multiset hotkeyDescriptors; std::mutex mutex; HHOOK hHook{}; struct DestroyOnExit { ~DestroyOnExit() { Stop(); } } destroyOnExitObj; LRESULT CALLBACK KeyboardHookProc(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam) { if (nCode < 0 || ((wParam != WM_KEYDOWN) && (wParam != WM_SYSKEYDOWN))) { return CallNextHookEx(hHook, nCode, wParam, lParam); } const auto& keyPressInfo = *reinterpret_cast(lParam); Hotkey hotkey{ .win = (GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000), .ctrl = static_cast(GetAsyncKeyState(VK_CONTROL) & 0x8000), .shift = static_cast(GetAsyncKeyState(VK_SHIFT) & 0x8000), .alt = static_cast(GetAsyncKeyState(VK_MENU) & 0x8000), .key = static_cast(keyPressInfo.vkCode) }; std::function action; { // Hold the lock for the shortest possible duration std::unique_lock lock{ mutex }; HotkeyDescriptor dummy{ .hotkey = hotkey }; auto it = hotkeyDescriptors.find(dummy); if (it != hotkeyDescriptors.end()) { action = it->action; } } if (action) { if (action()) { // After invoking the hotkey send a dummy key to prevent Start Menu from activating INPUT dummyEvent[1] = {}; dummyEvent[0].type = INPUT_KEYBOARD; dummyEvent[0].ki.wVk = 0xFF; dummyEvent[0].ki.dwFlags = KEYEVENTF_KEYUP; SendInput(1, dummyEvent, sizeof(INPUT)); // Swallow the key press return 1; } } return CallNextHookEx(hHook, nCode, wParam, lParam); } void SetHotkeyAction(const std::wstring& moduleName, const Hotkey& hotkey, std::function&& action) noexcept { std::unique_lock lock{ mutex }; hotkeyDescriptors.insert({ .hotkey = hotkey, .moduleName = moduleName, .action = std::move(action) }); } void ClearModuleHotkeys(const std::wstring& moduleName) noexcept { std::unique_lock lock{ mutex }; auto it = hotkeyDescriptors.begin(); while (it != hotkeyDescriptors.end()) { if (it->moduleName == moduleName) { it = hotkeyDescriptors.erase(it); } else { ++it; } } } void Start() noexcept { #if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED) const bool hook_disabled = IsDebuggerPresent(); #else const bool hook_disabled = false; #endif if (!hook_disabled) { if (!hHook) { hHook = SetWindowsHookExW(WH_KEYBOARD_LL, KeyboardHookProc, NULL, NULL); if (!hHook) { DWORD errorCode = GetLastError(); show_last_error_message(L"SetWindowsHookEx", errorCode, L"centralized_kb_hook"); } } } } void Stop() noexcept { if (hHook && UnhookWindowsHookEx(hHook)) { hHook = NULL; } } }