win_wrap/uia/
element.rs

1/*
2 * Copyright (c) 2024. The RigelA open source project team and
3 * its contributors reserve all rights.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and limitations under the License.
12 */
13
14use std::{
15    fmt::{Debug, Display, Formatter},
16    sync::Weak,
17};
18
19use windows::{
20    core::BSTR,
21    Win32::{
22        Foundation::{HWND, RECT},
23        UI::Accessibility::{
24            IUIAutomation6, IUIAutomationElement, IUIAutomationElementArray, TreeScope_Children,
25            UIA_AppBarControlTypeId, UIA_ButtonControlTypeId, UIA_CalendarControlTypeId,
26            UIA_CheckBoxControlTypeId, UIA_ComboBoxControlTypeId, UIA_CustomControlTypeId,
27            UIA_DataGridControlTypeId, UIA_DataItemControlTypeId, UIA_DocumentControlTypeId,
28            UIA_EditControlTypeId, UIA_GroupControlTypeId, UIA_HeaderControlTypeId,
29            UIA_HeaderItemControlTypeId, UIA_HyperlinkControlTypeId, UIA_ImageControlTypeId,
30            UIA_ListControlTypeId, UIA_ListItemControlTypeId, UIA_MenuBarControlTypeId,
31            UIA_MenuControlTypeId, UIA_MenuItemControlTypeId, UIA_PaneControlTypeId,
32            UIA_ProgressBarControlTypeId, UIA_RadioButtonControlTypeId, UIA_ScrollBarControlTypeId,
33            UIA_SemanticZoomControlTypeId, UIA_SeparatorControlTypeId, UIA_SliderControlTypeId,
34            UIA_SpinnerControlTypeId, UIA_SplitButtonControlTypeId, UIA_StatusBarControlTypeId,
35            UIA_TabControlTypeId, UIA_TabItemControlTypeId, UIA_TableControlTypeId,
36            UIA_TextControlTypeId, UIA_ThumbControlTypeId, UIA_TitleBarControlTypeId,
37            UIA_ToolBarControlTypeId, UIA_ToolTipControlTypeId, UIA_TreeControlTypeId,
38            UIA_TreeItemControlTypeId, UIA_WindowControlTypeId, UIA_CONTROLTYPE_ID,
39        },
40    },
41};
42
43use crate::{common::Result, ext::VecExt};
44
45/// UiAutomationElement 的本地封装
46#[derive(Clone)]
47pub struct UiAutomationElement {
48    _automation: Weak<IUIAutomation6>,
49    _current: IUIAutomationElement,
50}
51
52impl UiAutomationElement {
53    /**
54    获取原始的元素引用(不对外暴露)。
55    */
56    pub(crate) fn get_raw(&self) -> &IUIAutomationElement {
57        &self._current
58    }
59    pub fn get_aut(&self) -> Weak<IUIAutomation6> {
60        self._automation.clone()
61    }
62
63    pub(crate) fn get_aut_ref(&self) -> &IUIAutomation6 {
64        unsafe { &*self._automation.as_ptr() }
65    }
66
67    pub(crate) fn obtain(automation: Weak<IUIAutomation6>, element: IUIAutomationElement) -> Self {
68        Self {
69            _automation: automation,
70            _current: element,
71        }
72    }
73
74    //noinspection SpellCheckingInspection
75    /**
76    获取元素的当前名称。
77    */
78    pub fn get_name(&self) -> String {
79        unsafe { self._current.CurrentName() }
80            // 不需要手动释放BSTR类型的指针,windows-rs已经对BSTR类型实现drop特征
81            .unwrap_or(BSTR::new())
82            .to_string()
83    }
84
85    /**
86    获取本土化的控件类型描述。
87    */
88    pub fn get_localized_control_type(&self) -> String {
89        unsafe { self._current.CurrentLocalizedControlType() }
90            .unwrap_or(BSTR::new())
91            .to_string()
92    }
93
94    /**
95    获取窗口句柄。
96    */
97    pub fn native_window_handle(&self) -> HWND {
98        unsafe { self._current.CurrentNativeWindowHandle() }.unwrap_or(HWND::default())
99    }
100
101    /**
102    获取父对象。
103    */
104    pub fn get_parent(&self) -> Result<UiAutomationElement> {
105        match unsafe { self._current.GetCachedParent() } {
106            Ok(p) => Ok(UiAutomationElement::obtain(self._automation.clone(), p)),
107            Err(e) => Err(e),
108        }
109    }
110
111    /**
112    获取控件类型。
113    */
114    pub fn get_control_type(&self) -> ControlType {
115        unsafe {
116            match self._current.CurrentControlType() {
117                Ok(x) => ControlType::from(x),
118                Err(_) => ControlType::Custom,
119            }
120        }
121    }
122
123    /**
124    获取元素支持的模式列表(ids,names)。
125    */
126    pub fn get_supported_patterns(&self) -> Result<(Vec<i32>, Vec<String>)> {
127        unsafe {
128            let (mut ids, mut names) = std::mem::zeroed();
129            if let Err(e) = self.get_aut_ref().PollForPotentialSupportedPatterns(
130                &self._current,
131                &mut ids,
132                &mut names,
133            ) {
134                return Err(e);
135            }
136            let names: Vec<BSTR> = names.to_vec();
137            Ok((ids.to_vec(), names.iter().map(|i| i.to_string()).collect()))
138        }
139    }
140
141    /**
142    获取提供程序描述。
143    */
144    pub fn get_provider_description(&self) -> String {
145        unsafe { self._current.CurrentProviderDescription() }
146            .unwrap_or(BSTR::new())
147            .to_string()
148    }
149
150    /**
151    获取子元素数量。
152    */
153    pub fn get_child_count(&self) -> i32 {
154        if let Ok(children) = unsafe {
155            self._current.FindAll(
156                TreeScope_Children,
157                &self.get_aut_ref().CreateTrueCondition().unwrap(),
158            )
159        } {
160            return unsafe { children.Length() }.unwrap();
161        }
162        0
163    }
164
165    /**
166    获取子元素。
167    `index` 序号。
168    */
169    pub fn get_child(&self, index: i32) -> Option<UiAutomationElement> {
170        if let Ok(children) = unsafe {
171            self._current.FindAll(
172                TreeScope_Children,
173                &self.get_aut_ref().CreateTrueCondition().unwrap(),
174            )
175        } {
176            if let Ok(el) = unsafe { children.GetElement(index) } {
177                return Some(UiAutomationElement::obtain(self._automation.clone(), el));
178            }
179        }
180        None
181    }
182
183    /**
184    获取元素的矩形框
185    */
186    pub fn get_bounding_rectangle(&self) -> RECT {
187        unsafe { self._current.CurrentBoundingRectangle() }
188            .expect("Can't get the location of element.")
189    }
190
191    /**
192    获取元素的当前自动化ID。
193    */
194    pub fn get_automation_id(&self) -> String {
195        unsafe { self._current.CurrentAutomationId() }
196            .unwrap_or(BSTR::new())
197            .to_string()
198    }
199
200    /**
201    获取元素的当前类名。
202    */
203    #[allow(dead_code)]
204    pub fn get_class_name(&self) -> String {
205        unsafe { self._current.CurrentClassName() }
206            .unwrap_or(BSTR::new())
207            .to_string()
208    }
209
210    /**
211    获取项目状态。
212    */
213    pub fn get_item_status(&self) -> String {
214        unsafe { self._current.CurrentItemStatus() }
215            .unwrap_or(BSTR::new())
216            .to_string()
217    }
218
219    /**
220    获取加速键。
221    */
222    pub fn get_accelerator_key(&self) -> String {
223        unsafe { self._current.CurrentAcceleratorKey() }
224            .unwrap_or(BSTR::new())
225            .to_string()
226    }
227
228    /**
229    获取访问键。
230    */
231    pub fn get_access_key(&self) -> String {
232        unsafe { self._current.CurrentAccessKey() }
233            .unwrap_or(BSTR::new())
234            .to_string()
235    }
236}
237
238unsafe impl Send for UiAutomationElement {}
239
240unsafe impl Sync for UiAutomationElement {}
241
242impl Debug for UiAutomationElement {
243    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
244        Display::fmt(self, f)
245    }
246}
247
248impl Display for UiAutomationElement {
249    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
250        write!(f, "UiAutomationElement(name:{})", self.get_name())
251    }
252}
253
254impl VecExt<IUIAutomationElement> for &IUIAutomationElementArray {
255    fn to_vec(self) -> Vec<IUIAutomationElement> {
256        unsafe {
257            let mut v = vec![];
258            for i in 0..self.Length().unwrap() {
259                v.push(self.GetElement(i).unwrap());
260            }
261            v
262        }
263    }
264}
265
266#[derive(PartialEq)]
267pub enum ControlType {
268    AppBar,
269    Button,
270    Calendar,
271    CheckBox,
272    ComboBox,
273    Custom,
274    DataGrid,
275    DataItem,
276    Document,
277    Edit,
278    Group,
279    Header,
280    HeaderItem,
281    Hyperlink,
282    Image,
283    List,
284    ListItem,
285    MenuBar,
286    Menu,
287    MenuItem,
288    Pane,
289    ProgressBar,
290    RadioButton,
291    ScrollBar,
292    SemanticZoom,
293    Separator,
294    Slider,
295    Spinner,
296    SplitButton,
297    StatusBar,
298    Tab,
299    TabItem,
300    Table,
301    Text,
302    Thumb,
303    TitleBar,
304    ToolBar,
305    ToolTip,
306    Tree,
307    TreeItem,
308    Window,
309}
310
311impl From<UIA_CONTROLTYPE_ID> for ControlType {
312    #[allow(non_upper_case_globals)]
313    fn from(value: UIA_CONTROLTYPE_ID) -> Self {
314        match value {
315            UIA_AppBarControlTypeId => Self::AppBar,
316            UIA_ButtonControlTypeId => Self::Button,
317            UIA_CalendarControlTypeId => Self::Calendar,
318            UIA_CheckBoxControlTypeId => Self::CheckBox,
319            UIA_ComboBoxControlTypeId => Self::ComboBox,
320            UIA_CustomControlTypeId => Self::Custom,
321            UIA_DataGridControlTypeId => Self::DataGrid,
322            UIA_DataItemControlTypeId => Self::DataItem,
323            UIA_DocumentControlTypeId => Self::Document,
324            UIA_EditControlTypeId => Self::Edit,
325            UIA_GroupControlTypeId => Self::Group,
326            UIA_HeaderControlTypeId => Self::Header,
327            UIA_HeaderItemControlTypeId => Self::HeaderItem,
328            UIA_HyperlinkControlTypeId => Self::Hyperlink,
329            UIA_ImageControlTypeId => Self::Image,
330            UIA_ListControlTypeId => Self::List,
331            UIA_ListItemControlTypeId => Self::ListItem,
332            UIA_MenuBarControlTypeId => Self::MenuBar,
333            UIA_MenuControlTypeId => Self::Menu,
334            UIA_MenuItemControlTypeId => Self::MenuItem,
335            UIA_PaneControlTypeId => Self::Pane,
336            UIA_ProgressBarControlTypeId => Self::ProgressBar,
337            UIA_RadioButtonControlTypeId => Self::RadioButton,
338            UIA_ScrollBarControlTypeId => Self::ScrollBar,
339            UIA_SemanticZoomControlTypeId => Self::SemanticZoom,
340            UIA_SeparatorControlTypeId => Self::Separator,
341            UIA_SliderControlTypeId => Self::Slider,
342            UIA_SpinnerControlTypeId => Self::Spinner,
343            UIA_SplitButtonControlTypeId => Self::SplitButton,
344            UIA_StatusBarControlTypeId => Self::StatusBar,
345            UIA_TabControlTypeId => Self::Tab,
346            UIA_TabItemControlTypeId => Self::TabItem,
347            UIA_TableControlTypeId => Self::Table,
348            UIA_TextControlTypeId => Self::Text,
349            UIA_ThumbControlTypeId => Self::Thumb,
350            UIA_TitleBarControlTypeId => Self::TitleBar,
351            UIA_ToolBarControlTypeId => Self::ToolBar,
352            UIA_ToolTipControlTypeId => Self::ToolTip,
353            UIA_TreeControlTypeId => Self::Tree,
354            UIA_TreeItemControlTypeId => Self::TreeItem,
355            UIA_WindowControlTypeId => Self::Window,
356            _ => Self::Custom,
357        }
358    }
359}