EventWaiter.h
1 #pragma once 2 3 #include <functional> 4 #include <thread> 5 #include <string> 6 #include <atomic> 7 #include <windows.h> 8 9 /// <summary> 10 /// A reusable utility class that listens for a named Windows event and invokes a callback when triggered. 11 /// Provides RAII-based resource management for event handles and the listener thread. 12 /// The thread is properly joined on destruction to ensure clean shutdown. 13 /// </summary> 14 class EventWaiter 15 { 16 public: 17 EventWaiter() = default; 18 19 EventWaiter(const EventWaiter&) = delete; 20 EventWaiter& operator=(const EventWaiter&) = delete; 21 EventWaiter(EventWaiter&&) = delete; 22 EventWaiter& operator=(EventWaiter&&) = delete; 23 24 ~EventWaiter() 25 { 26 stop(); 27 } 28 29 /// <summary> 30 /// Starts listening for the specified named event. When the event is signaled, the callback is invoked. 31 /// </summary> 32 /// <param name="name">The name of the Windows event to listen for.</param> 33 /// <param name="callback">The callback function to invoke when the event is triggered. Receives ERROR_SUCCESS on success.</param> 34 /// <returns>true if listening started successfully, false otherwise.</returns> 35 bool start(const std::wstring& name, std::function<void(DWORD)> callback) 36 { 37 if (m_listening) 38 { 39 return false; 40 } 41 42 m_exitThreadEvent = CreateEventW(nullptr, false, false, nullptr); 43 m_waitingEvent = CreateEventW(nullptr, false, false, name.c_str()); 44 45 if (!m_exitThreadEvent || !m_waitingEvent) 46 { 47 cleanup(); 48 return false; 49 } 50 51 m_listening = true; 52 m_eventThread = std::thread([this, cb = std::move(callback)]() { 53 HANDLE events[2] = { m_waitingEvent, m_exitThreadEvent }; 54 while (m_listening) 55 { 56 auto waitResult = WaitForMultipleObjects(2, events, false, INFINITE); 57 if (!m_listening) 58 { 59 break; 60 } 61 62 if (waitResult == WAIT_OBJECT_0 + 1) 63 { 64 // Exit event signaled 65 break; 66 } 67 68 if (waitResult == WAIT_FAILED) 69 { 70 cb(GetLastError()); 71 continue; 72 } 73 74 if (waitResult == WAIT_OBJECT_0) 75 { 76 cb(ERROR_SUCCESS); 77 } 78 } 79 }); 80 81 return true; 82 } 83 84 /// <summary> 85 /// Stops listening for the event and cleans up resources. 86 /// Waits for the listener thread to finish before returning. 87 /// Safe to call multiple times. 88 /// </summary> 89 void stop() 90 { 91 m_listening = false; 92 if (m_exitThreadEvent) 93 { 94 SetEvent(m_exitThreadEvent); 95 } 96 if (m_eventThread.joinable()) 97 { 98 m_eventThread.join(); 99 } 100 cleanup(); 101 } 102 103 /// <summary> 104 /// Returns whether the listener is currently active. 105 /// </summary> 106 bool is_listening() const 107 { 108 return m_listening; 109 } 110 111 private: 112 void cleanup() 113 { 114 if (m_exitThreadEvent) 115 { 116 CloseHandle(m_exitThreadEvent); 117 m_exitThreadEvent = nullptr; 118 } 119 if (m_waitingEvent) 120 { 121 CloseHandle(m_waitingEvent); 122 m_waitingEvent = nullptr; 123 } 124 } 125 126 HANDLE m_exitThreadEvent = nullptr; 127 HANDLE m_waitingEvent = nullptr; 128 std::thread m_eventThread; 129 std::atomic_bool m_listening{ false }; 130 };