Skip to content

Commit 932e061

Browse files
committed
Replace WindowProcDelegateManager with WindowMessageDispatcher
Removed window_proc_delegate_manager.* and introduced window_message_dispatcher.* for improved window message handling. Updated tray_icon_windows.cpp to use the new dispatcher, simplifying handler registration and cleanup. This refactor provides more flexible and robust message dispatching for Windows platform code.
1 parent a495a69 commit 932e061

File tree

5 files changed

+517
-226
lines changed

5 files changed

+517
-226
lines changed

src/platform/windows/tray_icon_windows.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#include "../../tray_icon.h"
1616
#include "../../tray_icon_event.h"
1717
#include "string_utils_windows.h"
18-
#include "window_proc_delegate_manager.h"
18+
#include "window_message_dispatcher.h"
1919

2020
namespace nativeapi {
2121

@@ -57,15 +57,14 @@ class TrayIcon::Impl {
5757
nid_.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
5858
nid_.uCallbackMessage = WM_USER + 1; // Custom message for tray icon events
5959

60-
window_proc_id_ = WindowProcDelegateManager::GetInstance().RegisterDelegate(
60+
window_proc_handle_ = WindowMessageDispatcher::GetInstance().RegisterGlobalHandler(
6161
[this](HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
6262
return HandleWindowProc(hwnd, message, wparam, lparam);
6363
});
6464
}
6565

6666
~Impl() {
67-
WindowProcDelegateManager::GetInstance().UnregisterDelegate(
68-
window_proc_id_);
67+
// window_proc_handle_ automatically unregisters when destroyed
6968
if (hwnd_) {
7069
Shell_NotifyIconW(NIM_DELETE, &nid_);
7170
// Destroy the window if we own it
@@ -112,7 +111,7 @@ class TrayIcon::Impl {
112111
return std::nullopt; // Let default window procedure handle it
113112
}
114113

115-
int window_proc_id_;
114+
WindowMessageDispatcher::Handle window_proc_handle_;
116115
HWND hwnd_;
117116
NOTIFYICONDATAW nid_;
118117
std::shared_ptr<Menu> context_menu_;
@@ -142,8 +141,8 @@ TrayIcon::TrayIcon(void* native_tray_icon) {
142141

143142
if (!class_registered) {
144143
WNDCLASSW wc = {};
145-
// Use WindowProcDelegateManager to route messages to registered delegates
146-
wc.lpfnWndProc = WindowProcDelegateManager::InternalWindowProc;
144+
// Use WindowMessageDispatcher to route messages to registered handlers
145+
wc.lpfnWndProc = WindowMessageDispatcher::DispatchWindowProc;
147146
wc.hInstance = hInstance;
148147
wc.lpszClassName = class_name.c_str();
149148

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
#include "window_message_dispatcher.h"
2+
3+
#include <windows.h>
4+
#include <algorithm>
5+
#include <iostream>
6+
7+
namespace nativeapi {
8+
9+
WindowMessageDispatcher& WindowMessageDispatcher::GetInstance() {
10+
static WindowMessageDispatcher instance;
11+
return instance;
12+
}
13+
14+
WindowMessageDispatcher::~WindowMessageDispatcher() {
15+
std::lock_guard<std::mutex> lock(mutex_);
16+
17+
// Clean up all window contexts
18+
for (auto& pair : window_contexts_) {
19+
if (pair.second) {
20+
pair.second->UninstallHook();
21+
}
22+
}
23+
window_contexts_.clear();
24+
handlers_.clear();
25+
}
26+
27+
WindowMessageDispatcher::Handle WindowMessageDispatcher::RegisterGlobalHandler(
28+
WindowMessageHandler handler) {
29+
int id = RegisterHandler(std::move(handler), true, nullptr);
30+
return (id != -1) ? Handle(id) : Handle(-1);
31+
}
32+
33+
WindowMessageDispatcher::Handle WindowMessageDispatcher::RegisterWindowHandler(
34+
HWND hwnd,
35+
WindowMessageHandler handler) {
36+
int id = RegisterHandler(std::move(handler), false, hwnd);
37+
return (id != -1) ? Handle(id) : Handle(-1);
38+
}
39+
40+
int WindowMessageDispatcher::RegisterHandler(WindowMessageHandler handler,
41+
bool is_global,
42+
HWND target_hwnd) {
43+
if (!handler) {
44+
return -1;
45+
}
46+
47+
std::lock_guard<std::mutex> lock(mutex_);
48+
49+
int id = next_id_++;
50+
handlers_[id] = {id, std::move(handler), is_global, target_hwnd};
51+
52+
// For window-specific handlers, install hook if needed
53+
if (!is_global && target_hwnd) {
54+
if (!InstallWindowHook(target_hwnd)) {
55+
handlers_.erase(id);
56+
return -1;
57+
}
58+
}
59+
60+
return id;
61+
}
62+
63+
bool WindowMessageDispatcher::UnregisterHandler(int id) {
64+
if (id < 0) {
65+
return false;
66+
}
67+
68+
std::lock_guard<std::mutex> lock(mutex_);
69+
70+
auto it = handlers_.find(id);
71+
if (it == handlers_.end()) {
72+
return false;
73+
}
74+
75+
const HandlerInfo& info = it->second;
76+
77+
// For window-specific handlers, check if we need to uninstall hook
78+
if (!info.is_global && info.target_hwnd) {
79+
// Check if any other handlers exist for this window
80+
bool has_other_handlers = false;
81+
for (const auto& pair : handlers_) {
82+
if (pair.first != id && !pair.second.is_global &&
83+
pair.second.target_hwnd == info.target_hwnd) {
84+
has_other_handlers = true;
85+
break;
86+
}
87+
}
88+
89+
if (!has_other_handlers) {
90+
UninstallWindowHook(info.target_hwnd);
91+
}
92+
}
93+
94+
handlers_.erase(it);
95+
return true;
96+
}
97+
98+
bool WindowMessageDispatcher::IsHandlerRegistered(int id) const {
99+
std::lock_guard<std::mutex> lock(mutex_);
100+
return handlers_.find(id) != handlers_.end();
101+
}
102+
103+
size_t WindowMessageDispatcher::GetGlobalHandlerCount() const {
104+
std::lock_guard<std::mutex> lock(mutex_);
105+
return std::count_if(handlers_.begin(), handlers_.end(),
106+
[](const auto& pair) { return pair.second.is_global; });
107+
}
108+
109+
size_t WindowMessageDispatcher::GetWindowHandlerCount() const {
110+
std::lock_guard<std::mutex> lock(mutex_);
111+
return std::count_if(handlers_.begin(), handlers_.end(),
112+
[](const auto& pair) { return !pair.second.is_global; });
113+
}
114+
115+
size_t WindowMessageDispatcher::GetHookedWindowCount() const {
116+
std::lock_guard<std::mutex> lock(mutex_);
117+
return window_contexts_.size();
118+
}
119+
120+
LRESULT CALLBACK WindowMessageDispatcher::DispatchWindowProc(HWND hwnd,
121+
UINT msg,
122+
WPARAM wparam,
123+
LPARAM lparam) {
124+
WindowMessageDispatcher& dispatcher = Instance();
125+
126+
// Get window context
127+
auto context = dispatcher.GetWindowContext(hwnd);
128+
if (!context) {
129+
return DefWindowProc(hwnd, msg, wparam, lparam);
130+
}
131+
132+
// Dispatch to window-specific handlers
133+
auto result = context->Dispatch(hwnd, msg, wparam, lparam);
134+
if (result.has_value()) {
135+
return result.value();
136+
}
137+
138+
// Dispatch to global handlers
139+
std::vector<WindowMessageHandler> global_handlers;
140+
{
141+
std::lock_guard<std::mutex> lock(dispatcher.mutex_);
142+
for (const auto& pair : dispatcher.handlers_) {
143+
if (pair.second.is_global && pair.second.handler) {
144+
global_handlers.push_back(pair.second.handler);
145+
}
146+
}
147+
}
148+
149+
// Call global handlers without holding the lock
150+
for (const auto& handler : global_handlers) {
151+
auto handler_result = handler(hwnd, msg, wparam, lparam);
152+
if (handler_result.has_value()) {
153+
return handler_result.value();
154+
}
155+
}
156+
157+
// Call original window procedure
158+
return context->GetOriginalProc()(hwnd, msg, wparam, lparam);
159+
}
160+
161+
bool WindowMessageDispatcher::InstallWindowHook(HWND hwnd) {
162+
if (!hwnd || !IsWindow(hwnd)) {
163+
return false;
164+
}
165+
166+
auto context = GetWindowContext(hwnd);
167+
return context && context->InstallHook();
168+
}
169+
170+
void WindowMessageDispatcher::UninstallWindowHook(HWND hwnd) {
171+
auto context = GetWindowContext(hwnd);
172+
if (context) {
173+
context->UninstallHook();
174+
}
175+
}
176+
177+
std::shared_ptr<WindowMessageDispatcher::WindowContext>
178+
WindowMessageDispatcher::GetWindowContext(HWND hwnd) {
179+
std::lock_guard<std::mutex> lock(mutex_);
180+
181+
auto it = window_contexts_.find(hwnd);
182+
if (it != window_contexts_.end()) {
183+
return it->second;
184+
}
185+
186+
// Create new context
187+
auto context = std::make_shared<WindowContext>(hwnd);
188+
window_contexts_[hwnd] = context;
189+
return context;
190+
}
191+
192+
// WindowContext implementation
193+
void WindowMessageDispatcher::WindowContext::AddHandler(
194+
int id,
195+
WindowMessageHandler handler) {
196+
std::lock_guard<std::mutex> lock(mutex_);
197+
handlers_[id] = std::move(handler);
198+
}
199+
200+
void WindowMessageDispatcher::WindowContext::RemoveHandler(int id) {
201+
std::lock_guard<std::mutex> lock(mutex_);
202+
handlers_.erase(id);
203+
}
204+
205+
std::optional<LRESULT> WindowMessageDispatcher::WindowContext::Dispatch(
206+
HWND hwnd,
207+
UINT msg,
208+
WPARAM wparam,
209+
LPARAM lparam) {
210+
// Copy handlers while holding the lock
211+
std::vector<WindowMessageHandler> handlers_copy;
212+
{
213+
std::lock_guard<std::mutex> lock(mutex_);
214+
handlers_copy.reserve(handlers_.size());
215+
for (const auto& pair : handlers_) {
216+
if (pair.second) {
217+
handlers_copy.push_back(pair.second);
218+
}
219+
}
220+
}
221+
222+
// Call handlers without holding the lock
223+
for (const auto& handler : handlers_copy) {
224+
auto result = handler(hwnd, msg, wparam, lparam);
225+
if (result.has_value()) {
226+
return result;
227+
}
228+
}
229+
230+
return std::nullopt;
231+
}
232+
233+
bool WindowMessageDispatcher::WindowContext::InstallHook() {
234+
if (is_hooked_) {
235+
return true;
236+
}
237+
238+
if (!hwnd_ || !IsWindow(hwnd_)) {
239+
return false;
240+
}
241+
242+
// Store original window procedure
243+
original_proc_ =
244+
reinterpret_cast<WNDPROC>(GetWindowLongPtr(hwnd_, GWLP_WNDPROC));
245+
if (!original_proc_) {
246+
std::cerr << "Failed to get original WindowProc for HWND: " << hwnd_
247+
<< std::endl;
248+
return false;
249+
}
250+
251+
// Set our hooked window procedure
252+
if (SetWindowLongPtr(hwnd_, GWLP_WNDPROC,
253+
reinterpret_cast<LONG_PTR>(HookedWindowProc)) == 0) {
254+
DWORD error = GetLastError();
255+
std::cerr << "Failed to set hooked WindowProc. Error: " << error
256+
<< std::endl;
257+
return false;
258+
}
259+
260+
is_hooked_ = true;
261+
return true;
262+
}
263+
264+
void WindowMessageDispatcher::WindowContext::UninstallHook() {
265+
if (!is_hooked_ || !hwnd_ || !original_proc_) {
266+
return;
267+
}
268+
269+
// Restore original window procedure
270+
if (SetWindowLongPtr(hwnd_, GWLP_WNDPROC,
271+
reinterpret_cast<LONG_PTR>(original_proc_)) == 0) {
272+
DWORD error = GetLastError();
273+
std::cerr << "Failed to restore original WindowProc. Error: " << error
274+
<< std::endl;
275+
}
276+
277+
is_hooked_ = false;
278+
original_proc_ = nullptr;
279+
}
280+
281+
LRESULT CALLBACK
282+
WindowMessageDispatcher::WindowContext::HookedWindowProc(HWND hwnd,
283+
UINT msg,
284+
WPARAM wparam,
285+
LPARAM lparam) {
286+
// Get the context from the dispatcher
287+
WindowMessageDispatcher& dispatcher = WindowMessageDispatcher::Instance();
288+
auto context = dispatcher.GetWindowContext(hwnd);
289+
290+
if (!context) {
291+
return DefWindowProc(hwnd, msg, wparam, lparam);
292+
}
293+
294+
// Dispatch to window-specific handlers
295+
auto result = context->Dispatch(hwnd, msg, wparam, lparam);
296+
if (result.has_value()) {
297+
return result.value();
298+
}
299+
300+
// Call original window procedure
301+
return context->original_proc_(hwnd, msg, wparam, lparam);
302+
}
303+
304+
WNDPROC WindowMessageDispatcher::WindowContext::GetOriginalProc() const {
305+
return original_proc_ ? original_proc_ : DefWindowProc;
306+
}
307+
308+
} // namespace nativeapi

0 commit comments

Comments
 (0)