use std::fmt::Debug;
use std::fmt::Display;
use std::thread::sleep;
use std::time::Duration;
use chrono::Local;
use windows::core::Param;
use windows::Win32::System::Com::CLSCTX_ALL;
use windows::Win32::System::Com::COINIT_MULTITHREADED;
use windows::Win32::System::Com::CoCreateInstance;
use windows::Win32::System::Com::CoInitializeEx;
use windows::Win32::UI::Accessibility::CUIAutomation;
use windows::Win32::UI::Accessibility::IUIAutomation;
use windows::Win32::UI::Accessibility::IUIAutomationAndCondition;
use windows::Win32::UI::Accessibility::IUIAutomationBoolCondition;
use windows::Win32::UI::Accessibility::IUIAutomationCacheRequest;
use windows::Win32::UI::Accessibility::IUIAutomationCondition;
use windows::Win32::UI::Accessibility::IUIAutomationElement;
use windows::Win32::UI::Accessibility::IUIAutomationElement3;
use windows::Win32::UI::Accessibility::IUIAutomationElementArray;
use windows::Win32::UI::Accessibility::IUIAutomationNotCondition;
use windows::Win32::UI::Accessibility::IUIAutomationOrCondition;
use windows::Win32::UI::Accessibility::IUIAutomationPropertyCondition;
use windows::Win32::UI::Accessibility::IUIAutomationTreeWalker;
use windows::core::IUnknown;
use windows::core::Interface;
use windows::Win32::UI::Accessibility::UIA_PROPERTY_ID;
use crate::controls::ControlType;
use crate::events::UIEventHandler;
use crate::events::UIEventType;
use crate::events::UIFocusChangedEventHandler;
use crate::events::UIPropertyChangedEventHandler;
use crate::events::UIStructureChangeEventHandler;
use crate::filters::FnFilter;
use crate::inputs::Mouse;
use crate::patterns::UIPatternType;
use crate::types::ElementMode;
use crate::types::OrientationType;
use crate::types::PropertyConditionFlags;
use crate::types::TreeScope;
use crate::types::UIProperty;
use crate::variants::SafeArray;
use super::filters::ClassNameFilter;
use super::filters::MatcherFilter;
use super::filters::ControlTypeFilter;
use super::filters::NameFilter;
use super::errors::ERR_NOTFOUND;
use super::errors::ERR_TIMEOUT;
use super::errors::Error;
use super::errors::Result;
use super::inputs::Keyboard;
use super::patterns::UIPattern;
use super::types::Handle;
use super::types::Rect;
use super::types::Point;
use super::variants::Variant;
#[derive(Debug, Clone)]
pub struct UIAutomation {
automation: IUIAutomation
}
impl UIAutomation {
pub fn new() -> Result<UIAutomation> {
let result = unsafe {
CoInitializeEx(None, COINIT_MULTITHREADED)
};
if result.is_ok() {
UIAutomation::new_direct()
} else {
Err(result.into())
}
}
pub fn new_direct() -> Result<UIAutomation> {
let automation: IUIAutomation = unsafe {
CoCreateInstance(&CUIAutomation, None, CLSCTX_ALL)?
};
Ok(UIAutomation {
automation
})
}
pub fn compare_elements(&self, element1: &UIElement, element2: &UIElement) -> Result<bool> {
let same;
unsafe {
same = self.automation.CompareElements(element1.as_ref(), element2.as_ref())?;
}
Ok(same.as_bool())
}
pub fn element_from_handle(&self, hwnd: Handle) -> Result<UIElement> {
let element = unsafe {
self.automation.ElementFromHandle(hwnd)?
};
Ok(UIElement::from(element))
}
pub fn element_from_handle_build_cache(&self, hwnd: Handle, cache_request: &UICacheRequest) -> Result<UIElement> {
let element = unsafe {
self.automation.ElementFromHandleBuildCache(hwnd, cache_request)?
};
Ok(element.into())
}
pub fn element_from_point(&self, point: Point) -> Result<UIElement> {
let element = unsafe {
self.automation.ElementFromPoint(point.into())?
};
Ok(UIElement::from(element))
}
pub fn element_from_point_build_cache(&self, point: Point, cache_request: &UICacheRequest) -> Result<UIElement> {
let element = unsafe {
self.automation.ElementFromPointBuildCache(point.into(), cache_request)?
};
Ok(element.into())
}
pub fn get_focused_element(&self) -> Result<UIElement> {
let element = unsafe {
self.automation.GetFocusedElement()?
};
Ok(UIElement::from(element))
}
pub fn get_focused_element_build_cache(&self, cache_request: &UICacheRequest) -> Result<UIElement> {
let element = unsafe {
self.automation.GetFocusedElementBuildCache(cache_request)?
};
Ok(element.into())
}
pub fn get_root_element(&self) -> Result<UIElement> {
let element = unsafe {
self.automation.GetRootElement()?
};
Ok(UIElement::from(element))
}
pub fn get_root_element_build_cache(&self, cache_request: &UICacheRequest) -> Result<UIElement> {
let element = unsafe {
self.automation.GetRootElementBuildCache(cache_request)?
};
Ok(element.into())
}
pub fn create_tree_walker(&self) -> Result<UITreeWalker> {
let tree_walker = unsafe {
let condition = self.create_true_condition()?; self.automation.CreateTreeWalker(condition)?
};
Ok(UITreeWalker::from(tree_walker))
}
pub fn filter_tree_walker(&self, condition: UICondition) -> Result<UITreeWalker> {
let tree_walker = unsafe {
self.automation.CreateTreeWalker(condition)?
};
Ok(tree_walker.into())
}
pub fn get_raw_view_walker(&self) -> Result<UITreeWalker> {
let walker = unsafe {
self.automation.RawViewWalker()?
};
Ok(walker.into())
}
pub fn get_control_view_condition(&self) -> Result<UICondition> {
let condition = unsafe {
self.automation.ControlViewCondition()?
};
Ok(condition.into())
}
pub fn get_control_view_walker(&self) -> Result<UITreeWalker> {
let walker = unsafe {
self.automation.ControlViewWalker()?
};
Ok(walker.into())
}
pub fn get_content_view_condition(&self) -> Result<UICondition> {
let condition = unsafe {
self.automation.ContentViewCondition()?
};
Ok(condition.into())
}
pub fn get_content_view_walker(&self) -> Result<UITreeWalker> {
let walker = unsafe {
self.automation.ContentViewWalker()?
};
Ok(walker.into())
}
pub fn create_matcher(&self) -> UIMatcher {
UIMatcher::new(self.clone())
}
pub fn create_true_condition(&self) -> Result<UICondition> {
let condition = unsafe {
self.automation.CreateTrueCondition()?
};
Ok(condition.into())
}
pub fn create_false_condition(&self) -> Result<UICondition> {
let condition = unsafe {
self.automation.CreateFalseCondition()?
};
Ok(condition.into())
}
pub fn create_not_condition(&self, condition: UICondition) -> Result<UICondition> {
let result = unsafe {
self.automation.CreateNotCondition(condition)?
};
Ok(result.into())
}
pub fn create_and_condition(&self, condition1: UICondition, condition2: UICondition) -> Result<UICondition> {
let result = unsafe {
self.automation.CreateAndCondition(condition1, condition2)?
};
Ok(result.into())
}
pub fn create_or_condition(&self, condition1: UICondition, condition2: UICondition) -> Result<UICondition> {
let result = unsafe {
self.automation.CreateOrCondition(condition1, condition2)?
};
Ok(result.into())
}
pub fn create_property_condition(&self, property: UIProperty, value: Variant, flags: Option<PropertyConditionFlags>) -> Result<UICondition> {
let condition = unsafe {
if let Some(flags) = flags {
self.automation.CreatePropertyConditionEx(property.into(), value, flags.into())?
} else {
self.automation.CreatePropertyCondition(property.into(), value)?
}
};
Ok(condition.into())
}
pub fn create_cache_request(&self) -> Result<UICacheRequest> {
let request = unsafe { self.automation.CreateCacheRequest()? };
Ok(request.into())
}
pub fn add_automation_event_handler(&self, event_type: UIEventType, element: &UIElement, scope: TreeScope, cache_request: Option<&UICacheRequest>, handler: &UIEventHandler) -> Result<()> {
let cache_request = cache_request.map(|r| r.as_ref());
unsafe {
self.automation.AddAutomationEventHandler(event_type.into(), element, scope.into(), cache_request, handler)?
};
Ok(())
}
pub fn remove_automation_event_handler(&self, event_type: UIEventType, element: &UIElement, handler: &UIEventHandler) -> Result<()> {
unsafe {
self.automation.RemoveAutomationEventHandler(event_type.into(), element, handler)?
};
Ok(())
}
pub fn add_property_changed_event_handler(&self, element: &UIElement, scope: TreeScope, cache_request: Option<&UICacheRequest>, handler: &UIPropertyChangedEventHandler, properties: &[UIProperty]) -> Result<()> {
let cache_request = cache_request.map(|r| r.as_ref());
let prop_arr: Vec<UIA_PROPERTY_ID> = properties.iter().map(|p| (*p).into()).collect();
unsafe {
self.automation.AddPropertyChangedEventHandlerNativeArray(element, scope.into(), cache_request, handler, &prop_arr)?
};
Ok(())
}
pub fn remove_property_changed_event_handler(&self, element: &UIElement, handler: &UIPropertyChangedEventHandler) -> Result<()> {
unsafe {
self.automation.RemovePropertyChangedEventHandler(element, handler)?
};
Ok(())
}
pub fn add_structure_changed_event_handler(&self, element: &UIElement, scope: TreeScope, cache_request: Option<&UICacheRequest>, handler: &UIStructureChangeEventHandler) -> Result<()> {
let cache_request = cache_request.map(|r| r.as_ref());
unsafe {
self.automation.AddStructureChangedEventHandler(element, scope.into(), cache_request, handler)?
};
Ok(())
}
pub fn remove_structure_changed_event_handler(&self, element: &UIElement, handler: &UIStructureChangeEventHandler) -> Result<()> {
unsafe {
self.automation.RemoveStructureChangedEventHandler(element, handler)?
};
Ok(())
}
pub fn add_focus_changed_event_handler(&self, cache_request: Option<&UICacheRequest>, handler: &UIFocusChangedEventHandler) -> Result<()> {
let cache_request = cache_request.map(|r| r.as_ref());
unsafe {
self.automation.AddFocusChangedEventHandler(cache_request, handler)?
};
Ok(())
}
pub fn remove_focus_changed_event_handler(&self, handler: &UIFocusChangedEventHandler) -> Result<()> {
unsafe {
self.automation.RemoveFocusChangedEventHandler(handler)?
};
Ok(())
}
pub fn remove_all_event_handlers(&self) -> Result<()> {
unsafe {
self.automation.RemoveAllEventHandlers()?
};
Ok(())
}
}
impl From<IUIAutomation> for UIAutomation {
fn from(automation: IUIAutomation) -> Self {
UIAutomation {
automation
}
}
}
impl Into<IUIAutomation> for UIAutomation {
fn into(self) -> IUIAutomation {
self.automation
}
}
impl AsRef<IUIAutomation> for UIAutomation {
fn as_ref(&self) -> &IUIAutomation {
&self.automation
}
}
#[derive(Clone)]
pub struct UIElement {
element: IUIAutomationElement
}
impl UIElement {
pub fn build_updated_cache(&self, cache_request: &UICacheRequest) -> Result<UIElement> {
let element = unsafe {
self.element.BuildUpdatedCache(cache_request)?
};
Ok(element.into())
}
pub fn find_first(&self, scope: TreeScope, condition: &UICondition) -> Result<UIElement> {
let result = unsafe {
self.element.FindFirst(scope.into(), condition.as_ref())?
};
Ok(result.into())
}
pub fn find_first_build_cache(&self, scope: TreeScope, condition: &UICondition, cache_request: &UICacheRequest) -> Result<UIElement> {
let element = unsafe {
self.element.FindFirstBuildCache(scope.into(), condition, cache_request)?
};
Ok(element.into())
}
pub fn find_all(&self, scope: TreeScope, condition: &UICondition) -> Result<Vec<UIElement>> {
let elements = unsafe {
self.element.FindAll(scope.into(), condition.as_ref())?
};
Self::to_elements(elements)
}
pub fn find_all_build_cache(&self, scope: TreeScope, condition: &UICondition, cache_request: &UICacheRequest) -> Result<Vec<UIElement>> {
let elements = unsafe {
self.element.FindAllBuildCache(scope.into(), condition, cache_request)?
};
Self::to_elements(elements)
}
pub fn get_cached_parent(&self) -> Result<UIElement> {
let parent = unsafe {
self.element.GetCachedParent()?
};
Ok(parent.into())
}
pub fn get_cached_children(&self) -> Result<Vec<UIElement>> {
let elements = unsafe {
self.element.GetCachedChildren()?
};
Self::to_elements(elements)
}
pub fn get_runtime_id(&self) -> Result<Vec<i32>> {
let id = unsafe {
self.element.GetRuntimeId()?
};
let arr = SafeArray::new(id, false);
arr.try_into()
}
pub fn get_name(&self) -> Result<String> {
let name = unsafe {
self.element.CurrentName()?
};
Ok(name.to_string())
}
pub fn get_cached_name(&self) -> Result<String> {
let name = unsafe {
self.element.CachedName()?
};
Ok(name.to_string())
}
pub fn get_automation_id(&self) -> Result<String> {
let automation_id = unsafe {
self.element.CurrentAutomationId()?
};
Ok(automation_id.to_string())
}
pub fn get_cached_automation_id(&self) -> Result<String> {
let automation_id = unsafe {
self.element.CachedAutomationId()?
};
Ok(automation_id.to_string())
}
pub fn get_process_id(&self) -> Result<i32> {
let id = unsafe {
self.element.CurrentProcessId()?
};
Ok(id)
}
pub fn get_cached_process_id(&self) -> Result<i32> {
let id = unsafe {
self.element.CachedProcessId()?
};
Ok(id)
}
pub fn get_classname(&self) -> Result<String> {
let classname = unsafe {
self.element.CurrentClassName()?
};
Ok(classname.to_string())
}
pub fn get_cached_classname(&self) -> Result<String> {
let classname = unsafe {
self.element.CachedClassName()?
};
Ok(classname.to_string())
}
pub fn get_control_type(&self) -> Result<ControlType> {
let control_type = unsafe {
self.element.CurrentControlType()?
};
Ok(ControlType::from(control_type))
}
pub fn get_cached_control_type(&self) -> Result<ControlType> {
let control_type = unsafe {
self.element.CachedControlType()?
};
Ok(control_type.into())
}
pub fn get_localized_control_type(&self) -> Result<String> {
let control_type = unsafe {
self.element.CurrentLocalizedControlType()?
};
Ok(control_type.to_string())
}
pub fn get_cached_localized_control_type(&self) -> Result<String> {
let control_type = unsafe {
self.element.CachedLocalizedControlType()?
};
Ok(control_type.to_string())
}
pub fn get_accelerator_key(&self) -> Result<String> {
let accelerator_key = unsafe {
self.element.CurrentAcceleratorKey()?
};
Ok(accelerator_key.to_string())
}
pub fn get_cached_accelerator_key(&self) -> Result<String> {
let accelerator_key = unsafe {
self.element.CachedAcceleratorKey()?
};
Ok(accelerator_key.to_string())
}
pub fn get_access_key(&self) -> Result<String> {
let access_key = unsafe {
self.element.CurrentAccessKey()?
};
Ok(access_key.to_string())
}
pub fn get_cached_access_key(&self) -> Result<String> {
let access_key = unsafe {
self.element.CachedAccessKey()?
};
Ok(access_key.to_string())
}
pub fn has_keyboard_focus(&self) -> Result<bool> {
let has_focus = unsafe {
self.element.CurrentHasKeyboardFocus()?
};
Ok(has_focus.as_bool())
}
pub fn has_cached_keyboard_focus(&self) -> Result<bool> {
let has_focus = unsafe {
self.element.CachedHasKeyboardFocus()?
};
Ok(has_focus.as_bool())
}
pub fn is_keyboard_focusable(&self) -> Result<bool> {
let focusable = unsafe {
self.element.CurrentIsKeyboardFocusable()?
};
Ok(focusable.as_bool())
}
pub fn is_cached_keyboard_focusable(&self) -> Result<bool> {
let focusable = unsafe {
self.element.CachedIsKeyboardFocusable()?
};
Ok(focusable.as_bool())
}
pub fn is_enabled(&self) -> Result<bool> {
let enabled = unsafe {
self.element.CurrentIsEnabled()?
};
Ok(enabled.as_bool())
}
pub fn is_cached_enabled(&self) -> Result<bool> {
let enabled = unsafe {
self.element.CachedIsEnabled()?
};
Ok(enabled.as_bool())
}
pub fn get_help_text(&self) -> Result<String> {
let text = unsafe {
self.element.CurrentHelpText()?
};
Ok(text.to_string())
}
pub fn get_cached_help_text(&self) -> Result<String> {
let text = unsafe {
self.element.CachedHelpText()?
};
Ok(text.to_string())
}
pub fn get_culture(&self) -> Result<i32> {
let culture = unsafe {
self.element.CurrentCulture()?
};
Ok(culture)
}
pub fn get_cached_culture(&self) -> Result<i32> {
let culture = unsafe {
self.element.CachedCulture()?
};
Ok(culture)
}
pub fn is_control_element(&self) -> Result<bool> {
let is_control = unsafe {
self.element.CurrentIsControlElement()?
};
Ok(is_control.as_bool())
}
pub fn is_cached_control_element(&self) -> Result<bool> {
let is_control = unsafe {
self.element.CachedIsControlElement()?
};
Ok(is_control.as_bool())
}
pub fn is_content_element(&self) -> Result<bool> {
let is_content = unsafe {
self.element.CurrentIsContentElement()?
};
Ok(is_content.as_bool())
}
pub fn is_cached_content_element(&self) -> Result<bool> {
let is_content = unsafe {
self.element.CachedIsContentElement()?
};
Ok(is_content.as_bool())
}
pub fn is_password(&self) -> Result<bool> {
let is_password = unsafe {
self.element.CurrentIsPassword()?
};
Ok(is_password.as_bool())
}
pub fn is_cached_password(&self) -> Result<bool> {
let is_password = unsafe {
self.element.CachedIsPassword()?
};
Ok(is_password.as_bool())
}
pub fn get_native_window_handle(&self) -> Result<Handle> {
let handle = unsafe {
self.element.CurrentNativeWindowHandle()?
};
Ok(handle.into())
}
pub fn get_cached_native_window_handle(&self) -> Result<Handle> {
let handle = unsafe {
self.element.CachedNativeWindowHandle()?
};
Ok(handle.into())
}
pub fn get_item_type(&self) -> Result<String> {
let item_type = unsafe {
self.element.CurrentItemType()?
};
Ok(item_type.to_string())
}
pub fn get_cached_item_type(&self) -> Result<String> {
let item_type = unsafe {
self.element.CachedItemType()?
};
Ok(item_type.to_string())
}
pub fn is_offscreen(&self) -> Result<bool> {
let off_screen = unsafe {
self.element.CurrentIsOffscreen()?
};
Ok(off_screen.as_bool())
}
pub fn is_cached_offscreen(&self) -> Result<bool> {
let off_screen = unsafe {
self.element.CachedIsOffscreen()?
};
Ok(off_screen.as_bool())
}
pub fn get_orientation(&self) -> Result<OrientationType> {
let orientation = unsafe {
self.element.CurrentOrientation()?
};
Ok(orientation.into())
}
pub fn get_cached_orientation(&self) -> Result<OrientationType> {
let orientation = unsafe {
self.element.CachedOrientation()?
};
Ok(orientation.into())
}
pub fn get_framework_id(&self) -> Result<String> {
let id = unsafe {
self.element.CurrentFrameworkId()?
};
Ok(id.to_string())
}
pub fn get_cached_framework_id(&self) -> Result<String> {
let id = unsafe {
self.element.CachedFrameworkId()?
};
Ok(id.to_string())
}
pub fn is_required_for_form(&self) -> Result<bool> {
let required = unsafe {
self.element.CurrentIsRequiredForForm()?
};
Ok(required.as_bool())
}
pub fn is_cached_required_for_form(&self) -> Result<bool> {
let required = unsafe {
self.element.CachedIsRequiredForForm()?
};
Ok(required.as_bool())
}
pub fn is_data_valid_for_form(&self) -> Result<bool> {
let valid = unsafe {
self.element.CurrentIsDataValidForForm()?
};
Ok(valid.as_bool())
}
pub fn is_cached_data_valid_for_form(&self) -> Result<bool> {
let valid = unsafe {
self.element.CachedIsDataValidForForm()?
};
Ok(valid.as_bool())
}
pub fn get_item_status(&self) -> Result<String> {
let status = unsafe {
self.element.CurrentItemStatus()?
};
Ok(status.to_string())
}
pub fn get_cached_item_status(&self) -> Result<String> {
let status = unsafe {
self.element.CachedItemStatus()?
};
Ok(status.to_string())
}
pub fn get_bounding_rectangle(&self) -> Result<Rect> {
let rect = unsafe {
self.element.CurrentBoundingRectangle()?
};
Ok(rect.into())
}
pub fn get_cached_bounding_rectangle(&self) -> Result<Rect> {
let rect = unsafe {
self.element.CachedBoundingRectangle()?
};
Ok(rect.into())
}
pub fn get_labeled_by(&self) -> Result<UIElement> {
let labeled_by = unsafe {
self.element.CurrentLabeledBy()?
};
Ok(UIElement::from(labeled_by))
}
pub fn get_cached_labeled_by(&self) -> Result<UIElement> {
let labeled_by = unsafe {
self.element.CachedLabeledBy()?
};
Ok(UIElement::from(labeled_by))
}
pub fn get_controller_for(&self) -> Result<Vec<UIElement>> {
let elements = unsafe {
self.element.CurrentControllerFor()?
};
Self::to_elements(elements)
}
pub fn get_cached_controller_for(&self) -> Result<Vec<UIElement>> {
let elements = unsafe {
self.element.CachedControllerFor()?
};
Self::to_elements(elements)
}
pub fn get_described_by(&self) -> Result<Vec<UIElement>> {
let elements = unsafe {
self.element.CurrentDescribedBy()?
};
Self::to_elements(elements)
}
pub fn get_cached_described_by(&self) -> Result<Vec<UIElement>> {
let elements = unsafe {
self.element.CachedDescribedBy()?
};
Self::to_elements(elements)
}
pub fn get_flows_to(&self) -> Result<Vec<UIElement>> {
let elements = unsafe {
self.element.CurrentFlowsTo()?
};
Self::to_elements(elements)
}
pub fn get_cached_flows_to(&self) -> Result<Vec<UIElement>> {
let elements = unsafe {
self.element.CachedFlowsTo()?
};
Self::to_elements(elements)
}
pub fn get_provider_description(&self) -> Result<String> {
let descr = unsafe {
self.element.CurrentProviderDescription()?
};
Ok(descr.to_string())
}
pub fn get_cached_provider_description(&self) -> Result<String> {
let descr = unsafe {
self.element.CachedProviderDescription()?
};
Ok(descr.to_string())
}
pub fn set_focus(&self) -> Result<()> {
unsafe {
self.element.SetFocus()?;
}
Ok(())
}
pub fn try_focus(&self) -> bool {
self.set_focus().is_ok()
}
pub fn get_pattern<T: UIPattern + TryFrom<IUnknown, Error = Error>>(&self) -> Result<T> {
let pattern = unsafe {
self.element.GetCurrentPattern(T::TYPE.into())?
};
T::try_from(pattern)
}
pub fn get_cached_pattern<T: UIPattern + TryFrom<IUnknown, Error = Error>>(&self) -> Result<T> {
let pattern = unsafe {
self.element.GetCachedPattern(T::TYPE.into())?
};
T::try_from(pattern)
}
pub fn get_clickable_point(&self) -> Result<Option<Point>> {
let mut point = Point::default();
let got = unsafe {
self.element.GetClickablePoint(point.as_mut())?
};
Ok(if got.as_bool() {
Some(point)
} else {
None
})
}
pub fn get_property_value(&self, property: UIProperty) -> Result<Variant> {
let value = unsafe {
self.element.GetCurrentPropertyValue(property.into())?
};
Ok(value.into())
}
pub fn get_cached_property_value(&self, property: UIProperty) -> Result<Variant> {
let value = unsafe {
self.element.GetCachedPropertyValue(property.into())?
};
Ok(value.into())
}
pub fn show_context_menu(&self) -> Result<()> {
let element3: IUIAutomationElement3 = self.element.cast()?;
unsafe {
element3.ShowContextMenu()?
}
Ok(())
}
pub(crate) fn to_elements(elements: IUIAutomationElementArray) -> Result<Vec<UIElement>> {
let mut arr: Vec<UIElement> = Vec::new();
unsafe {
for i in 0..elements.Length()? {
let elem = UIElement::from(elements.GetElement(i)?);
arr.push(elem);
}
}
Ok(arr)
}
pub fn send_keys(&self, keys: &str, interval: u64) -> Result<()> {
self.set_focus()?;
let kb = Keyboard::new();
kb.interval(interval).send_keys(keys)
}
pub fn hold_send_keys(&self, holdkeys: &str, keys: &str, interval: u64) -> Result<()> {
self.set_focus()?;
let mut kb = Keyboard::new().interval(interval);
kb.begin_hold_keys(holdkeys)?;
kb.send_keys(keys)?;
kb.end_hold_keys()
}
pub fn click(&self) -> Result<()> {
self.try_focus();
let point = self.get_click_point()?;
let mouse = Mouse::default();
mouse.click(point)
}
pub fn hold_click(&self, holdkeys: &str) -> Result<()> {
let point = self.get_click_point()?;
let mouse = Mouse::default().holdkeys(holdkeys);
mouse.click(point)
}
pub fn double_click(&self) -> Result<()> {
self.try_focus();
let point = self.get_click_point()?;
let mouse = Mouse::default();
mouse.double_click(point)
}
pub fn right_click(&self) -> Result<()> {
self.try_focus();
let point = self.get_click_point()?;
let mouse = Mouse::default();
mouse.right_click(point)
}
fn get_click_point(&self) -> Result<Point> {
if let Ok(Some(point)) = self.get_clickable_point() {
Ok(point)
} else {
let rect = self.get_bounding_rectangle()?;
let point = Point::new((rect.get_left() + rect.get_right()) / 2, (rect.get_top() + rect.get_bottom()) / 2);
Ok(point)
}
}
}
impl From<IUIAutomationElement> for UIElement {
fn from(element: IUIAutomationElement) -> Self {
UIElement {
element
}
}
}
impl From<&IUIAutomationElement> for UIElement {
fn from(value: &IUIAutomationElement) -> Self {
value.clone().into()
}
}
impl Into<IUIAutomationElement> for UIElement {
fn into(self) -> IUIAutomationElement {
self.element
}
}
impl Param<IUIAutomationElement> for UIElement {
unsafe fn param(self) -> windows::core::ParamValue<IUIAutomationElement> {
windows::core::ParamValue::Owned(self.element)
}
}
impl Param<IUIAutomationElement> for &UIElement {
unsafe fn param(self) -> windows::core::ParamValue<IUIAutomationElement> {
windows::core::ParamValue::Borrowed(self.element.as_raw())
}
}
impl AsRef<IUIAutomationElement> for UIElement {
fn as_ref(&self) -> &IUIAutomationElement {
&self.element
}
}
impl Display for UIElement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = self.get_name().unwrap_or(String::from("(NONE)"));
let control_type = self.get_localized_control_type().unwrap_or(String::from("UNKNOWN_TYPE"));
write!(f, "{} {}", name, control_type)
}
}
impl Debug for UIElement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut d = f.debug_struct("UIElement");
if let Ok(name) = self.get_name() {
d.field("name", &name);
};
if let Ok(control_type) = self.get_control_type() {
d.field("control_type", &control_type);
};
if let Ok(classname) = self.get_classname() {
d.field("classname", &classname);
};
d.finish()
}
}
#[derive(Debug, Clone)]
pub struct UICacheRequest {
request: IUIAutomationCacheRequest
}
impl UICacheRequest {
pub fn add_pattern(&self, pattern: UIPatternType) -> Result<()> {
unsafe {
self.request.AddPattern(pattern.into())?
};
Ok(())
}
pub fn add_property(&self, property: UIProperty) -> Result<()> {
unsafe {
self.request.AddProperty(property.into())?
};
Ok(())
}
pub fn get_element_mode(&self) -> Result<ElementMode> {
let mode = unsafe {
self.request.AutomationElementMode()?
};
Ok(mode.into())
}
pub fn set_element_mode(&self, mode: ElementMode) -> Result<()> {
unsafe {
self.request.SetAutomationElementMode(mode.into())?
};
Ok(())
}
pub fn get_tree_filter(&self) -> Result<UICondition> {
let condition = unsafe {
self.request.TreeFilter()?
};
Ok(condition.into())
}
pub fn set_tree_filter(&self, filter: UICondition) -> Result<()> {
unsafe {
self.request.SetTreeFilter(filter)?
};
Ok(())
}
pub fn get_tree_scope(&self) -> Result<TreeScope> {
let scope = unsafe {
self.request.TreeScope()?
};
Ok(scope.into())
}
pub fn set_tree_scope(&self, scope: TreeScope) -> Result<()> {
unsafe {
self.request.SetTreeScope(scope.into())?
};
Ok(())
}
}
impl From<IUIAutomationCacheRequest> for UICacheRequest {
fn from(value: IUIAutomationCacheRequest) -> Self {
Self {
request: value
}
}
}
impl Into<IUIAutomationCacheRequest> for UICacheRequest {
fn into(self) -> IUIAutomationCacheRequest {
self.request
}
}
impl AsRef<IUIAutomationCacheRequest> for UICacheRequest {
fn as_ref(&self) -> &IUIAutomationCacheRequest {
&self.request
}
}
impl Param<IUIAutomationCacheRequest> for UICacheRequest {
unsafe fn param(self) -> windows::core::ParamValue<IUIAutomationCacheRequest> {
windows::core::ParamValue::Owned(self.request)
}
}
impl Param<IUIAutomationCacheRequest> for &UICacheRequest {
unsafe fn param(self) -> windows::core::ParamValue<IUIAutomationCacheRequest> {
windows::core::ParamValue::Borrowed(self.request.as_raw())
}
}
#[derive(Clone)]
pub struct UITreeWalker {
tree_walker: IUIAutomationTreeWalker
}
impl UITreeWalker {
pub fn get_parent(&self, element: &UIElement) -> Result<UIElement> {
let parent: IUIAutomationElement;
unsafe {
parent = self.tree_walker.GetParentElement(&element.element)?;
}
Ok(UIElement::from(parent))
}
pub fn get_parent_build_cache(&self, element: &UIElement, cache_request: &UICacheRequest) -> Result<UIElement> {
let parent = unsafe {
self.tree_walker.GetParentElementBuildCache(element, cache_request)?
};
Ok(parent.into())
}
pub fn get_first_child(&self, element: &UIElement) -> Result<UIElement> {
let child: IUIAutomationElement;
unsafe {
child = self.tree_walker.GetFirstChildElement(&element.element)?;
}
Ok(UIElement::from(child))
}
pub fn get_first_child_build_cache(&self, element: &UIElement, cache_request: &UICacheRequest) -> Result<UIElement> {
let child = unsafe {
self.tree_walker.GetFirstChildElementBuildCache(element, cache_request)?
};
Ok(child.into())
}
pub fn get_last_child(&self, element: &UIElement) -> Result<UIElement> {
let child: IUIAutomationElement;
unsafe {
child = self.tree_walker.GetLastChildElement(&element.element)?;
}
Ok(UIElement::from(child))
}
pub fn get_last_child_build_cache(&self, element: &UIElement, cache_request: &UICacheRequest) -> Result<UIElement> {
let child = unsafe {
self.tree_walker.GetLastChildElementBuildCache(element, cache_request)?
};
Ok(child.into())
}
pub fn get_next_sibling(&self, element: &UIElement) -> Result<UIElement> {
let sibling: IUIAutomationElement;
unsafe {
sibling = self.tree_walker.GetNextSiblingElement(&element.element)?;
}
Ok(UIElement::from(sibling))
}
pub fn get_next_sibling_build_cache(&self, element: &UIElement, cache_request: &UICacheRequest) -> Result<UIElement> {
let sibling = unsafe {
self.tree_walker.GetNextSiblingElementBuildCache(element, cache_request)?
};
Ok(sibling.into())
}
pub fn get_previous_sibling(&self, element: &UIElement) -> Result<UIElement> {
let sibling: IUIAutomationElement;
unsafe {
sibling = self.tree_walker.GetPreviousSiblingElement(&element.element)?;
}
Ok(UIElement::from(sibling))
}
pub fn get_previous_sibling_build_cache(&self, element: &UIElement, cache_request: &UICacheRequest) -> Result<UIElement> {
let sibling = unsafe {
self.tree_walker.GetPreviousSiblingElementBuildCache(element, cache_request)?
};
Ok(sibling.into())
}
pub fn normalize(&self, element: &UIElement) -> Result<UIElement> {
let result = unsafe {
self.tree_walker.NormalizeElement(element.as_ref())?
};
Ok(result.into())
}
pub fn normalize_build_cache(&self, element: &UIElement, cache_request: &UICacheRequest) -> Result<UIElement> {
let ret = unsafe {
self.tree_walker.NormalizeElementBuildCache(element, cache_request)?
};
Ok(ret.into())
}
pub fn get_condition(&self) -> Result<UICondition> {
let condition = unsafe {
self.tree_walker.Condition()?
};
Ok(condition.into())
}
}
impl From<IUIAutomationTreeWalker> for UITreeWalker {
fn from(tree_walker: IUIAutomationTreeWalker) -> Self {
UITreeWalker {
tree_walker
}
}
}
impl Into<IUIAutomationTreeWalker> for UITreeWalker {
fn into(self) -> IUIAutomationTreeWalker {
self.tree_walker
}
}
impl AsRef<IUIAutomationTreeWalker> for UITreeWalker {
fn as_ref(&self) -> &IUIAutomationTreeWalker {
&self.tree_walker
}
}
#[derive(Debug)]
pub enum UIMatcherMode {
Raw,
Control,
Content
}
pub struct UIMatcher {
automation: UIAutomation,
mode: UIMatcherMode,
depth: u32,
from: Option<UIElement>,
filters: Vec<Box<dyn MatcherFilter>>,
timeout: u64,
interval: u64,
debug: bool
}
impl UIMatcher {
pub fn new(automation: UIAutomation) -> Self {
UIMatcher {
automation,
mode: UIMatcherMode::Control,
depth: 7,
from: None,
filters: Vec::new(),
timeout: 3000,
interval: 100,
debug: false
}
}
pub fn mode(mut self, search_mode: UIMatcherMode) -> Self {
self.mode = search_mode;
self
}
pub fn from(mut self, element: UIElement) -> Self {
self.from = Some(element);
self
}
pub fn from_ref(mut self, element: &UIElement) -> Self {
self.from = Some(element.clone());
self
}
pub fn depth(mut self, depth: u32) -> Self {
self.depth = depth;
self
}
pub fn timeout(mut self, timeout: u64) -> Self {
self.timeout = timeout;
self
}
pub fn interval(mut self, interval: u64) -> Self {
self.interval = interval;
self
}
pub fn filter(mut self, filter: Box<dyn MatcherFilter>) -> Self {
self.filters.push(filter);
self
}
pub fn filter_fn<F>(mut self, f: Box<F>) -> Self where F: Fn(&UIElement) -> Result<bool> + 'static {
let filter = FnFilter {
filter: f
};
self.filters.push(Box::new(filter));
self
}
pub fn name<S: Into<String>>(self, name: S) -> Self {
let condition = NameFilter {
value: name.into(),
casesensitive: true,
partial: false
};
self.filter(Box::new(condition))
}
pub fn contains_name<S: Into<String>>(self, name: S) -> Self {
let condition = NameFilter {
value: name.into(),
casesensitive: false,
partial: true
};
self.filter(Box::new(condition))
}
pub fn match_name<S: Into<String>>(self, name: S) -> Self {
let condition = NameFilter {
value: name.into(),
casesensitive: false,
partial: false
};
self.filter(Box::new(condition))
}
pub fn classname<S: Into<String>>(self, classname: S) -> Self {
let condition = ClassNameFilter {
classname: classname.into()
};
self.filter(Box::new(condition))
}
pub fn control_type(self, control_type: ControlType) -> Self {
let condition = ControlTypeFilter {
control_type
};
self.filter(Box::new(condition))
}
pub fn reset(mut self) -> Self {
self.filters.clear();
self
}
pub fn debug(mut self, debug: bool) -> Self {
self.debug = debug;
self
}
pub fn find_first(&self) -> Result<UIElement> {
let elements = self.find(true)?;
if elements.is_empty() {
Err(Error::new(ERR_NOTFOUND, "can not find element"))
} else {
Ok(elements[0].clone())
}
}
pub fn find_all(&self) -> Result<Vec<UIElement>> {
let elements = self.find(false)?;
if elements.is_empty() {
Err(Error::new(ERR_NOTFOUND, "can not find element"))
} else {
Ok(elements)
}
}
fn find(&self, first_only: bool) -> Result<Vec<UIElement>> {
let mut elements: Vec<UIElement> = Vec::new();
let start = Local::now().timestamp_millis();
loop {
if self.debug {
#[cfg(feature = "log")]
log::debug!("Try to match element...");
#[cfg(not(feature = "log"))]
println!("Try to match element...")
}
let (root, walker) = self.prepare()?;
self.search(&walker, &root, &mut elements, 1, first_only)?;
if !elements.is_empty() || self.timeout <= 0 {
break;
}
let now = Local::now().timestamp_millis();
if now - start >= self.timeout as i64 {
return Err(Error::new(ERR_TIMEOUT, "find element time out"));
}
sleep(Duration::from_millis(self.interval));
}
Ok(elements)
}
fn prepare(&self) -> Result<(UIElement, UITreeWalker)> {
let root = if let Some(ref from) = self.from {
from.clone()
} else {
self.automation.get_root_element()?
};
let walker = match self.mode {
UIMatcherMode::Raw => self.automation.create_tree_walker()?,
UIMatcherMode::Control => self.automation.filter_tree_walker(self.automation.get_control_view_condition()?)?,
UIMatcherMode::Content => self.automation.filter_tree_walker(self.automation.get_content_view_condition()?)?,
};
Ok((root, walker))
}
fn search(&self, walker: &UITreeWalker, element: &UIElement, elements: &mut Vec<UIElement>, depth: u32, first_only: bool) -> Result<()> {
if self.is_matched(element)? {
elements.push(element.clone());
if first_only {
return Ok(());
}
}
if depth < self.depth {
let mut next = walker.get_first_child(element);
while let Ok(ref child) = next {
self.search(walker, child, elements, depth + 1, first_only)?;
if first_only && !elements.is_empty() {
return Ok(());
}
next = walker.get_next_sibling(child);
}
}
Ok(())
}
fn is_matched(&self, element: &UIElement) -> Result<bool> {
if let Some(ref root) = self.from {
if self.automation.compare_elements(root, element)? {
return Ok(false);
}
}
let mut ret = true;
let mut failed_filter = 0;
for (idx, condition) in self.filters.iter().enumerate() {
ret = condition.judge(element)?;
if !ret {
failed_filter = idx;
break;
}
}
if self.debug {
#[cfg(feature = "log") ]
log::debug!("{:?} -> {} in filter {}", element, ret, failed_filter);
#[cfg(not(feature = "log"))]
println!("{:?} -> {} in filter {}", element, ret, failed_filter);
}
Ok(ret)
}
}
impl Debug for UIMatcher {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("UIMatcher")
.field("automation", &self.automation)
.field("mode", &self.mode)
.field("depth", &self.depth)
.field("from", &self.from)
.field("filters", &format!("({} filers)", self.filters.len()))
.field("timeout", &self.timeout)
.field("interval", &self.interval)
.field("debug", &self.debug)
.finish()
}
}
pub trait IUICondition<T: Interface>: Sized + From<T> + Into<T> + AsRef<T> {
}
#[derive(Debug, Clone)]
pub struct UICondition(IUIAutomationCondition);
impl UICondition {
pub fn is_bool_condition(&self) -> bool {
let bool_cond: windows::core::Result<IUIAutomationBoolCondition> = self.0.cast();
bool_cond.is_ok()
}
pub fn is_and_condition(&self) -> bool {
let and_cond: windows::core::Result<IUIAutomationAndCondition> = self.0.cast();
and_cond.is_ok()
}
pub fn is_or_condition(&self) -> bool {
let or_cond: windows::core::Result<IUIAutomationOrCondition> = self.0.cast();
or_cond.is_ok()
}
pub fn is_not_condition(&self) -> bool {
let not_cond: windows::core::Result<IUIAutomationNotCondition> = self.0.cast();
not_cond.is_ok()
}
pub fn is_property_condition(&self) -> bool {
let prop_cond: windows::core::Result<IUIAutomationPropertyCondition> = self.0.cast();
prop_cond.is_ok()
}
}
impl IUICondition<IUIAutomationCondition> for UICondition {
}
impl From<IUIAutomationCondition> for UICondition {
fn from(condition: IUIAutomationCondition) -> Self {
Self(condition)
}
}
impl Into<IUIAutomationCondition> for UICondition {
fn into(self) -> IUIAutomationCondition {
self.0
}
}
impl AsRef<IUIAutomationCondition> for UICondition {
fn as_ref(&self) -> &IUIAutomationCondition {
&self.0
}
}
impl Param<IUIAutomationCondition> for UICondition {
unsafe fn param(self) -> windows::core::ParamValue<IUIAutomationCondition> {
windows::core::ParamValue::Owned(self.0)
}
}
impl Param<IUIAutomationCondition> for &UICondition {
unsafe fn param(self) -> windows::core::ParamValue<IUIAutomationCondition> {
windows::core::ParamValue::Borrowed(self.0.as_raw())
}
}
#[derive(Debug, Clone)]
pub struct UIBoolCondition(IUIAutomationBoolCondition);
impl UIBoolCondition {
pub fn get_bool_value(&self) -> Result<bool> {
let val = unsafe {
self.0.BooleanValue()?
};
Ok(val.as_bool())
}
}
impl IUICondition<IUIAutomationBoolCondition> for UIBoolCondition {
}
impl From<IUIAutomationBoolCondition> for UIBoolCondition {
fn from(condition: IUIAutomationBoolCondition) -> Self {
Self(condition)
}
}
impl Into<IUIAutomationBoolCondition> for UIBoolCondition {
fn into(self) -> IUIAutomationBoolCondition {
self.0
}
}
impl AsRef<IUIAutomationBoolCondition> for UIBoolCondition {
fn as_ref(&self) -> &IUIAutomationBoolCondition {
&self.0
}
}
impl TryFrom<IUIAutomationCondition> for UIBoolCondition {
type Error = Error;
fn try_from(condition: IUIAutomationCondition) -> Result<Self> {
let bool_cond: IUIAutomationBoolCondition = condition.cast()?;
Ok(bool_cond.into())
}
}
impl Into<IUIAutomationCondition> for UIBoolCondition {
fn into(self) -> IUIAutomationCondition {
self.0.cast().unwrap()
}
}
impl TryFrom<UICondition> for UIBoolCondition {
type Error = Error;
fn try_from(condition: UICondition) -> Result<Self> {
condition.0.try_into()
}
}
impl Into<UICondition> for UIBoolCondition {
fn into(self) -> UICondition {
let condition: IUIAutomationCondition = self.0.cast().unwrap();
condition.into()
}
}
#[derive(Debug, Clone)]
pub struct UINotCondition(IUIAutomationNotCondition);
impl UINotCondition {
pub fn get_child(&self) -> Result<UICondition> {
let child = unsafe {
self.0.GetChild()?
};
Ok(child.into())
}
}
impl IUICondition<IUIAutomationNotCondition> for UINotCondition {
}
impl From<IUIAutomationNotCondition> for UINotCondition {
fn from(condition: IUIAutomationNotCondition) -> Self {
Self(condition)
}
}
impl Into<IUIAutomationNotCondition> for UINotCondition {
fn into(self) -> IUIAutomationNotCondition {
self.0
}
}
impl AsRef<IUIAutomationNotCondition> for UINotCondition {
fn as_ref(&self) -> &IUIAutomationNotCondition {
&self.0
}
}
impl TryFrom<IUIAutomationCondition> for UINotCondition {
type Error = Error;
fn try_from(condition: IUIAutomationCondition) -> Result<Self> {
let not_cond: IUIAutomationNotCondition = condition.cast()?;
Ok(not_cond.into())
}
}
impl Into<IUIAutomationCondition> for UINotCondition {
fn into(self) -> IUIAutomationCondition {
self.0.cast().unwrap()
}
}
impl TryFrom<UICondition> for UINotCondition {
type Error = Error;
fn try_from(condition: UICondition) -> Result<Self> {
condition.0.try_into()
}
}
impl Into<UICondition> for UINotCondition {
fn into(self) -> UICondition {
let condition: IUIAutomationCondition = self.0.cast().unwrap();
condition.into()
}
}
#[derive(Debug, Clone)]
pub struct UIAndCondition(IUIAutomationAndCondition);
impl UIAndCondition {
pub fn get_children_count(&self) -> Result<i32> {
let count = unsafe {
self.0.ChildCount()?
};
Ok(count)
}
pub fn get_children(&self) -> Result<Vec<UICondition>> {
let arr = unsafe {
self.0.GetChildren()?
};
let arr = SafeArray::from(arr);
let children: Vec<IUIAutomationCondition> = arr.into_interface_vector()?;
let conditions: Vec<UICondition> = children.into_iter().map(|c| c.into()).collect();
Ok(conditions)
}
}
impl IUICondition<IUIAutomationAndCondition> for UIAndCondition {
}
impl From<IUIAutomationAndCondition> for UIAndCondition {
fn from(condition: IUIAutomationAndCondition) -> Self {
Self(condition)
}
}
impl Into<IUIAutomationAndCondition> for UIAndCondition {
fn into(self) -> IUIAutomationAndCondition {
self.0
}
}
impl AsRef<IUIAutomationAndCondition> for UIAndCondition {
fn as_ref(&self) -> &IUIAutomationAndCondition {
&self.0
}
}
impl TryFrom<IUIAutomationCondition> for UIAndCondition {
type Error = Error;
fn try_from(condition: IUIAutomationCondition) -> Result<Self> {
let and_cond: IUIAutomationAndCondition = condition.cast()?;
Ok(and_cond.into())
}
}
impl Into<IUIAutomationCondition> for UIAndCondition {
fn into(self) -> IUIAutomationCondition {
self.0.cast().unwrap()
}
}
impl TryFrom<UICondition> for UIAndCondition {
type Error = Error;
fn try_from(condition: UICondition) -> Result<Self> {
condition.0.try_into()
}
}
impl Into<UICondition> for UIAndCondition {
fn into(self) -> UICondition {
let condition: IUIAutomationCondition = self.0.cast().unwrap();
condition.into()
}
}
#[derive(Debug, Clone)]
pub struct UIOrCondition(IUIAutomationOrCondition);
impl UIOrCondition {
pub fn get_children_count(&self) -> Result<i32> {
let count = unsafe {
self.0.ChildCount()?
};
Ok(count)
}
pub fn get_children(&self) -> Result<Vec<UICondition>> {
let arr = unsafe {
self.0.GetChildren()?
};
let arr = SafeArray::from(arr);
let children: Vec<IUIAutomationCondition> = arr.into_interface_vector()?;
let conditions: Vec<UICondition> = children.into_iter().map(|c| c.into()).collect();
Ok(conditions)
}
}
impl IUICondition<IUIAutomationOrCondition> for UIOrCondition {
}
impl From<IUIAutomationOrCondition> for UIOrCondition {
fn from(condition: IUIAutomationOrCondition) -> Self {
Self(condition)
}
}
impl Into<IUIAutomationOrCondition> for UIOrCondition {
fn into(self) -> IUIAutomationOrCondition {
self.0
}
}
impl AsRef<IUIAutomationOrCondition> for UIOrCondition {
fn as_ref(&self) -> &IUIAutomationOrCondition {
&self.0
}
}
impl TryFrom<IUIAutomationCondition> for UIOrCondition {
type Error = Error;
fn try_from(condition: IUIAutomationCondition) -> Result<Self> {
let or_cond: IUIAutomationOrCondition = condition.cast()?;
Ok(or_cond.into())
}
}
impl Into<IUIAutomationCondition> for UIOrCondition {
fn into(self) -> IUIAutomationCondition {
self.0.cast().unwrap()
}
}
impl TryFrom<UICondition> for UIOrCondition {
type Error = Error;
fn try_from(condition: UICondition) -> Result<Self> {
let or_cond: IUIAutomationOrCondition = condition.0.cast()?;
Ok(or_cond.into())
}
}
impl Into<UICondition> for UIOrCondition {
fn into(self) -> UICondition {
let condition: IUIAutomationCondition = self.into();
condition.into()
}
}
#[derive(Debug, Clone)]
pub struct UIPropertyCondition(IUIAutomationPropertyCondition);
impl UIPropertyCondition {
pub fn get_property(&self) -> Result<UIProperty> {
let property_id = unsafe {
self.0.PropertyId()?
};
Ok(property_id.into())
}
pub fn get_property_value(&self) -> Result<Variant> {
let value = unsafe {
self.0.PropertyValue()?
};
Ok(value.into())
}
pub fn get_property_condition_flags(&self) -> Result<PropertyConditionFlags> {
Ok(unsafe {
self.0.PropertyConditionFlags()?
}.into())
}
}
impl IUICondition<IUIAutomationPropertyCondition> for UIPropertyCondition {
}
impl From<IUIAutomationPropertyCondition> for UIPropertyCondition {
fn from(condition: IUIAutomationPropertyCondition) -> Self {
Self(condition)
}
}
impl Into<IUIAutomationPropertyCondition> for UIPropertyCondition {
fn into(self) -> IUIAutomationPropertyCondition {
self.0
}
}
impl AsRef<IUIAutomationPropertyCondition> for UIPropertyCondition {
fn as_ref(&self) -> &IUIAutomationPropertyCondition {
&self.0
}
}
impl TryFrom<IUIAutomationCondition> for UIPropertyCondition {
type Error = Error;
fn try_from(condition: IUIAutomationCondition) -> Result<Self> {
let prop_cond: IUIAutomationPropertyCondition = condition.cast()?;
Ok(prop_cond.into())
}
}
impl Into<IUIAutomationCondition> for UIPropertyCondition {
fn into(self) -> IUIAutomationCondition {
self.0.cast().unwrap()
}
}
impl TryFrom<UICondition> for UIPropertyCondition {
type Error = Error;
fn try_from(condition: UICondition) -> Result<Self> {
condition.0.try_into()
}
}
impl Into<UICondition> for UIPropertyCondition {
fn into(self) -> UICondition {
let condition: IUIAutomationCondition = self.0.cast().unwrap();
condition.into()
}
}
#[cfg(test)]
mod tests {
use std::thread::sleep;
use std::time::Duration;
use windows::Win32::UI::Accessibility::*;
use windows::Win32::UI::WindowsAndMessaging::GetForegroundWindow;
use crate::UIAutomation;
use crate::UIElement;
use crate::controls::ControlType;
use crate::filters::MatcherFilter;
use crate::types::TreeScope;
fn print_element(element: &UIElement) {
println!("Name: {}", element.get_name().unwrap());
println!("ControlType: {:?}", element.get_control_type().unwrap());
println!("LocalizedControlType: {}", element.get_localized_control_type().unwrap());
println!("BoundingRectangle: {}", element.get_bounding_rectangle().unwrap());
println!("IsEnabled: {}", element.is_enabled().unwrap());
println!("IsOffscreen: {}", element.is_offscreen().unwrap());
println!("IsKeyboardFocusable: {}", element.is_keyboard_focusable().unwrap());
println!("HasKeyboardFocus: {}", element.has_keyboard_focus().unwrap());
println!("ProcessId: {}", element.get_process_id().unwrap());
println!("RuntimeId: {:?}", element.get_runtime_id().unwrap());
println!("FrameworkId: {}", element.get_framework_id().unwrap());
println!("ClassName: {}", element.get_classname().unwrap());
println!("NativeWindowHandle: {}", element.get_native_window_handle().unwrap());
println!("ProviderDescription: {}", element.get_provider_description().unwrap());
println!("IsPassword: {}", element.is_password().unwrap());
println!("AutomationId: {}", element.get_automation_id().unwrap());
}
#[test]
fn test_element_properties() {
let automation = UIAutomation::new().unwrap();
sleep(Duration::new(0, 500));
let root = automation.get_root_element().unwrap();
println!("---------------------");
print_element(&root);
println!("---------------------");
}
#[test]
fn test_tree_walker() {
let automation = UIAutomation::new().unwrap();
let root = automation.get_root_element().unwrap();
let walker = automation.create_tree_walker().unwrap();
let child = walker.get_first_child(&root);
assert!(child.is_ok());
}
#[test]
fn test_zh_input() {
let automation = UIAutomation::new().unwrap();
let matcher = automation.create_matcher().depth(2).classname("Notepad").timeout(1000);
if let Ok(notepad) = matcher.find_first() {
notepad.send_keys("你好!{enter}", 0).unwrap();
notepad.send_keys("Hello!", 0).unwrap();
}
}
#[test]
fn test_menu_click() {
let automation = UIAutomation::new().unwrap();
let matcher = automation.create_matcher().depth(2).classname("Notepad").timeout(1000);
if let Ok(notepad) = matcher.find_first() {
let matcher = automation.create_matcher().control_type(ControlType::MenuItem).from_ref(¬epad).name("文件").depth(5).timeout(1000);
if let Ok(menu_item) = matcher.find_first() {
menu_item.click().unwrap();
}
}
}
#[test]
fn test_element_find() {
let automation = UIAutomation::new().unwrap();
let root = automation.get_root_element().unwrap();
let condition = automation.create_true_condition().unwrap();
let child = root.find_first(TreeScope::Children, &condition).unwrap();
println!("{}", child);
}
#[test]
fn test_notepad() {
let automation = UIAutomation::new().unwrap();
let matcher = automation.create_matcher();
if let Ok(window) = matcher.classname("Notepad").timeout(0).find_first() {
println!("{}", window.get_name().unwrap());
let menubar = automation.create_matcher() .from(window.clone())
.control_type(ControlType::Pane)
.timeout(0)
.find_first().unwrap();
println!("{}, {}", menubar.get_framework_id().unwrap(), menubar.get_classname().unwrap());
}
}
#[test]
fn test_search_from() {
let automation = UIAutomation::new().unwrap();
if let Ok(window) = automation.create_matcher().classname("Notepad").find_first() {
let nothing = automation.create_matcher().from(window.clone()).control_type(ControlType::Window).find_first();
assert!(nothing.is_err());
}
}
struct FrameworkIdFilter(String);
impl MatcherFilter for FrameworkIdFilter {
fn judge(&self, element: &crate::UIElement) -> crate::Result<bool> {
let id = element.get_framework_id()?;
Ok(id == self.0)
}
}
#[test]
fn test_custom_search() {
let automation = UIAutomation::new().unwrap();
let matcher = automation.create_matcher().timeout(0).filter(Box::new(FrameworkIdFilter("Win32".into()))).depth(2);
let element = matcher.find_first();
assert!(element.is_ok());
println!("{}", element.unwrap());
}
#[test]
fn test_find_no_wait() {
let automation = UIAutomation::new().unwrap();
let matcher = automation.create_matcher().timeout(0).name("You can find nothing!");
let item = matcher.find_first();
assert!(item.is_err());
}
#[test]
fn test_automation_id() {
let automation = UIAutomation::new().unwrap();
if let Ok(notepad) = automation.create_matcher().timeout(0).classname("Notepad").find_first() {
let title_bar = automation.create_matcher().from(notepad).timeout(0).control_type(ControlType::TitleBar).find_first().unwrap();
let element: &IUIAutomationElement = title_bar.as_ref();
let automation_id = unsafe {
element.CurrentAutomationId().unwrap()
};
println!("{} -> {}", title_bar, automation_id);
}
}
#[test]
fn test_window_rect_prop() {
let window = unsafe { GetForegroundWindow() };
if window.is_invalid() {
return;
}
let automation = UIAutomation::new().unwrap();
let element = automation.element_from_handle(window.into()).unwrap();
let rect = element.get_bounding_rectangle().unwrap();
println!("Window Rect = {}", rect);
let val = element.get_property_value(crate::types::UIProperty::BoundingRectangle).unwrap();
println!("Window Rect Prop = {}", val.to_string());
assert!(val.is_array());
let arr = val.get_array().unwrap();
let l: f64 = arr.get_element(0).unwrap();
let t: f64 = arr.get_element(1).unwrap();
let r: f64 = arr.get_element(2).unwrap();
let b: f64 = arr.get_element(3).unwrap();
println!("Window Rect Array = [{}, {}, {}, {}]", l, t, r, b);
}
#[test]
fn test_create() {
let _ = UIAutomation::new();
let uiautomation = UIAutomation::new_direct();
assert!(uiautomation.is_ok());
}
#[test]
fn test_search_from_handle() {
let auto = UIAutomation::new().unwrap();
let _ = auto.element_from_handle(crate::types::Handle::from(0x2006C6));
}
}