windows_win/raw/
window.rs

1//! Provides functions to interact with windows.
2
3use std::ffi;
4use std::os::windows::ffi::OsStrExt;
5use core::ptr;
6
7use crate::sys::SetLastErrorEx;
8
9use crate::sys::*;
10use crate::utils::{self, Result};
11
12///Determines if window is visible.
13///
14///#Note:
15/// The visibility state of a window is indicated by the **WS_VISIBLE** style bit.
16///
17/// When **WS_VISIBLE** is set, the window is displayed and subsequent drawing into it is displayed as long as the window has the **WS_VISIBLE** style.
18///
19///# Parameters
20///
21///* ```window``` - A handle to the window to be tested.
22///
23///# Returns
24///
25///* ```true``` - If window is visible.
26///* ```false``` - Otherwise.
27pub fn is_visible(window: HWND) -> bool {
28    return unsafe {IsWindowVisible(window) != 0};
29}
30
31///Retrieves window's class name.
32///
33///# Parameters
34///
35///* ```window``` - A handle to the window.
36///
37///# Returns
38///
39///* ```Ok``` - Contains name of class.
40///* ```Err``` - Error reason.
41pub fn get_class(window: HWND) -> Result<String> {
42    const BUF_SIZE: usize = 512;
43    let mut buff: [u16; BUF_SIZE] = [0; BUF_SIZE];
44
45    let writ_chars = unsafe { RealGetWindowClassW(window,
46                                                  buff.as_mut_ptr(),
47                                                  BUF_SIZE as u32) };
48
49    if writ_chars == 0 {
50        return Err(utils::get_last_error());
51    }
52
53    Ok(String::from_utf16_lossy(&buff[0..writ_chars as usize]))
54}
55
56///Retrieves window's title.
57///
58///# Parameters
59///
60///* ```window``` - A handle to the window to be tested.
61///
62///# Return
63///
64///* ```Ok``` - Contains name of class.
65///* ```Err``` - Error reason.
66pub fn get_text(window: HWND) -> Result<String> {
67    const BUF_SIZE: usize = 512;
68    let mut buff: [u16; BUF_SIZE] = [0; BUF_SIZE];
69
70    let writ_chars = unsafe { GetWindowTextW(window,
71                                             buff.as_mut_ptr(),
72                                             BUF_SIZE as i32) };
73
74    if writ_chars == 0 {
75        return Err(utils::get_last_error());
76    }
77
78    Ok(String::from_utf16_lossy(&buff[0..writ_chars as usize]))
79}
80
81unsafe extern "system" fn callback_enum_windows<T: FnMut(HWND)>(window: HWND, param: LPARAM) -> i32 {
82    let func = &mut *(param as *mut T);
83
84    func(window);
85
86    1
87}
88
89unsafe extern "system" fn callback_enum_windows_until<T: FnMut(HWND) -> i32>(window: HWND, param: LPARAM) -> i32 {
90    let func = &mut *(param as *mut T);
91
92    func(window)
93}
94
95///Enumerates over windows handles and calls callback on each
96///
97///# Parameters
98///
99///* ```parent``` - Handle of parent window to look up through its children only. Optional.
100///* ```cmp_func``` - Callback that will be called on each window.
101///
102///# Return
103///
104///* ```Ok``` - Success.
105///* ```Err``` - Error reason.
106pub fn enum_by<T: FnMut(HWND)>(parent: Option<HWND>, mut cmp_func: T) -> Result<()> {
107    let lparam = &mut cmp_func as *mut _ as LPARAM;
108
109    let result: i32;
110
111    if let Some(parent_window) = parent {
112        result = unsafe { EnumChildWindows(parent_window, Some(callback_enum_windows::<T>), lparam) };
113    }
114    else {
115        result = unsafe { EnumWindows(Some(callback_enum_windows::<T>), lparam) };
116    }
117
118    if result == 0 {
119        return Err(utils::get_last_error());
120    }
121
122    Ok(())
123}
124
125///Enumerates over windows handles and calls callback on each
126///
127///# Note
128/// Enumeration continues until callback return non-zero value.
129///
130///# WinAPI error
131///
132///Due to `enum_by_until` allowing to interrupt enumeration, having error set
133///cause inability detect accurately whether enumeration failed or has been interrupted.
134///Hence this function always set last error to `0`.
135///
136///# Parameters
137///
138///* ```parent``` - Handle of parent window to look up through its children only. Optional.
139///* ```cmp_func``` - Callback that will be called on each window.
140///
141///# Return
142///
143///* ```Ok``` - Success.
144///* ```Err``` - Error reason.
145pub fn enum_by_until<T: FnMut(HWND) -> i32>(parent: Option<HWND>, mut cmp_func: T) -> Result<()> {
146    let lparam = &mut cmp_func as *mut _ as LPARAM;
147
148    let result: i32;
149
150    //Necessary if we want to guarantee that we can correctly detect interrupt of enumeration.
151    unsafe { SetLastErrorEx(0, 0) };
152    if let Some(parent_window) = parent {
153        result = unsafe { EnumChildWindows(parent_window, Some(callback_enum_windows_until::<T>), lparam) };
154    }
155    else {
156        result = unsafe { EnumWindows(Some(callback_enum_windows_until::<T>), lparam) };
157    }
158
159    //If cmp_func returns 0 then EnumWindows too.
160    //But it is not an error case.
161    if result == 0 {
162        let error = utils::get_last_error();
163
164        if error.raw_code() != 0 {
165            return Err(utils::get_last_error());
166        }
167    }
168
169    Ok(())
170}
171
172///Retrieves handle to a window by pid using `enum_by_until`.
173///
174///# Parameters
175///
176///* ```pid``` - Pid of the process
177///
178///# Return
179///
180///* ```Ok``` - Success.
181///* ```Err``` - Error reason.
182pub fn get_by_pid(pid: u32) -> Result<Option<HWND>> {
183    let mut found_window: Option<HWND> = None;
184
185    let res = enum_by_until(None,
186                            |handle: HWND| {
187                                let (process_pid, _) = get_thread_process_id(handle);
188                                if process_pid == pid {
189                                    found_window = Some(handle);
190                                    return 0;
191                                }
192                                1
193                            });
194
195    if let Err(error) = res {
196        Err(error)
197    }
198    else {
199        Ok(found_window)
200    }
201
202}
203
204///Retrieves list of handles to specific window class
205///
206///# Parameters
207///
208///* ```class_name``` - a name of class for which handle to be looked up.
209///* ```parent``` - handle of parent window to look up through its children only. optional.
210///
211///# Return
212///
213///* ```ok``` - vector of handles.
214///* ```err``` - error reason.
215pub fn get_by_class(class_name: &str, parent: Option<HWND>) -> Result<Vec<HWND>> {
216    let mut found_windows: Vec<HWND> = vec![];
217
218    let res = enum_by(parent,
219                      |handle: HWND| {
220                          if let Ok(window_class) = get_class(handle) {
221                              if window_class == class_name {
222                                  found_windows.push(handle);
223                              }
224                          }
225                      });
226
227    if let Err(error) = res {
228        Err(error)
229    }
230    else {
231        Ok(found_windows)
232    }
233}
234
235///Retrieves list of handles to windows with specific title's text.
236///
237///# Parameters
238///
239///* ```name``` - Window's title text.
240///* ```parent``` - Handle of parent window to look up through its children only. Optional.
241///
242///# Return
243///
244///* ```Ok``` - Vector of handles.
245///* ```Err``` - Error reason.
246pub fn get_by_title(name: &str, parent: Option<HWND>) -> Result<Vec<HWND>> {
247    let mut found_windows: Vec<HWND> = vec![];
248
249    let res = enum_by(parent,
250                      |handle: HWND| {
251                          if let Ok(window_title) = get_text(handle) {
252                              if window_title == name {
253                                  found_windows.push(handle);
254                              }
255                          }
256                      });
257
258    if let Err(error) = res {
259        Err(error)
260    }
261    else {
262        Ok(found_windows)
263    }
264}
265
266///Retrieves the identifier of the thread and process that created the specified window.
267///
268///# Parameters
269///
270///* ```window``` - Handle to a window.
271///
272///# Return(tuple)
273///
274///1. Process pid
275///2. Thread id.
276pub fn get_thread_process_id(window: HWND) -> (u32, u32) {
277    let mut process_pid: u32 = 0;
278    let thread_pid = unsafe {GetWindowThreadProcessId(window, &mut process_pid)};
279
280    (process_pid, thread_pid)
281}
282
283///Search for a window's handle.
284///
285///# Parameters
286///
287///* ```class_name``` - Name of window's class.
288///* ```window_name``` - Window's title.
289///
290///# Return
291///
292///* ```Ok``` - Handle to window.
293///* ```Err``` - Error reason.
294pub fn find<T: AsRef<ffi::OsStr>>(class_name: T, window_name: Option<T>) -> Result<HWND> {
295    let result: HWND;
296    let mut class_name: Vec<u16> = class_name.as_ref().encode_wide().collect();
297    class_name.push(0);
298    let class_name = class_name.as_ptr();
299
300    if let Some(window_name) = window_name {
301        let mut window_name: Vec<u16> = window_name.as_ref().encode_wide().collect();
302        window_name.push(0);
303        let window_name = window_name.as_ptr();
304
305        result = unsafe {FindWindowW(class_name, window_name)};
306    }
307    else {
308        result = unsafe {FindWindowW(class_name, ptr::null())};
309    }
310
311    if result.is_null() {
312        return Err(utils::get_last_error());
313    }
314
315    Ok(result)
316}
317
318///Search for a window's child.
319///
320///# Parameters
321///
322///* ```class_name``` - Name of window's class.
323///* ```window_name``` - Window's title.
324///* ```parent``` - Handle to a parent window. Default is desktop.
325///* ```child_after``` - Handle to a child window after which to start search.
326///
327///# Return
328///
329///* ```Ok``` - Handle to window.
330///* ```Err``` - Error reason.
331pub fn find_child<T: AsRef<ffi::OsStr>>(class_name: T,
332                                        window_name: Option<T>,
333                                        parent: Option<HWND>,
334                                        child_after: Option<HWND>) -> Result<HWND> {
335    let result: HWND;
336    let mut class_name: Vec<u16> = class_name.as_ref().encode_wide().collect();
337    class_name.push(0);
338    let class_name = class_name.as_ptr();
339
340    let parent = parent.unwrap_or(0x0 as HWND);
341    let child_after = child_after.unwrap_or(0x0 as HWND);
342
343    if let Some(window_name) = window_name {
344        let mut window_name: Vec<u16> = window_name.as_ref().encode_wide().collect();
345        window_name.push(0);
346        let window_name = window_name.as_ptr();
347
348        result = unsafe {FindWindowExW(parent, child_after, class_name, window_name)};
349    }
350    else {
351        result = unsafe {FindWindowExW(parent, child_after, class_name, ptr::null())};
352    }
353
354    if result.is_null() {
355        return Err(utils::get_last_error());
356    }
357
358    Ok(result)
359}
360
361///Sends message to a window.
362///
363///# Note
364///All messages that this function sends are blocking.
365///
366///You can specify timeout for how long to block.
367///
368///# Parameters
369///
370///* ```window``` - Handle to the window for which to send.
371///* ```msg_type``` - Type of message. See WinAPI docs.
372///* ```wParam``` - Additional message specific parameter.
373///* ```lParam``` - Additional message specific parameter.
374///* ```timeout``` - Optional timeout in milliseconds.
375///
376///# Return
377///
378///* ```Ok``` - Message has been sent  successfully.
379///* ```Err``` - Error reason. Relevant only to message with timeout.
380pub fn send_message(window: HWND,
381                    msg_type: UINT,
382                    w_param: WPARAM,
383                    l_param: LPARAM,
384                    timeout: Option<UINT>) -> Result<LRESULT> {
385    if let Some(timeout) = timeout {
386        unsafe {
387            let mut result: ULONG_PTR = 0;
388            let result_ptr = &mut result as PDWORD_PTR;
389            if SendMessageTimeoutW(window, msg_type, w_param, l_param, SMTO_BLOCK, timeout, result_ptr) == 0 {
390                return Err(utils::get_last_error());
391            }
392            Ok(result as LRESULT)
393        }
394    }
395    else {
396        unsafe {
397            Ok(SendMessageW(window, msg_type, w_param, l_param))
398        }
399    }
400}
401
402///Button click message type
403const BM_CLICK: c_uint = 0x00F5;
404
405///Sends push button message to a window.
406///
407///# Parameters
408///
409///* ```window``` - Handle to the window for which to send.
410///* ```timeout``` - Optional timeout in milliseconds.
411pub fn send_push_button(window: HWND, timeout: Option<UINT>) -> Result<LRESULT> {
412    send_message(window, BM_CLICK, 0, 0, timeout)
413}
414
415///Sends set text message to a window.
416///
417///# Parameters
418///
419///* ```window``` - Handle to the window for which to send.
420///* ```text``` - Text with which to update window.
421///
422///# Return
423///
424///* ```true``` - On success.
425///* ```false``` - Otherwise.
426pub fn send_set_text<T: AsRef<ffi::OsStr>>(window: HWND, text: T) -> bool {
427    let mut text: Vec<u16> = text.as_ref().encode_wide().collect();
428    text.push(0);
429    let text = text.as_ptr() as LPARAM;
430
431    let result = send_message(window, WM_SETTEXT, 0, text, None);
432    result.is_ok() && result.unwrap() != 0
433}
434
435///Sends get text message to a window
436///
437///# Parameters
438///
439///* ```window``` - Handle to the window for which to send.
440///
441///# Return
442///
443///* ```String``` - Window's text.
444///* ```None``` - If there is no text.
445pub fn send_get_text(window: HWND) -> Option<String> {
446    //Does not include null char
447    let buf_len = send_message(window, WM_GETTEXTLENGTH, 0, 0, None).unwrap();
448
449    if buf_len == 0 {
450        return None
451    }
452
453    let buf_len = buf_len + 1;
454
455    let text: Vec<u16> = vec![0; buf_len as usize];
456    let text_ptr = text.as_ptr() as LPARAM;
457    //Does not include null char
458    let buf_len = send_message(window, WM_GETTEXT, buf_len as WPARAM, text_ptr, None).unwrap() as usize;
459
460    Some(String::from_utf16_lossy(&text[..buf_len]))
461}
462
463///Sends sys command to a window.
464///
465///Refer to https://msdn.microsoft.com/en-us/library/windows/desktop/ms646360%28v=vs.85%29.aspx
466///
467///# Parameters
468///
469///* ```window``` - Handle to the window for which to send.
470///* ```cmd_type``` - Type of sys command.
471///* ```l_param``` - Mouse & screen coordinates.
472///
473///# Return
474///
475///* ```true``` - On success.
476///* ```false``` - Otherwise.
477pub fn send_sys_command(window: HWND, cmd_type: WPARAM, l_param: LPARAM) -> bool {
478    let result = send_message(window, WM_SYSCOMMAND, cmd_type, l_param, None);
479    //Return is zero if Application proceed message.
480    result.is_ok() && result.unwrap() == 0
481}
482
483#[inline]
484///Retrieves the window handle to the active window attached to the calling thread's message queue.
485pub fn get_active() -> HWND {
486    unsafe {
487        GetActiveWindow()
488    }
489}
490
491#[inline]
492///Retrieves the window handle used by the console associated with the calling process.
493pub fn get_console() -> HWND {
494    unsafe {
495        GetConsoleWindow()
496    }
497}
498
499///A window builder.
500///
501///To successfully create window at least class name should be specified.
502///You can use pre-defined [classes](https://msdn.microsoft.com/en-us/library/windows/desktop/ms633574(v=vs.85).aspx#system) for simple message only window.
503pub struct Builder {
504    ex_style: DWORD,
505    class_name: Option<Vec<u16>>,
506    window_name: Option<Vec<u16>>,
507    style: DWORD,
508    position: (c_int, c_int),
509    width: c_int,
510    height: c_int,
511    parent: HWND,
512    menu: HMENU,
513    inst: HINSTANCE,
514    param: Option<CREATESTRUCTW>
515}
516
517impl Builder {
518    ///Initialize builder.
519    pub fn new() -> Builder {
520        Builder {
521            ex_style: 0,
522            class_name: None,
523            window_name: None,
524            style: 0,
525            position: (CW_USEDEFAULT, CW_USEDEFAULT),
526            width: CW_USEDEFAULT,
527            height: CW_USEDEFAULT,
528            parent: ptr::null_mut(),
529            menu: ptr::null_mut(),
530            inst: ptr::null_mut(),
531            param: None
532        }
533    }
534
535    ///Sets style.
536    ///
537    ///See possible [values](https://msdn.microsoft.com/en-us/library/ms632600(v=vs.85).aspx)
538    pub fn style(&mut self, value: DWORD) -> &mut Builder {
539        self.style = value;
540        self
541    }
542
543    ///Sets extended style.
544    ///
545    ///See possible [values](https://msdn.microsoft.com/en-us/library/ff700543(v=vs.85).aspx)
546    pub fn ex_style(&mut self, value: DWORD) -> &mut Builder {
547        self.ex_style = value;
548        self
549    }
550
551    ///Sets class name.
552    pub fn class_name<T: AsRef<ffi::OsStr>>(&mut self, value: T) -> &mut Builder {
553        let mut class_name: Vec<u16> = value.as_ref().encode_wide().collect();
554        class_name.push(0);
555        self.class_name = Some(class_name);
556        self
557    }
558
559    ///Sets class name.
560    pub fn window_name<T: AsRef<ffi::OsStr>>(&mut self, value: T) -> &mut Builder {
561        let mut window_name: Vec<u16> = value.as_ref().encode_wide().collect();
562        window_name.push(0);
563        self.window_name = Some(window_name);
564        self
565    }
566
567    ///Sets position. Default is `(CW_USEDEFAULT, CW_USEDEFAULT`.
568    pub fn position(&mut self, x: c_int, y: c_int) -> &mut Builder {
569        self.position.0 = x;
570        self.position.1 = y;
571        self
572    }
573
574    ///Sets size of window. Default is `CW_USEDEFAULT`.
575    pub fn size(&mut self, width: c_int, height: c_int) -> &mut Builder {
576        self.width = width;
577        self.height = height;
578        self
579    }
580
581    ///Sets parent window. Default is `null`
582    pub fn parent(&mut self, value: HWND) -> &mut Builder {
583        self.parent = value;
584        self
585    }
586
587    ///Sets parent window to message only `HWND_MESSAGE`.
588    pub fn parent_message(&mut self) -> &mut Builder {
589        self.parent = HWND_MESSAGE;
590        self
591    }
592
593    ///Seta module instance associated with window.
594    pub fn instance(&mut self, value: HINSTANCE) -> &mut Builder {
595        self.inst = value;
596        self
597    }
598
599    ///Sets param which will be sent in `WM_CREATE`
600    pub fn param(&mut self, value: &CREATESTRUCTW) -> &mut Builder {
601        self.param = Some(*value);
602        self
603    }
604
605    ///Creates window.
606    pub fn create(&mut self) -> Result<HWND> {
607        let param = self.param.as_mut()
608                              .map(|create_struct| create_struct as *mut CREATESTRUCTW as *mut c_void)
609                              .unwrap_or(ptr::null_mut());
610
611        let result = unsafe { CreateWindowExW(self.ex_style,
612                                              self.class_name.as_mut().map(|val| val.as_ptr()).unwrap_or(ptr::null()),
613                                              self.window_name.as_mut().map(|val| val.as_ptr()).unwrap_or(ptr::null()),
614                                              self.style,
615                                              self.position.0, self.position.1,
616                                              self.width, self.height,
617                                              self.parent,
618                                              self.menu,
619                                              self.inst,
620                                              param) };
621
622        if result.is_null() {
623            Err(utils::get_last_error())
624        }
625        else {
626            Ok(result)
627        }
628    }
629}
630
631///Shows window
632///
633///[See](https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx) possible commands
634///
635///If the window was previously visible, the return value is `true`.
636///If the window was previously hidden, the return value is `false`.
637pub fn show(window: HWND, cmd: c_int) -> bool {
638    unsafe {
639        ShowWindow(window, cmd) != 0
640    }
641}
642
643///Destroy window.
644///
645///`WM_DESTROY` and `WM_NCDESTROY` are sent after.
646#[inline]
647pub fn destroy(window: HWND) -> bool {
648    unsafe {
649        DestroyWindow(window) != 0
650    }
651}