Files
cgss-http-hook/src/helper_main.cpp
T
2026-06-23 20:51:38 +08:00

452 lines
16 KiB
C++

#include <windows.h>
#include <shellapi.h>
#include <cstdarg>
#include <cstdio>
#include <fstream>
#include <string>
namespace {
constexpr DWORD kWindowPollIntervalMs = 500;
constexpr int kWindowPollMaxAttempts = 240;
constexpr DWORD kHotkeyPollIntervalMs = 50;
constexpr DWORD kMaintainIntervalMs = 500;
constexpr DWORD kDelayedStyleApplyMs = 4;
constexpr int kToggleKey = VK_F11;
HWND g_main_window = nullptr;
bool g_borderless_active = false;
bool g_saved_state_valid = false;
bool g_borderless_drift_logged = false;
RECT g_saved_window_rect = {};
RECT g_saved_client_rect = {};
LONG_PTR g_saved_style = 0;
LONG_PTR g_saved_exstyle = 0;
DWORD g_target_process_id = 0;
std::wstring get_window_class_name(HWND hwnd) {
wchar_t buffer[256] = {};
int len = GetClassNameW(hwnd, buffer, static_cast<int>(std::size(buffer)));
if (len <= 0) {
return {};
}
return std::wstring(buffer, buffer + len);
}
void helper_logf(const char* format, ...) {
char exe_path[MAX_PATH] = {};
DWORD len = GetModuleFileNameA(nullptr, exe_path, MAX_PATH);
if (len == 0 || len >= MAX_PATH) {
return;
}
char* last_sep = exe_path;
for (char* p = exe_path; *p != '\0'; ++p) {
if (*p == '\\' || *p == '/') {
last_sep = p;
}
}
*last_sep = '\0';
lstrcatA(exe_path, "\\cgss-borderless-helper.log");
char message[1024] = {};
va_list args;
va_start(args, format);
_vsnprintf_s(message, sizeof(message), _TRUNCATE, format, args);
va_end(args);
std::ofstream stream(exe_path, std::ios::app | std::ios::binary);
if (!stream.is_open()) {
return;
}
SYSTEMTIME st = {};
GetLocalTime(&st);
char line[1200] = {};
_snprintf_s(
line,
sizeof(line),
_TRUNCATE,
"[%04u-%02u-%02u %02u:%02u:%02u] %s\r\n",
static_cast<unsigned>(st.wYear),
static_cast<unsigned>(st.wMonth),
static_cast<unsigned>(st.wDay),
static_cast<unsigned>(st.wHour),
static_cast<unsigned>(st.wMinute),
static_cast<unsigned>(st.wSecond),
message
);
stream << line;
}
void set_dpi_awareness() {
using SetProcessDpiAwarenessContextFn = BOOL(WINAPI*)(HANDLE);
auto user32 = GetModuleHandleW(L"user32.dll");
if (!user32) {
user32 = LoadLibraryW(L"user32.dll");
}
if (user32) {
auto set_context = reinterpret_cast<SetProcessDpiAwarenessContextFn>(
GetProcAddress(user32, "SetProcessDpiAwarenessContext")
);
if (set_context && set_context(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) {
helper_logf("[cgss-borderless-helper] enabled per-monitor-v2 DPI awareness");
return;
}
}
SetProcessDPIAware();
helper_logf("[cgss-borderless-helper] enabled legacy DPI awareness");
}
bool parse_arguments() {
LPWSTR* argv = nullptr;
int argc = 0;
argv = CommandLineToArgvW(GetCommandLineW(), &argc);
if (!argv) {
return false;
}
bool ok = false;
for (int i = 1; i + 1 < argc; ++i) {
if (lstrcmpiW(argv[i], L"--pid") == 0) {
g_target_process_id = wcstoul(argv[i + 1], nullptr, 10);
ok = g_target_process_id != 0;
break;
}
}
LocalFree(argv);
return ok;
}
struct WindowSearchContext {
DWORD process_id = 0;
HWND hwnd = nullptr;
};
BOOL CALLBACK enum_windows_proc(HWND hwnd, LPARAM lparam) {
auto* context = reinterpret_cast<WindowSearchContext*>(lparam);
if (!context) {
return TRUE;
}
DWORD process_id = 0;
GetWindowThreadProcessId(hwnd, &process_id);
if (process_id != context->process_id) {
return TRUE;
}
if (GetWindow(hwnd, GW_OWNER) != nullptr) {
return TRUE;
}
LONG_PTR style = GetWindowLongPtrW(hwnd, GWL_STYLE);
if ((style & WS_CHILD) != 0) {
return TRUE;
}
context->hwnd = hwnd;
return FALSE;
}
HWND find_main_window(DWORD process_id) {
WindowSearchContext context;
context.process_id = process_id;
EnumWindows(&enum_windows_proc, reinterpret_cast<LPARAM>(&context));
return context.hwnd;
}
bool get_monitor_rect(HWND hwnd, RECT& rect, MONITORINFOEXW* out_monitor_info = nullptr) {
MONITORINFOEXW monitor_info = {};
monitor_info.cbSize = sizeof(monitor_info);
HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (!monitor || !GetMonitorInfoW(monitor, &monitor_info)) {
return false;
}
rect = monitor_info.rcMonitor;
if (out_monitor_info) {
*out_monitor_info = monitor_info;
}
return true;
}
RECT get_window_rect_for_client_rect(HWND hwnd, const RECT& target_client_rect, LONG_PTR style, LONG_PTR exstyle) {
RECT window_rect = {
target_client_rect.left,
target_client_rect.top,
target_client_rect.right,
target_client_rect.bottom,
};
using AdjustWindowRectExForDpiFn = BOOL(WINAPI*)(LPRECT, DWORD, BOOL, DWORD, UINT);
auto user32 = GetModuleHandleW(L"user32.dll");
auto adjust_for_dpi = user32
? reinterpret_cast<AdjustWindowRectExForDpiFn>(GetProcAddress(user32, "AdjustWindowRectExForDpi"))
: nullptr;
UINT dpi = 96;
using GetDpiForWindowFn = UINT(WINAPI*)(HWND);
auto get_dpi_for_window = user32
? reinterpret_cast<GetDpiForWindowFn>(GetProcAddress(user32, "GetDpiForWindow"))
: nullptr;
if (get_dpi_for_window) {
dpi = get_dpi_for_window(hwnd);
}
if (adjust_for_dpi) {
adjust_for_dpi(&window_rect, static_cast<DWORD>(style), FALSE, static_cast<DWORD>(exstyle), dpi);
} else {
AdjustWindowRectEx(&window_rect, static_cast<DWORD>(style), FALSE, static_cast<DWORD>(exstyle));
}
return window_rect;
}
bool get_client_rect_in_screen(HWND hwnd, RECT& rect) {
RECT client_rect = {};
if (!GetClientRect(hwnd, &client_rect)) {
return false;
}
POINT top_left = {client_rect.left, client_rect.top};
POINT bottom_right = {client_rect.right, client_rect.bottom};
if (!ClientToScreen(hwnd, &top_left) || !ClientToScreen(hwnd, &bottom_right)) {
return false;
}
rect.left = top_left.x;
rect.top = top_left.y;
rect.right = bottom_right.x;
rect.bottom = bottom_right.y;
return true;
}
void save_window_state(HWND hwnd) {
if (g_saved_state_valid) {
return;
}
GetWindowRect(hwnd, &g_saved_window_rect);
get_client_rect_in_screen(hwnd, g_saved_client_rect);
g_saved_style = GetWindowLongPtrW(hwnd, GWL_STYLE);
g_saved_exstyle = GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
g_saved_state_valid = true;
helper_logf(
"[cgss-borderless-helper] saved window rect=(%ld,%ld)-(%ld,%ld) client=(%ld,%ld)-(%ld,%ld)",
static_cast<long>(g_saved_window_rect.left),
static_cast<long>(g_saved_window_rect.top),
static_cast<long>(g_saved_window_rect.right),
static_cast<long>(g_saved_window_rect.bottom),
static_cast<long>(g_saved_client_rect.left),
static_cast<long>(g_saved_client_rect.top),
static_cast<long>(g_saved_client_rect.right),
static_cast<long>(g_saved_client_rect.bottom)
);
}
void apply_borderless_once(HWND hwnd) {
RECT monitor_rect = {};
MONITORINFOEXW monitor_info = {};
if (!get_monitor_rect(hwnd, monitor_rect, &monitor_info)) {
helper_logf("[cgss-borderless-helper] failed to query monitor rect");
return;
}
save_window_state(hwnd);
LONG_PTR style = GetWindowLongPtrW(hwnd, GWL_STYLE);
LONG_PTR exstyle = GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
LONG_PTR new_style = style & ~(WS_CAPTION | WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX);
LONG_PTR new_exstyle = exstyle &
~(WS_EX_DLGMODALFRAME | WS_EX_COMPOSITED | WS_EX_WINDOWEDGE |
WS_EX_CLIENTEDGE | WS_EX_LAYERED | WS_EX_STATICEDGE |
WS_EX_TOOLWINDOW | WS_EX_APPWINDOW);
RECT target_window_rect = get_window_rect_for_client_rect(hwnd, monitor_rect, new_style, new_exstyle);
SetWindowLongPtrW(hwnd, GWL_STYLE, new_style);
SetWindowLongPtrW(hwnd, GWL_EXSTYLE, new_exstyle);
SetWindowPos(
hwnd,
nullptr,
target_window_rect.left,
target_window_rect.top,
target_window_rect.right - target_window_rect.left,
target_window_rect.bottom - target_window_rect.top,
SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING
);
Sleep(kDelayedStyleApplyMs);
RECT actual_rect = {};
GetWindowRect(hwnd, &actual_rect);
RECT actual_client_rect = {};
get_client_rect_in_screen(hwnd, actual_client_rect);
helper_logf(
"[cgss-borderless-helper] applied borderless class=%ls monitor=%ls target-client=(%ld,%ld)-(%ld,%ld) target-window=(%ld,%ld)-(%ld,%ld) actual-window=(%ld,%ld)-(%ld,%ld) actual-client=(%ld,%ld)-(%ld,%ld)",
get_window_class_name(hwnd).c_str(),
monitor_info.szDevice,
static_cast<long>(monitor_rect.left),
static_cast<long>(monitor_rect.top),
static_cast<long>(monitor_rect.right),
static_cast<long>(monitor_rect.bottom),
static_cast<long>(target_window_rect.left),
static_cast<long>(target_window_rect.top),
static_cast<long>(target_window_rect.right),
static_cast<long>(target_window_rect.bottom),
static_cast<long>(actual_rect.left),
static_cast<long>(actual_rect.top),
static_cast<long>(actual_rect.right),
static_cast<long>(actual_rect.bottom),
static_cast<long>(actual_client_rect.left),
static_cast<long>(actual_client_rect.top),
static_cast<long>(actual_client_rect.right),
static_cast<long>(actual_client_rect.bottom)
);
}
bool needs_borderless_reapply(HWND hwnd) {
RECT expected_client_rect = {};
if (!get_monitor_rect(hwnd, expected_client_rect)) {
return false;
}
RECT current_client_rect = {};
if (!get_client_rect_in_screen(hwnd, current_client_rect)) {
return false;
}
LONG_PTR style = GetWindowLongPtrW(hwnd, GWL_STYLE);
const bool has_border = (style & (WS_CAPTION | WS_THICKFRAME | WS_SYSMENU)) != 0;
const bool rect_mismatch = current_client_rect.left != expected_client_rect.left ||
current_client_rect.top != expected_client_rect.top ||
current_client_rect.right != expected_client_rect.right ||
current_client_rect.bottom != expected_client_rect.bottom;
return has_border || rect_mismatch;
}
void restore_window(HWND hwnd) {
if (!g_saved_state_valid) {
return;
}
SetWindowLongPtrW(hwnd, GWL_STYLE, g_saved_style);
SetWindowLongPtrW(hwnd, GWL_EXSTYLE, g_saved_exstyle);
SetWindowPos(
hwnd,
nullptr,
g_saved_window_rect.left,
g_saved_window_rect.top,
g_saved_window_rect.right - g_saved_window_rect.left,
g_saved_window_rect.bottom - g_saved_window_rect.top,
SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING
);
helper_logf("[cgss-borderless-helper] restored original window style and rect");
}
void toggle_borderless(HWND hwnd) {
if (!hwnd) {
return;
}
RECT monitor_rect = {};
MONITORINFOEXW monitor_info = {};
if (!get_monitor_rect(hwnd, monitor_rect, &monitor_info)) {
helper_logf("[cgss-borderless-helper] failed to resolve monitor rect for toggle");
return;
}
if (g_borderless_active) {
g_borderless_active = false;
g_borderless_drift_logged = false;
restore_window(hwnd);
return;
}
g_borderless_active = true;
g_borderless_drift_logged = false;
helper_logf(
"[cgss-borderless-helper] borderless enabled monitor=%ls rect=(%ld,%ld)-(%ld,%ld)",
monitor_info.szDevice,
static_cast<long>(monitor_rect.left),
static_cast<long>(monitor_rect.top),
static_cast<long>(monitor_rect.right),
static_cast<long>(monitor_rect.bottom)
);
apply_borderless_once(hwnd);
}
}
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
set_dpi_awareness();
if (!parse_arguments()) {
helper_logf("[cgss-borderless-helper] missing or invalid --pid argument");
return 1;
}
HANDLE target_process = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, g_target_process_id);
if (!target_process) {
helper_logf("[cgss-borderless-helper] failed to open target process");
return 1;
}
for (int i = 0; i < kWindowPollMaxAttempts; ++i) {
g_main_window = find_main_window(g_target_process_id);
if (g_main_window) {
helper_logf("[cgss-borderless-helper] found main window hwnd=0x%p", g_main_window);
break;
}
if (WaitForSingleObject(target_process, 0) == WAIT_OBJECT_0) {
CloseHandle(target_process);
helper_logf("[cgss-borderless-helper] target process exited before main window appeared");
return 0;
}
Sleep(kWindowPollIntervalMs);
}
if (!g_main_window) {
CloseHandle(target_process);
helper_logf("[cgss-borderless-helper] timed out waiting for main window");
return 1;
}
SHORT previous_hotkey_state = 0;
DWORD last_maintain_tick = GetTickCount();
while (WaitForSingleObject(target_process, 0) != WAIT_OBJECT_0) {
if (!IsWindow(g_main_window)) {
helper_logf("[cgss-borderless-helper] target window no longer valid");
break;
}
SHORT current_hotkey_state = GetAsyncKeyState(kToggleKey);
bool is_pressed = (current_hotkey_state & 0x8000) != 0;
bool was_pressed = (previous_hotkey_state & 0x8000) != 0;
if (is_pressed && !was_pressed) {
helper_logf("[cgss-borderless-helper] F11 pressed");
toggle_borderless(g_main_window);
}
previous_hotkey_state = current_hotkey_state;
DWORD now = GetTickCount();
if (g_borderless_active && now - last_maintain_tick >= kMaintainIntervalMs) {
if (needs_borderless_reapply(g_main_window)) {
if (!g_borderless_drift_logged) {
helper_logf("[cgss-borderless-helper] detected borderless drift, reapplying");
g_borderless_drift_logged = true;
}
apply_borderless_once(g_main_window);
} else {
g_borderless_drift_logged = false;
}
last_maintain_tick = now;
}
Sleep(kHotkeyPollIntervalMs);
}
CloseHandle(target_process);
helper_logf("[cgss-borderless-helper] target process exited, helper shutting down");
return 0;
}