window_getter/platform_impl/
windows.rs

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