Skip to content

Commit f6725b4

Browse files
committed
Add internal WindowProc hooking for delegate management
Introduces logic to hook and restore the WindowProc of the top-level window, dispatching messages to registered delegates. The internal WindowProc is set up when the first delegate is registered and restored when the last is unregistered, improving message handling and resource management.
1 parent 94822e1 commit f6725b4

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed

src/platform/windows/window_proc_delegate_manager.cpp

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#include "window_proc_delegate_manager.h"
2+
#include <windows.h>
3+
#include <iostream>
24

35
namespace nativeapi {
46

@@ -7,6 +9,108 @@ WindowProcDelegateManager& WindowProcDelegateManager::GetInstance() {
79
return instance;
810
}
911

12+
LRESULT CALLBACK WindowProcDelegateManager::InternalWindowProc(HWND hwnd,
13+
UINT message,
14+
WPARAM wparam,
15+
LPARAM lparam) {
16+
WindowProcDelegateManager& manager = GetInstance();
17+
std::lock_guard<std::mutex> lock(manager.mutex_);
18+
19+
// Dispatch message to all registered delegates
20+
for (const auto& pair : manager.delegates_) {
21+
const auto& delegate = pair.second;
22+
if (delegate) {
23+
auto result = delegate(hwnd, message, wparam, lparam);
24+
if (result.has_value()) {
25+
return result.value();
26+
}
27+
}
28+
}
29+
30+
// If no delegate handled the message, call the original WindowProc
31+
if (manager.original_window_proc_) {
32+
return CallWindowProc(manager.original_window_proc_, hwnd, message, wparam,
33+
lparam);
34+
}
35+
36+
// Fallback to DefWindowProc if no original proc is available
37+
return DefWindowProc(hwnd, message, wparam, lparam);
38+
}
39+
40+
HWND WindowProcDelegateManager::GetTopLevelWindow() {
41+
// Try to get the foreground window first
42+
HWND hwnd = GetForegroundWindow();
43+
if (hwnd && IsWindow(hwnd)) {
44+
return hwnd;
45+
}
46+
47+
// If no foreground window, try to get the active window
48+
hwnd = GetActiveWindow();
49+
if (hwnd && IsWindow(hwnd)) {
50+
return hwnd;
51+
}
52+
53+
// If still no window, try to find any top-level window
54+
hwnd = FindWindow(nullptr, nullptr);
55+
if (hwnd && IsWindow(hwnd)) {
56+
return hwnd;
57+
}
58+
59+
return nullptr;
60+
}
61+
62+
void WindowProcDelegateManager::SetupInternalWindowProc() {
63+
if (window_proc_hooked_) {
64+
return; // Already hooked
65+
}
66+
67+
top_level_window_ = GetTopLevelWindow();
68+
if (!top_level_window_) {
69+
std::cerr << "Failed to get top level window for WindowProc hooking"
70+
<< std::endl;
71+
return;
72+
}
73+
74+
// Store the original WindowProc
75+
original_window_proc_ = reinterpret_cast<WNDPROC>(
76+
GetWindowLongPtr(top_level_window_, GWLP_WNDPROC));
77+
if (!original_window_proc_) {
78+
std::cerr << "Failed to get original WindowProc" << std::endl;
79+
return;
80+
}
81+
82+
// Set our internal WindowProc
83+
if (SetWindowLongPtr(top_level_window_, GWLP_WNDPROC,
84+
reinterpret_cast<LONG_PTR>(InternalWindowProc)) == 0) {
85+
DWORD error = GetLastError();
86+
std::cerr << "Failed to set internal WindowProc. Error: " << error
87+
<< std::endl;
88+
return;
89+
}
90+
91+
window_proc_hooked_ = true;
92+
}
93+
94+
void WindowProcDelegateManager::RestoreOriginalWindowProc() {
95+
if (!window_proc_hooked_ || !top_level_window_ || !original_window_proc_) {
96+
return;
97+
}
98+
99+
// Restore the original WindowProc
100+
if (SetWindowLongPtr(top_level_window_, GWLP_WNDPROC,
101+
reinterpret_cast<LONG_PTR>(original_window_proc_)) ==
102+
0) {
103+
DWORD error = GetLastError();
104+
std::cerr << "Failed to restore original WindowProc. Error: " << error
105+
<< std::endl;
106+
}
107+
108+
// Reset state
109+
window_proc_hooked_ = false;
110+
top_level_window_ = nullptr;
111+
original_window_proc_ = nullptr;
112+
}
113+
10114
int WindowProcDelegateManager::RegisterDelegate(WindowProcDelegate delegate) {
11115
if (!delegate) {
12116
return -1;
@@ -16,6 +120,12 @@ int WindowProcDelegateManager::RegisterDelegate(WindowProcDelegate delegate) {
16120

17121
int id = next_id_++;
18122
delegates_[id] = std::move(delegate);
123+
124+
// If this is the first delegate, setup the internal WindowProc
125+
if (delegates_.size() == 1) {
126+
SetupInternalWindowProc();
127+
}
128+
19129
return id;
20130
}
21131

@@ -29,6 +139,12 @@ bool WindowProcDelegateManager::UnregisterDelegate(int id) {
29139
auto it = delegates_.find(id);
30140
if (it != delegates_.end()) {
31141
delegates_.erase(it);
142+
143+
// If no delegates remain, restore the original WindowProc
144+
if (delegates_.empty()) {
145+
RestoreOriginalWindowProc();
146+
}
147+
32148
return true;
33149
}
34150

src/platform/windows/window_proc_delegate_manager.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,27 @@ class WindowProcDelegateManager {
2525
// found
2626
bool UnregisterDelegate(int id);
2727

28+
// Internal WindowProc function that dispatches messages to registered delegates
29+
static LRESULT CALLBACK InternalWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
30+
2831
private:
32+
// Get the top level window handle
33+
HWND GetTopLevelWindow();
34+
35+
// Setup internal WindowProc for the top level window
36+
void SetupInternalWindowProc();
37+
38+
// Restore original WindowProc when no delegates are registered
39+
void RestoreOriginalWindowProc();
40+
2941
mutable std::mutex mutex_;
3042
std::unordered_map<int, WindowProcDelegate> delegates_;
3143
int next_id_ = 1;
44+
45+
// Store original WindowProc and window handle
46+
HWND top_level_window_ = nullptr;
47+
WNDPROC original_window_proc_ = nullptr;
48+
bool window_proc_hooked_ = false;
3249
};
3350

3451
} // namespace nativeapi

0 commit comments

Comments
 (0)