libobs_window_helper\util/
win_iterator.rs1use anyhow::Result;
2use windows::{
3 core::PWSTR,
4 Win32::{
5 Foundation::HWND,
6 UI::WindowsAndMessaging::{
7 FindWindowExW, GetDesktopWindow, GetWindow, GW_CHILD, GW_HWNDNEXT,
8 },
9 },
10};
11
12use crate::{
13 get_thread_proc_id,
14 validators::{is_window_valid, WindowSearchMode},
15 window::get_window_class,
16 ProcessInfo,
17};
18
19pub unsafe fn is_uwp_window(hwnd: HWND) -> Result<bool> {
20 if hwnd.is_invalid() {
21 return Ok(false);
22 }
23
24 let class = get_window_class(hwnd)?;
25 Ok(class == "ApplicationFrameWindow")
26}
27
28pub unsafe fn get_uwp_actual_window(parent: HWND) -> Result<Option<HWND>> {
29 let ProcessInfo {
30 process_id: parent_id,
31 ..
32 } = get_thread_proc_id(parent)?;
33
34 let mut child = FindWindowExW(Some(parent), None, PWSTR::null(), PWSTR::null())?;
35
36 while !child.is_invalid() {
37 let ProcessInfo {
38 process_id: child_id,
39 ..
40 } = get_thread_proc_id(child)?;
41
42 if child_id != parent_id {
43 return Ok(Some(child));
44 }
45
46 child = FindWindowExW(Some(parent), Some(child), PWSTR::null(), PWSTR::null())
47 .unwrap_or(HWND::default());
48 }
49
50 return Ok(None);
51}
52
53pub unsafe fn next_window(
54 window: Option<HWND>,
55 mode: WindowSearchMode,
56 parent: &mut Option<HWND>,
57 use_find_window_ex: bool,
58) -> anyhow::Result<Option<HWND>> {
59 let mut window = window.unwrap_or(HWND::default());
60
61 let parent_valid = parent.is_some_and(|e| !e.is_invalid());
62 if parent_valid {
63 window = parent.unwrap_or(HWND::default());
64 *parent = None;
65 }
66
67 loop {
68 window = if use_find_window_ex {
69 FindWindowExW(Some(GetDesktopWindow()), Some(window), PWSTR::null(), PWSTR::null())
70 } else {
71 GetWindow(window, GW_HWNDNEXT)
72 }.unwrap_or(HWND::default());
73
74 let valid = is_window_valid(window, mode).ok().unwrap_or(false);
75 if window.is_invalid() || valid {
76 break;
77 }
78 }
79
80 let window_opt = if window.is_invalid() {
81 None
82 } else {
83 Some(window)
84 };
85
86 if is_uwp_window(window)? {
87 if format!("{:?}", window.0).ends_with("041098") {
88 println!("UWP Window: {:?}", window);
89 }
90 let actual = get_uwp_actual_window(window)?;
91 if let Some(child) = actual {
92 *parent = window_opt;
93
94 return Ok(Some(child));
95 }
96 }
97
98 return Ok(window_opt);
99}
100
101pub unsafe fn first_window(
102 mode: WindowSearchMode,
103 parent: &mut Option<HWND>,
104 use_find_window_ex: &mut bool,
105) -> anyhow::Result<HWND> {
106 let mut window = FindWindowExW(
107 Some(GetDesktopWindow()),
108 None,
109 PWSTR::null(),
110 PWSTR::null(),
111 )
112 .ok();
113
114 if window.is_none() {
115 *use_find_window_ex = false;
116 window = GetWindow(GetDesktopWindow(), GW_CHILD).ok();
117 } else {
118 *use_find_window_ex = true;
119 }
120
121 *parent = None;
122
123 let is_valid = window.is_some_and(|e| is_window_valid(e, mode).unwrap_or(false));
124
125 if !is_valid {
126 window = next_window(window, mode, parent, *use_find_window_ex)?;
127
128 if window.is_none() && *use_find_window_ex {
129 *use_find_window_ex = false;
130
131 window = GetWindow(GetDesktopWindow(), GW_CHILD).ok();
132 let valid = window.is_some_and(|e| is_window_valid(e, mode).unwrap_or(false));
133
134 if !valid {
135 window = next_window(window, mode, parent, *use_find_window_ex)?;
136 }
137 }
138 }
139
140 if window.is_none() {
141 return Err(anyhow::anyhow!("No window found"));
142 }
143
144 let window = window.unwrap();
145 if is_uwp_window(window)? {
146 let child = get_uwp_actual_window(window)?;
147 if let Some(c) = child {
148 *parent = Some(window);
149 return Ok(c);
150 }
151 }
152
153 return Ok(window);
154}