window_getter/platform_impl/
windows.rs

1use windows::{
2    Win32::{
3        Foundation::LPARAM,
4        Foundation::{HWND, RECT},
5        UI::WindowsAndMessaging::IsWindow,
6    },
7    core::BOOL,
8};
9
10use crate::{Error, Window};
11
12pub use error::WindowsError;
13pub use window::WindowsWindow;
14
15pub type WindowsBounds = RECT;
16pub type WindowsWindowId = HWND;
17
18/// Retrieves a window by its platform-specific identifier.
19pub fn get_window(id: WindowsWindowId) -> Option<Window> {
20    let hwnd = id;
21
22    if hwnd.is_invalid() || !unsafe { IsWindow(Some(hwnd)) }.as_bool() {
23        None
24    } else {
25        Some(Window(WindowsWindow::new(hwnd)))
26    }
27}
28
29unsafe extern "system" fn enum_windows_callback(hwnd: HWND, lparam: LPARAM) -> BOOL {
30    let windows = unsafe { &mut *(lparam.0 as *mut Vec<Window>) };
31    windows.push(Window(WindowsWindow::new(hwnd)));
32
33    BOOL::from(true)
34}
35
36/// Retrieves a list of all windows on the screen.
37pub fn get_windows() -> Result<Vec<Window>, Error> {
38    let mut windows = Vec::new();
39
40    // SAFETY: `Vec` should not be used during enumeration because it is used by mutable reference.
41    unsafe {
42        windows::Win32::UI::WindowsAndMessaging::EnumWindows(
43            Some(enum_windows_callback),
44            LPARAM(&mut windows as *const Vec<Window> as _),
45        )?
46    };
47
48    Ok(windows)
49}
50
51mod window {
52    use windows::Win32::{
53        Foundation::{self, HWND, RECT},
54        Graphics::Dwm::{DWMWA_EXTENDED_FRAME_BOUNDS, DwmGetWindowAttribute},
55        System::Threading,
56        UI::WindowsAndMessaging::{self, GetWindowRect},
57    };
58
59    use crate::Bounds;
60
61    use super::WindowsError;
62
63    /// Represents a window in the Windows platform.
64    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
65    pub struct WindowsWindow(pub(crate) HWND);
66
67    unsafe impl Send for WindowsWindow {}
68    unsafe impl Sync for WindowsWindow {}
69
70    impl WindowsWindow {
71        /// Creates a new `WindowsWindow` from a raw `HWND`.
72        ///
73        /// # Warning
74        /// You must ensure that the `hwnd` is a valid window handle. If you pass an invalid handle,
75        /// it may lead to errors on methods.
76        /// You can use [`get_window`][super::get_window] to safely retrieve a `WindowsWindow`.
77        pub fn new(hwnd: HWND) -> Self {
78            Self(hwnd)
79        }
80
81        /// Returns the raw handle to the window.
82        pub fn hwnd(&self) -> HWND {
83            self.0
84        }
85
86        /// Returns the title of the window.
87        pub fn title(&self) -> Result<Option<String>, WindowsError> {
88            let mut buffer = [0u16; 256];
89            let length = unsafe { WindowsAndMessaging::GetWindowTextW(self.0, &mut buffer) };
90
91            if length == 0 {
92                let raw = windows::core::Error::from_thread();
93
94                return match raw.code() {
95                    // If the length is 0 and error is success,
96                    // it means the window has no title.
97                    Foundation::S_OK => Ok(None),
98                    _ => Err(raw),
99                };
100            }
101
102            Ok(Some(String::from_utf16_lossy(&buffer[..length as usize])))
103        }
104
105        /// Returns the raw rectangle of the window by [`GetWindowRect`].
106        ///
107        /// It includes the invisible resize borders.
108        /// So it may not be the same as the window rectangle that is actually seen.
109        pub fn rect(&self) -> Result<RECT, WindowsError> {
110            Ok(unsafe {
111                let mut rect = std::mem::zeroed();
112                GetWindowRect(self.0, &mut rect)?;
113                rect
114            })
115        }
116
117        /// This will return [`rect`](Self::rect) value wrapped in [`WindowsBounds`](super::WindowsBounds).
118        pub fn bounds(&self) -> Result<Bounds, WindowsError> {
119            Ok(self.rect()?.into())
120        }
121
122        /// Returns the extended frame bounds of the window
123        /// by [`DwmGetWindowAttribute`] with [`DWMWA_EXTENDED_FRAME_BOUNDS`].
124        pub fn extended_frame_bounds(&self) -> Result<RECT, WindowsError> {
125            Ok(unsafe {
126                let mut rect: RECT = std::mem::zeroed();
127                DwmGetWindowAttribute(
128                    self.0,
129                    DWMWA_EXTENDED_FRAME_BOUNDS,
130                    &mut rect as *mut RECT as _,
131                    std::mem::size_of::<RECT>() as _,
132                )?;
133                rect
134            })
135        }
136
137        /// Returns the bounds of the window.
138        /// This will return [`extended_frame_bounds`](Self::extended_frame_bounds)
139        /// value wrapped in [`WindowsBounds`](super::WindowsBounds).
140        pub fn visible_bounds(&self) -> Result<Bounds, WindowsError> {
141            Ok(self.extended_frame_bounds()?.into())
142        }
143
144        /// Returns the process ID of the owner of this window.
145        pub fn owner_pid(&self) -> Result<u32, WindowsError> {
146            let mut pid = 0;
147            let thread =
148                unsafe { WindowsAndMessaging::GetWindowThreadProcessId(self.0, Some(&mut pid)) };
149
150            if thread == 0 {
151                Err(windows::core::Error::from_thread())
152            } else {
153                Ok(pid)
154            }
155        }
156
157        /// Returns the handle to the process that owns this window.
158        pub fn owner_process_handle(
159            &self,
160        ) -> Result<windows::Win32::Foundation::HANDLE, WindowsError> {
161            let pid = self.owner_pid()?;
162            let process_handle = unsafe {
163                Threading::OpenProcess(
164                    Threading::PROCESS_QUERY_INFORMATION | Threading::PROCESS_VM_READ,
165                    false,
166                    pid,
167                )?
168            };
169
170            Ok(process_handle)
171        }
172
173        /// Returns the file name of the process that owns this window.
174        /// This will return the name of the executable file.
175        pub fn owner_name(&self) -> Result<String, WindowsError> {
176            let process_handle = self.owner_process_handle()?;
177
178            let mut buffer = [0u16; 256];
179            let length = unsafe {
180                windows::Win32::System::ProcessStatus::GetModuleBaseNameW(
181                    process_handle,
182                    None,
183                    &mut buffer,
184                )
185            };
186
187            if length == 0 {
188                return Err(windows::core::Error::from_thread());
189            }
190
191            Ok(String::from_utf16_lossy(&buffer[..length as usize]))
192        }
193
194        /// Checks if the window is foreground.
195        pub fn is_foreground(&self) -> bool {
196            self.0 == unsafe { WindowsAndMessaging::GetForegroundWindow() }
197        }
198    }
199}
200
201mod error {
202    pub type WindowsError = windows::core::Error;
203
204    impl From<WindowsError> for crate::Error {
205        fn from(error: WindowsError) -> Self {
206            if error.code() == windows::Win32::Foundation::E_ACCESSDENIED {
207                Self::PermissionDenied(error)
208            } else {
209                Self::PlatformSpecificError(error)
210            }
211        }
212    }
213}