win_wrap/msaa/
object.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 crate::common::{Result, HWND};
15use std::fmt::{Debug, Display, Formatter};
16pub use windows::Win32::UI::{
17    Accessibility::{
18        ROLE_SYSTEM_ALERT, ROLE_SYSTEM_ANIMATION, ROLE_SYSTEM_APPLICATION, ROLE_SYSTEM_BORDER,
19        ROLE_SYSTEM_BUTTONDROPDOWN, ROLE_SYSTEM_BUTTONDROPDOWNGRID, ROLE_SYSTEM_BUTTONMENU,
20        ROLE_SYSTEM_CARET, ROLE_SYSTEM_CELL, ROLE_SYSTEM_CHARACTER, ROLE_SYSTEM_CHART,
21        ROLE_SYSTEM_CHECKBUTTON, ROLE_SYSTEM_CLIENT, ROLE_SYSTEM_CLOCK, ROLE_SYSTEM_COLUMN,
22        ROLE_SYSTEM_COLUMNHEADER, ROLE_SYSTEM_COMBOBOX, ROLE_SYSTEM_CURSOR, ROLE_SYSTEM_DIAGRAM,
23        ROLE_SYSTEM_DIAL, ROLE_SYSTEM_DIALOG, ROLE_SYSTEM_DOCUMENT, ROLE_SYSTEM_DROPLIST,
24        ROLE_SYSTEM_EQUATION, ROLE_SYSTEM_GRAPHIC, ROLE_SYSTEM_GRIP, ROLE_SYSTEM_GROUPING,
25        ROLE_SYSTEM_HELPBALLOON, ROLE_SYSTEM_HOTKEYFIELD, ROLE_SYSTEM_INDICATOR,
26        ROLE_SYSTEM_IPADDRESS, ROLE_SYSTEM_LINK, ROLE_SYSTEM_LIST, ROLE_SYSTEM_LISTITEM,
27        ROLE_SYSTEM_MENUBAR, ROLE_SYSTEM_MENUITEM, ROLE_SYSTEM_MENUPOPUP, ROLE_SYSTEM_OUTLINE,
28        ROLE_SYSTEM_OUTLINEBUTTON, ROLE_SYSTEM_OUTLINEITEM, ROLE_SYSTEM_PAGETAB,
29        ROLE_SYSTEM_PAGETABLIST, ROLE_SYSTEM_PANE, ROLE_SYSTEM_PROGRESSBAR,
30        ROLE_SYSTEM_PROPERTYPAGE, ROLE_SYSTEM_PUSHBUTTON, ROLE_SYSTEM_RADIOBUTTON, ROLE_SYSTEM_ROW,
31        ROLE_SYSTEM_ROWHEADER, ROLE_SYSTEM_SCROLLBAR, ROLE_SYSTEM_SEPARATOR, ROLE_SYSTEM_SLIDER,
32        ROLE_SYSTEM_SOUND, ROLE_SYSTEM_SPINBUTTON, ROLE_SYSTEM_SPLITBUTTON, ROLE_SYSTEM_STATICTEXT,
33        ROLE_SYSTEM_STATUSBAR, ROLE_SYSTEM_TABLE, ROLE_SYSTEM_TEXT, ROLE_SYSTEM_TITLEBAR,
34        ROLE_SYSTEM_TOOLBAR, ROLE_SYSTEM_TOOLTIP, ROLE_SYSTEM_WHITESPACE, ROLE_SYSTEM_WINDOW,
35        STATE_SYSTEM_HASPOPUP, STATE_SYSTEM_NORMAL,
36    },
37    WindowsAndMessaging::{
38        OBJID_ALERT, OBJID_CARET, OBJID_CLIENT, OBJID_CURSOR, OBJID_HSCROLL, OBJID_MENU,
39        OBJID_NATIVEOM, OBJID_QUERYCLASSNAMEIDX, OBJID_SIZEGRIP, OBJID_SOUND, OBJID_SYSMENU,
40        OBJID_TITLEBAR, OBJID_VSCROLL, OBJID_WINDOW,
41    },
42};
43
44use windows::{
45    core::{Error, Interface, Type, BSTR},
46    Win32::{
47        Foundation::{POINT, S_FALSE},
48        System::{Com::IDispatch, Variant::VARIANT},
49        UI::Accessibility::{
50            AccessibleChildren, AccessibleObjectFromEvent, AccessibleObjectFromPoint,
51            AccessibleObjectFromWindow, GetRoleTextW, GetStateTextW, IAccessible,
52            WindowFromAccessibleObject,
53        },
54    },
55};
56
57#[derive(Clone)]
58pub struct AccessibleObject(IAccessible, i32);
59
60impl AccessibleObject {
61    pub(crate) fn from_raw(acc: IAccessible, child: i32) -> Self {
62        Self(acc, child)
63    }
64    pub fn get_raw(&self) -> &IAccessible {
65        &self.0
66    }
67    pub fn get_child_id(&self) -> i32 {
68        self.1
69    }
70
71    /**
72    从窗口获取对象。
73    `h_wnd` 窗口句柄。
74    */
75    pub fn from_window(h_wnd: HWND) -> Result<Self> {
76        // https://learn.microsoft.com/zh-cn/windows/win32/api/oleacc/nf-oleacc-accessibleobjectfromwindow
77        let acc = unsafe {
78            let mut p_acc = std::mem::zeroed();
79            if let Err(e) = AccessibleObjectFromWindow(
80                h_wnd,
81                OBJID_WINDOW.0 as u32,
82                &IAccessible::IID,
83                &mut p_acc,
84            ) {
85                return Err(e);
86            }
87            match IAccessible::from_abi(p_acc) {
88                Err(e) => return Err(e),
89                Ok(r) => r,
90            }
91        };
92        Ok(Self(acc, 0))
93    }
94
95    /**
96    从插入点获取对象。
97    */
98    pub fn from_caret() -> Result<Self> {
99        // https://learn.microsoft.com/zh-cn/windows/win32/api/oleacc/nf-oleacc-accessibleobjectfromwindow
100        let acc = unsafe {
101            let mut p_acc = std::mem::zeroed();
102            if let Err(e) = AccessibleObjectFromWindow(
103                Default::default(),
104                OBJID_CARET.0 as u32,
105                &IAccessible::IID,
106                &mut p_acc,
107            ) {
108                return Err(e);
109            }
110            match IAccessible::from_abi(p_acc) {
111                Err(e) => return Err(e),
112                Ok(r) => r,
113            }
114        };
115        Ok(Self(acc, 0))
116    }
117
118    /**
119    从屏幕坐标获取对象。
120    `x` 横坐标。
121    `y` 纵坐标。
122    */
123    pub fn from_point(x: i32, y: i32) -> Result<(Self, i32)> {
124        // https://learn.microsoft.com/zh-cn/previous-versions/ms696163(v=vs.85)
125        let acc = unsafe {
126            let mut p_acc: Option<IAccessible> = None;
127            let point = POINT { x, y };
128            let mut var = Default::default();
129            AccessibleObjectFromPoint(point, &mut p_acc, &mut var)?;
130            match p_acc {
131                None => {
132                    return Err(Error::new(
133                        S_FALSE,
134                        &format!("Can't obtain the accessible object at ({}, {}).", x, y),
135                    ));
136                }
137                Some(r) => (r, i32::try_from(&var).unwrap_or(0)),
138            }
139        };
140        Ok((Self(acc.0, acc.1), acc.1))
141    }
142
143    //noinspection SpellCheckingInspection
144    /**
145    从事件获取对象。
146    `h_wnd` 指定生成事件的窗口的窗口句柄。此值必须是发送到事件挂钩函数的窗口句柄。
147    `id` 指定生成事件的 对象的对象 ID。 此值必须是发送到事件挂钩函数的对象 ID。
148    `child_id` 指定事件是由对象还是由其子元素之一触发。如果对象触发了事件,则 child_id = CHILDID_SELF。如果子元素触发了事件, 则 child_id 是元素的子 ID。此值必须是发送到事件挂钩函数的子 ID。
149    */
150    pub fn from_event(h_wnd: HWND, id: i32, child_id: i32) -> Result<(Self, i32)> {
151        // https://learn.microsoft.com/zh-cn/windows/win32/api/oleacc/nf-oleacc-accessibleobjectfromevent
152        let acc = unsafe {
153            let mut p_acc = std::mem::zeroed();
154            let mut var = std::mem::zeroed();
155            if let Err(e) =
156                AccessibleObjectFromEvent(h_wnd, id as u32, child_id as u32, &mut p_acc, &mut var)
157            {
158                return Err(e);
159            }
160            match p_acc {
161                None => {
162                    return Err(Error::new(
163                        S_FALSE,
164                        &format!(
165                            "Can't obtain the accessible object, the h_wnd is {:?}.",
166                            h_wnd.0
167                        ),
168                    ));
169                }
170                Some(r) => (r, i32::try_from(&var).unwrap_or(0)),
171            }
172        };
173        Ok((Self(acc.0, acc.1), acc.1))
174    }
175
176    /**
177    获取对象名称。
178    `child` 子对象ID,0是对象本身。
179    */
180    pub fn get_name(&self, child: i32) -> String {
181        unsafe { self.0.get_accName(&VARIANT::from(child)) }
182            .unwrap_or(BSTR::new())
183            .to_string()
184    }
185
186    /**
187    获取对象描述。
188    `child` 子对象ID,0是对象本身。
189    */
190    pub fn get_description(&self, child: i32) -> String {
191        unsafe { self.0.get_accDescription(&VARIANT::from(child)) }
192            .unwrap_or(BSTR::new())
193            .to_string()
194    }
195
196    /**
197    获取对象帮助。
198    `child` 子对象ID,0是对象本身。
199    */
200    pub fn get_help(&self, child: i32) -> String {
201        unsafe { self.0.get_accHelp(&VARIANT::from(child)) }
202            .unwrap_or(BSTR::new())
203            .to_string()
204    }
205
206    /**
207    查询与指定对象关联的 WinHelp 文件的完整路径;它还检索该文件中相应主题的标识符。并非所有对象都支持此属性。应用程序很少支持或使用此属性。(已经弃用)
208    `child` 子对象ID,0是对象本身。
209    */
210    pub fn get_help_topic(&self, child: i32) -> (String, i32) {
211        unsafe {
212            let mut help_file = BSTR::new();
213            let id_topic = self
214                .0
215                .get_accHelpTopic(&mut help_file, &VARIANT::from(child))
216                .unwrap_or(0);
217            (help_file.to_string(), id_topic)
218        }
219    }
220
221    /**
222    获取对象快捷键。
223    `child` 子对象ID,0是对象本身。
224    */
225    pub fn get_keyboard_shortcut(&self, child: i32) -> String {
226        unsafe { self.0.get_accKeyboardShortcut(&VARIANT::from(child)) }
227            .unwrap_or(BSTR::new())
228            .to_string()
229    }
230
231    /**
232    获取对象值。
233    `child` 子对象ID,0是对象本身。
234    */
235    pub fn get_value(&self, child: i32) -> String {
236        unsafe { self.0.get_accValue(&VARIANT::from(child)) }
237            .unwrap_or(BSTR::new())
238            .to_string()
239    }
240
241    /**
242    获取对象默认动作。
243    `child` 子对象ID,0是对象本身。
244    */
245    pub fn get_default_action(&self, child: i32) -> String {
246        unsafe { self.0.get_accDefaultAction(&VARIANT::from(child)) }
247            .unwrap_or(BSTR::new())
248            .to_string()
249    }
250
251    /**
252    获取对象角色。
253    `child` 子对象ID,0是对象本身。
254    */
255    pub fn get_role(&self, child: i32) -> u32 {
256        unsafe {
257            if let Ok(v) = self.0.get_accRole(&VARIANT::from(child)) {
258                return u32::try_from(&v).unwrap_or(0);
259            } else {
260                0
261            }
262        }
263    }
264
265    /**
266    查询描述指定角色值的对象角色的本地化字符串。
267    `child` 子对象ID,0是对象本身。
268    */
269    pub fn get_role_text(&self, child: i32) -> String {
270        let role = self.get_role(child);
271        let mut text: [u16; 32] = [0; 32];
272        let len = unsafe { GetRoleTextW(role, Some(&mut text)) };
273        String::from_utf16_lossy(&text[..len as usize])
274    }
275
276    /**
277    查询描述单个预定义状态位标志的对象状态的本地化字符串。 由于状态值是一个或多个位标志的组合,因此客户端多次调用此函数以检索所有状态字符串。
278    `child` 子对象ID,0是对象本身。
279    */
280    pub fn get_state_text(&self, child: i32) -> String {
281        let state = self.get_state(child);
282        let mut text: [u16; 32] = [0; 32];
283        let len = unsafe { GetStateTextW(state, Some(&mut text)) };
284        String::from_utf16_lossy(&text[..len as usize])
285    }
286
287    /**
288    获取对象状态。
289    `child` 子对象ID,0是对象本身。
290    */
291    pub fn get_state(&self, child: i32) -> u32 {
292        unsafe {
293            if let Ok(v) = self.0.get_accState(&VARIANT::from(child)) {
294                return u32::try_from(&v).unwrap_or(0);
295            } else {
296                0
297            }
298        }
299    }
300
301    /**
302    执行默认动作。
303    `child` 子对象ID,0是对象本身。
304    */
305    pub fn do_default_action(&self, child: i32) {
306        unsafe {
307            self.0
308                .accDoDefaultAction(&VARIANT::from(child))
309                .unwrap_or(());
310        }
311    }
312
313    //noinspection SpellCheckingInspection
314    /**
315    修改所选内容或移动指定对象的键盘焦点。
316    `flags` 指定要执行哪些选择或焦点操作。此参数必须具有 SELFLAG 常量的组合。
317    `child` 子对象ID,0是对象本身。
318    */
319    pub fn select(&self, flags: i32, child: i32) {
320        unsafe {
321            self.0.accSelect(flags, &VARIANT::from(child)).unwrap_or(());
322        }
323    }
324
325    /**
326    遍历到容器中的另一个 UI 元素并查询对象。(已经弃用)
327    `nav_dir` 指定导航方向。此方向按空间顺序(如左或右)或逻辑顺序(例如下一个或上一个)。此值是导航常量之一。
328    `start` 起始子对象ID,0是对象本身。
329    */
330    pub fn navigate(&self, nav_dir: i32, start: i32) -> Option<Self> {
331        unsafe {
332            let Ok(v) = self.0.accNavigate(nav_dir, &VARIANT::from(start)) else {
333                return None;
334            };
335            let Ok(d) = IDispatch::try_from(&v) else {
336                return match i32::try_from(&v) {
337                    Ok(d) => Some(Self::from_raw(self.0.clone(), d)),
338                    Err(_) => None,
339                };
340            };
341            Some(Self::from_raw(d.cast().unwrap(), 0))
342        }
343    }
344
345    /**
346    查询在屏幕上特定点显示的子元素或子对象。
347    `left` 指定命中测试点的屏幕坐标。x 坐标从左到右增加。请注意,使用屏幕坐标时,原点是屏幕的左上角。
348    `top` 指定命中测试点的屏幕坐标。y 坐标从上到下增加。请注意,使用屏幕坐标时,原点是屏幕的左上角。
349    */
350    pub fn hit_test(&self, left: i32, top: i32) -> Option<Self> {
351        unsafe {
352            let Ok(v) = self.0.accHitTest(left, top) else {
353                return None;
354            };
355            if let Ok(d) = IDispatch::try_from(&v) {
356                return Some(Self::from_raw(d.cast().unwrap(), 0));
357            }
358            Some(Self::from_raw(
359                self.0.clone(),
360                i32::try_from(&v).unwrap_or(0),
361            ))
362        }
363    }
364
365    /**
366    获取焦点对象。
367    */
368    pub fn focus(&self) -> Option<Self> {
369        unsafe {
370            let Ok(v) = self.0.accFocus() else {
371                return None;
372            };
373            if let Ok(d) = IDispatch::try_from(&v) {
374                return Some(Self::from_raw(d.cast().unwrap(), 0));
375            }
376            Some(Self::from_raw(
377                self.0.clone(),
378                i32::try_from(&v).unwrap_or(0),
379            ))
380        }
381    }
382
383    /**
384    获取选中对象。
385    */
386    pub fn selection(&self) -> Option<Self> {
387        unsafe {
388            let Ok(v) = self.0.accSelection() else {
389                return None;
390            };
391            if let Ok(d) = IDispatch::try_from(&v) {
392                return Some(Self::from_raw(d.cast().unwrap(), 0));
393            }
394            Some(Self::from_raw(
395                self.0.clone(),
396                i32::try_from(&v).unwrap_or(0),
397            ))
398        }
399    }
400
401    /**
402    获取父对象。
403    */
404    pub fn parent(&self) -> Option<Self> {
405        unsafe {
406            if let Ok(r) = self.0.accParent() {
407                Some(Self::from_raw(r.cast().unwrap(), 0))
408            } else {
409                None
410            }
411        }
412    }
413
414    /**
415    获取子对象数量。
416    */
417    pub fn child_count(&self) -> u32 {
418        unsafe {
419            if let Ok(r) = self.0.accChildCount() {
420                return r as u32;
421            }
422        }
423        0
424    }
425
426    /**
427    获取子对象。
428    `child` 子对象ID,0是对象本身。
429    */
430    pub fn get_child(&self, child: i32) -> Result<Self> {
431        unsafe {
432            match self.0.get_accChild(&VARIANT::from(child)) {
433                Err(e) => Err(e),
434                Ok(o) => Ok(Self::from_raw(o.cast().unwrap(), 0)),
435            }
436        }
437    }
438
439    /**
440    获取所有子对象。
441    */
442    pub fn children(&self, start: u32, count: u32) -> Result<Vec<Self>> {
443        // https://learn.microsoft.com/zh-cn/windows/win32/api/oleacc/nf-oleacc-accessiblechildren
444        unsafe {
445            let mut arr = vec![];
446            for _ in 0..count {
447                arr.push(VARIANT::default());
448            }
449            let mut cnt = std::mem::zeroed();
450            match AccessibleChildren(&self.0, start as i32, &mut arr, &mut cnt) {
451                Err(e) => Err(e),
452                Ok(_) => {
453                    let mut v = vec![];
454                    for i in 0..cnt {
455                        if let Ok(d) = IDispatch::try_from(&arr[i as usize]) {
456                            v.push(Self::from_raw(d.cast().unwrap(), 0));
457                        }
458                        if let Ok(d) = i32::try_from(&arr[i as usize]) {
459                            v.push(Self::from_raw(self.0.clone(), d));
460                        }
461                    }
462                    Ok(v)
463                }
464            }
465        }
466    }
467
468    /**
469    获取对象位置和大小。
470    `child` 子对象ID,0是对象本身。
471    */
472    pub fn location(&self, child: i32) -> (i32, i32, i32, i32) {
473        unsafe {
474            let (mut left, mut top, mut width, mut height) = (0i32, 0i32, 0i32, 0i32);
475            if let Ok(_) = self.0.accLocation(
476                &mut left,
477                &mut top,
478                &mut width,
479                &mut height,
480                &VARIANT::from(child),
481            ) {
482                (left, top, width, height)
483            } else {
484                (0, 0, 0, 0)
485            }
486        }
487    }
488
489    /**
490    不再支持 put_accName 方法。客户端应用程序应使用特定于控件的解决方法,例如SetWindowText 函数。服务器应返回E_NOTIMPL。
491    */
492    #[allow(unused_variables)]
493    pub fn put_name(&self, child: i32, name: String) {
494        unreachable!()
495    }
496
497    /**
498    设置指定对象的值。并非所有对象都有值。
499    `child` 子对象ID,0是对象本身。
500    `value` 包含对象的值的本地化字符串。
501    */
502    pub fn put_value(&self, child: i32, value: String) {
503        unsafe {
504            self.0
505                .put_accValue(&VARIANT::from(child), &BSTR::from(value.as_str()))
506                .unwrap_or(());
507        }
508    }
509
510    /** 获取窗口句柄。 */
511    pub fn window(&self) -> HWND {
512        let mut h_wnd = HWND::default();
513        unsafe {
514            WindowFromAccessibleObject(&self.0, Some(&mut h_wnd)).unwrap_or(());
515        }
516        h_wnd
517    }
518}
519
520impl Debug for AccessibleObject {
521    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
522        Display::fmt(self, f)
523    }
524}
525
526impl Display for AccessibleObject {
527    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
528        write!(
529            f,
530            "AccessibleObject(name:{}, description:{}, role:{})",
531            self.get_name(self.1),
532            self.get_description(self.1),
533            self.get_role_text(self.1)
534        )
535    }
536}
537
538unsafe impl Sync for AccessibleObject {}
539
540unsafe impl Send for AccessibleObject {}