Skip to content

Instantly share code, notes, and snippets.

@tadghh
Created January 28, 2025 18:44
Show Gist options
  • Save tadghh/7d52becede210059978f6d585ea9bcb1 to your computer and use it in GitHub Desktop.
Save tadghh/7d52becede210059978f6d585ea9bcb1 to your computer and use it in GitHub Desktop.
// 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