KeyboardHook.cpp
1 #include "pch.h" 2 #include "KeyboardHook.h" 3 #include "KeyboardHook.g.cpp" 4 #include <common/debug_control.h> 5 #include <common/utils/winapi_error.h> 6 7 namespace winrt::PowerToys::Interop::implementation 8 { 9 std::mutex KeyboardHook::instancesMutex; 10 std::unordered_set<KeyboardHook*> KeyboardHook::instances; 11 12 KeyboardHook::KeyboardHook(winrt::PowerToys::Interop::KeyboardEventCallback const& keyboardEventCallback, winrt::PowerToys::Interop::IsActiveCallback const& isActiveCallback, winrt::PowerToys::Interop::FilterKeyboardEvent const& filterKeyboardEvent) 13 { 14 this->keyboardEventCallback = keyboardEventCallback; 15 this->isActiveCallback = isActiveCallback; 16 this->filterKeyboardEvent = filterKeyboardEvent; 17 } 18 19 void KeyboardHook::Close() 20 { 21 std::unique_lock lock { instancesMutex }; 22 auto iter = instances.find(this); 23 if (iter != instances.end()) 24 { 25 instances.erase(iter); 26 } 27 if (instances.size() < 1 && hookHandle != nullptr) 28 { 29 if (UnhookWindowsHookEx(hookHandle)) 30 { 31 hookHandle = nullptr; 32 } 33 } 34 } 35 36 37 void KeyboardHook::Start() 38 { 39 #if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED) 40 const bool hookDisabled = IsDebuggerPresent(); 41 #else 42 const bool hookDisabled = false; 43 #endif 44 if (!hookDisabled) 45 { 46 std::unique_lock lock { instancesMutex }; 47 assert(instances.find(this) == instances.end()); 48 // register low level hook procedure 49 instances.insert(this); 50 if (hookHandle == nullptr) 51 { 52 hookHandle = SetWindowsHookEx( 53 WH_KEYBOARD_LL, 54 HookProc, 55 0, 56 0); 57 if (hookHandle == nullptr) 58 { 59 DWORD errorCode = GetLastError(); 60 show_last_error_message(L"SetWindowsHookEx", errorCode, L"PowerToys - Interop"); 61 } 62 } 63 } 64 } 65 LRESULT KeyboardHook::HookProc(int nCode, WPARAM wParam, LPARAM lParam) 66 { 67 if (nCode == HC_ACTION) 68 { 69 std::vector<KeyboardHook*> instances_copy; 70 { 71 /* Use a copy of instances, to iterate through the copy without needing to maintain the lock */ 72 std::unique_lock lock{ instancesMutex }; 73 instances_copy.reserve(instances.size()); 74 std::copy(instances.begin(), instances.end(), std::back_inserter(instances_copy)); 75 } 76 77 for (auto const& s_instance : instances_copy) 78 { 79 if (s_instance->isActiveCallback()) 80 { 81 KeyboardEvent ev; 82 ev.message = wParam; 83 ev.key = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam)->vkCode; 84 ev.dwExtraInfo = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam)->dwExtraInfo; 85 86 // Ignore the keyboard hook if the FilterKeyboardEvent returns false. 87 if ((s_instance->filterKeyboardEvent != nullptr && !s_instance->filterKeyboardEvent(ev))) 88 { 89 continue; 90 } 91 92 s_instance->keyboardEventCallback(ev); 93 return 1; 94 } 95 } 96 } 97 return CallNextHookEx(NULL, nCode, wParam, lParam); 98 } 99 }