From 94bda30e1f1d68d985cb71db7f5b1a06722fc06a Mon Sep 17 00:00:00 2001 From: Monitor Brightness Control Date: Sun, 18 Oct 2026 03:34:24 +0200 Subject: [PATCH] fix(foreground): close the process handle after reading the exe name The foreground watcher opened the focused window's process with OpenProcess every 750 ms and never closed the handle. Over 20 days of uptime the app held 1.28 million process handles, growing by about 1.3 per second. The handle now lives in a guard that closes it on every path, including when QueryFullProcessImageNameW fails. --- app/src-tauri/src/foreground.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/app/src-tauri/src/foreground.rs b/app/src-tauri/src/foreground.rs index a8a8b7e..7d11350 100644 --- a/app/src-tauri/src/foreground.rs +++ b/app/src-tauri/src/foreground.rs @@ -76,6 +76,19 @@ mod windows_impl { .map(|s| s.to_string_lossy().to_string()) } + /// Process handle that is closed when it goes out of scope, on every path. + struct ProcessHandle(windows::Win32::Foundation::HANDLE); + + impl Drop for ProcessHandle { + fn drop(&mut self) { + // SAFETY: the handle came from a successful OpenProcess and is + // closed exactly once, here. + unsafe { + let _ = windows::Win32::Foundation::CloseHandle(self.0); + } + } + } + fn current_exe_name() -> Option { use windows::Win32::Foundation::HWND; use windows::Win32::System::Threading::{ @@ -97,11 +110,14 @@ mod windows_impl { if pid == 0 { return None; } - let proc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid).ok()?; + // This runs every 750 ms: a handle left open per call piles up to + // millions of process handles over days of uptime. + let proc = + ProcessHandle(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid).ok()?); let mut buf: Vec = vec![0u16; 1024]; let mut len: u32 = buf.len() as u32; let sl = windows::core::PWSTR(buf.as_mut_ptr()); - QueryFullProcessImageNameW(proc, PROCESS_NAME_FORMAT(0), sl, &mut len).ok()?; + QueryFullProcessImageNameW(proc.0, PROCESS_NAME_FORMAT(0), sl, &mut len).ok()?; buf.truncate(len as usize); let path = String::from_utf16_lossy(&buf); std::path::Path::new(&path)