Created
January 28, 2025 18:44
-
-
Save tadghh/7d52becede210059978f6d585ea9bcb1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Using the windows api to get a parents handle with only the childs window class | |
unsafe extern "system" fn enum_window_proc(hwnd: HWND, lparam: LPARAM) -> BOOL { | |
let data = &mut *(lparam.0 as *mut FindWindowData); | |
let mut pid = 0; | |
if !IsWindowVisible(hwnd).as_bool() { | |
return true.into(); | |
} | |
GetWindowThreadProcessId(hwnd, Some(&mut pid)); | |
if pid == data.target_pid { | |
data.found_hwnd = Some(hwnd); | |
} | |
true.into() | |
} | |
pub fn get_pid_from_window_class(class_name: &str) -> Vec<u32> { | |
let mut data = SearchWindowData { | |
class_name: class_name.to_string(), | |
pids: Vec::new(), | |
}; | |
unsafe { | |
let mut hwnd = FindWindowExW(None, None, PCWSTR::null(), PCWSTR::null()) | |
.ok() | |
.filter(|h| !h.is_invalid()); | |
while let Some(current_hwnd) = hwnd { | |
let _ = EnumChildWindows( | |
Some(current_hwnd), | |
Some(enum_child_proc), | |
LPARAM(&mut data as *mut _ as isize), | |
); | |
hwnd = FindWindowExW(None, Some(current_hwnd), PCWSTR::null(), PCWSTR::null()) | |
.ok() | |
.filter(|h| !h.is_invalid()); | |
} | |
} | |
data.pids | |
} | |
unsafe extern "system" fn enum_child_proc(hwnd: HWND, lparam: LPARAM) -> BOOL { | |
let mut class_buffer = vec![0u16; 128]; | |
let data = &mut *(lparam.0 as *mut SearchWindowData); | |
let len = GetClassNameW(hwnd, &mut class_buffer); | |
if len > 0 { | |
let window_class = String::from_utf16_lossy(&class_buffer[..len as usize]); | |
if window_class == data.class_name { | |
let mut pid = 0; | |
GetWindowThreadProcessId(hwnd, Some(&mut pid)); | |
if !data.pids.contains(&pid) { | |
data.pids.push(pid); | |
} | |
} | |
} | |
true.into() // Continue enumeration to find all matches | |
} | |
fn get_window_class_name(hwnd: HWND) -> Option<String> { | |
let mut class_name = [0u16; MAX_PATH as usize]; | |
// Get the class name of the window | |
unsafe { | |
let length = GetClassNameW(hwnd, &mut class_name); | |
if length == 0 { | |
return None; | |
} | |
// Convert the buffer to a string, truncating at the first null character | |
String::from_utf16_lossy(&class_name[..length as usize]) | |
.trim_end_matches('\0') | |
.to_string() | |
.into() | |
} | |
} | |
fn find_parent_from_child_class( | |
child_class: &str, | |
) -> windows::core::Result<Option<(HWND, String)>> { | |
// First find the child window by its class name | |
let child_hwnd = match find_window_by_class(child_class)? { | |
Some(hwnd) => hwnd, | |
None => { | |
println!("Could not find window with class: {}", child_class); | |
return Ok(None); | |
} | |
}; | |
// Then get its parent | |
let parent_hwnd = get_real_parent(child_hwnd)?; | |
if parent_hwnd.is_invalid() || parent_hwnd.0 == std::ptr::null_mut() { | |
println!("No parent window found"); | |
return Ok(None); | |
} | |
// Get the class name of the parent window | |
Ok(get_window_class_name(parent_hwnd).map(|class_name| (parent_hwnd, class_name))) | |
} | |
fn get_real_parent(hwnd: HWND) -> windows::core::Result<HWND> { | |
unsafe { | |
// First try to get the owner window | |
match GetWindow(hwnd, GW_OWNER) { | |
Ok(owner) if !owner.is_invalid() => Ok(owner), | |
// If no owner or error, get the parent window | |
_ => Ok(GetAncestor(hwnd, GA_PARENT)), | |
} | |
} | |
} | |
fn find_window_by_class(target_class: &str) -> windows::core::Result<Option<HWND>> { | |
struct SearchState<'a> { | |
target_class: &'a str, | |
found_hwnd: Option<HWND>, | |
} | |
unsafe extern "system" fn enum_child_windows_proc(child_hwnd: HWND, lparam: LPARAM) -> BOOL { | |
let state = &mut *(lparam.0 as *mut SearchState); | |
if let Some(class_name) = get_window_class_name(child_hwnd) { | |
if class_name == state.target_class { | |
state.found_hwnd = Some(child_hwnd); | |
return false.into(); // Stop enumeration | |
} | |
} | |
true.into() // Continue enumeration | |
} | |
unsafe extern "system" fn enum_windows_proc(parent_hwnd: HWND, lparam: LPARAM) -> BOOL { | |
let state = &mut *(lparam.0 as *mut SearchState); | |
let _ = EnumChildWindows(Some(parent_hwnd), Some(enum_child_windows_proc), lparam); | |
// Stop top-level enumeration if we found the window | |
if state.found_hwnd.is_some() { | |
state.found_hwnd = state.found_hwnd; | |
false.into() | |
} else { | |
true.into() | |
} | |
} | |
fn find_topmost_parent(hwnd: HWND) -> Option<HWND> { | |
unsafe { | |
let current_hwnd = GetParent(hwnd).ok()?; | |
Some(current_hwnd) | |
} | |
} | |
let mut state = SearchState { | |
target_class, | |
found_hwnd: None, | |
}; | |
unsafe { | |
_ = EnumWindows( | |
Some(enum_windows_proc), | |
LPARAM(&mut state as *mut _ as isize), | |
); | |
} | |
if let Some(found_hwnd) = state.found_hwnd { | |
Ok(find_topmost_parent(found_hwnd)) | |
} else { | |
Ok(None) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment