dxcapture/
window_finder.rs1use winapi::{
4 shared::{
5 minwindef::{BOOL, DWORD, LPARAM},
6 windef::HWND,
7 },
8 um::{
9 dwmapi::{DwmGetWindowAttribute, DWMWA_CLOAKED, DWM_CLOAKED_SHELL},
10 wincon::{GetConsoleTitleW, SetConsoleTitleW},
11 winuser::{
12 EnumWindows, GetAncestor, GetClassNameW, GetShellWindow, GetWindowLongW,
13 GetWindowTextLengthW, GetWindowTextW, IsWindowVisible, GA_ROOT, GWL_EXSTYLE, GWL_STYLE,
14 WS_DISABLED, WS_EX_TOOLWINDOW,
15 },
16 },
17};
18
19#[derive(Debug, Clone)]
21pub struct WindowInfo {
22 pub handle: HWND,
23 pub title: String,
24 pub class_name: String,
25}
26
27fn get_shell_window() -> HWND {
28 unsafe { GetShellWindow() }
29}
30
31fn is_window_visible(window: HWND) -> bool {
32 unsafe { IsWindowVisible(window) == 1 }
33}
34
35fn is_root_window(window: HWND) -> bool {
36 unsafe { GetAncestor(window, GA_ROOT) == window }
37}
38
39fn match_title_and_class_name(window: &WindowInfo, title: &str, class_name: &str) -> bool {
40 window.title == title && window.class_name == class_name
41}
42
43fn is_known_blocked_window(window: &WindowInfo) -> bool {
44 match_title_and_class_name(window, "Task View", "Windows.UI.Core.CoreWindow")
45 || match_title_and_class_name(
46 window,
47 "DesktopWindowXamlSource",
48 "Windows.UI.Core.CoreWindow",
49 )
50 || match_title_and_class_name(window, "PopupHost", "Xaml_WindowedPopupClass")
51}
52
53fn is_capturable_window(window: &WindowInfo) -> bool {
54 if window.title.is_empty()
55 || window.handle == get_shell_window()
56 || !is_window_visible(window.handle)
57 || !is_root_window(window.handle)
58 {
59 return false;
60 }
61
62 let style = unsafe { GetWindowLongW(window.handle, GWL_STYLE) as u32 };
63 if style & WS_DISABLED > 0 {
64 return false;
65 }
66
67 let ex_style = unsafe { GetWindowLongW(window.handle, GWL_EXSTYLE) as u32 };
68 if ex_style & WS_EX_TOOLWINDOW > 0 {
69 return false;
70 }
71
72 if window.class_name == "Windows.UI.Core.CoreWindow"
73 || window.class_name == "ApplicationFrameWindow"
74 {
75 let mut cloaked = 0;
76 let result = unsafe {
77 winrt::ErrorCode(DwmGetWindowAttribute(
78 window.handle,
79 DWMWA_CLOAKED,
80 &mut cloaked as *mut _ as *mut _,
81 std::mem::size_of::<DWORD>() as u32,
82 ) as u32)
83 .ok()
84 };
85 if let Ok(_) = result {
86 if cloaked == DWM_CLOAKED_SHELL {
87 return false;
88 }
89 }
90 }
91
92 if is_known_blocked_window(window) {
93 return false;
94 }
95
96 return true;
97}
98
99extern "system" fn enum_window(handle: HWND, lparam: LPARAM) -> BOOL {
100 let window_text_length = unsafe { GetWindowTextLengthW(handle) };
101 if window_text_length > 0 {
102 let window_text = unsafe {
103 let window_text_length = window_text_length + 1;
104 let mut text_array = vec![0u16; window_text_length as usize];
105 GetWindowTextW(
106 handle,
107 text_array.as_mut_ptr() as *mut _,
108 window_text_length,
109 );
110 std::string::String::from_utf16_lossy(&text_array)
111 .trim_matches(char::from(0))
112 .to_string()
113 };
114 let class_name = unsafe {
115 let class_text_length: i32 = 256;
116 let mut text_array = vec![0u16; class_text_length as usize];
117 GetClassNameW(handle, text_array.as_mut_ptr() as *mut _, class_text_length);
118 std::string::String::from_utf16_lossy(&text_array)
119 .trim_matches(char::from(0))
120 .to_string()
121 };
122 let info = WindowInfo {
123 handle: handle,
124 title: window_text,
125 class_name: class_name,
126 };
127
128 if !is_capturable_window(&info) {
129 return 1;
130 }
131
132 unsafe {
133 let list = std::mem::transmute::<LPARAM, *mut Vec<WindowInfo>>(lparam);
134 (*list).push(info);
135 };
136 }
137
138 return 1;
139}
140
141pub fn get_capturable_windows() -> Vec<WindowInfo> {
143 let current_console_title = unsafe {
145 let console_title_length: u32 = 256;
146 let mut text_array = vec![0u16; console_title_length as usize];
147 GetConsoleTitleW(text_array.as_mut_ptr() as *mut _, console_title_length);
148 std::string::String::from_utf16_lossy(&text_array)
149 .trim_matches(char::from(0))
150 .to_string()
151 };
152
153 unsafe {
154 let temp_guid = uuid::Uuid::new_v4();
155 let mut new_console_title: Vec<u16> = temp_guid.to_string().encode_utf16().collect();
156 new_console_title.push(0);
157 SetConsoleTitleW(new_console_title.as_mut_ptr() as *mut _);
158 };
159 let duration = std::time::Duration::from_millis(40);
160 std::thread::sleep(duration);
161
162 let mut window_list = Vec::<WindowInfo>::new();
163 let result = unsafe { EnumWindows(Some(enum_window), &mut window_list as *mut _ as _) };
164 if result == 0 {
165 panic!("EnumWindows failed!");
166 }
169
170 unsafe {
171 let mut new_console_title: Vec<u16> = current_console_title.encode_utf16().collect();
172 new_console_title.push(0);
173 SetConsoleTitleW(new_console_title.as_mut_ptr() as *mut _);
174 };
175
176 window_list
177}
178
179pub fn find_window(window_name: &str) -> Vec<WindowInfo> {
180 let window_list = get_capturable_windows();
181 let mut windows: Vec<WindowInfo> = Vec::new();
182 for window_info in &window_list {
183 let title = window_info.title.to_lowercase();
184 if title.contains(&window_name.to_string().to_lowercase()) {
185 windows.push(window_info.clone());
186 }
187 }
188 windows
189}