diff --git a/CHANGELOG.md b/CHANGELOG.md index a9ef1fb..68cba51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -138,3 +138,7 @@ ## v0.10.0 + Fix typo in method names (ragne -> range) [#57](https://github.com/leexgone/uiautomation-rs/pull/57) + +## v0.11.0 + ++ Support event handler. [#59](https://github.com/leexgone/uiautomation-rs/issues/59) diff --git a/Cargo.toml b/Cargo.toml index 88555ab..45df5f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,10 @@ [workspace] members = [ - "crates/uiautomation", "crates/uiautomation_derive", - "samples/uia_print", - "samples/uia_cached_print", - "samples/uia_notepad", - "samples/uia_varaint", - "samples/win_update", - "tests/core_test", + "crates/uiautomation", + "samples/*", + "tests/*", ] resolver = "2" diff --git a/README.cn.md b/README.cn.md index 4c71bec..91ae94f 100644 --- a/README.cn.md +++ b/README.cn.md @@ -103,3 +103,41 @@ fn main() { root.send_keys("{Win}D", 10).unwrap(); } ``` + +### 添加事件侦听接口 + +``` rust +struct MyFocusChangedEventHandler{} + +impl CustomFocusChangedEventHandler for MyFocusChangedEventHandler { + fn handle(&self, sender: &uiautomation::UIElement) -> uiautomation::Result<()> { + println!("Focus changed: {}", sender); + Ok(()) + } +} + +fn main() { + let note_proc = Process::create("notepad.exe").unwrap(); + + let automation = UIAutomation::new().unwrap(); + let root = automation.get_root_element().unwrap(); + let matcher = automation.create_matcher().from(root).timeout(10000).classname("Notepad"); + if let Ok(notepad) = matcher.find_first() { + let focus_changed_handler = MyFocusChangedEventHandler {}; + let focus_changed_handler = UIFocusChangedEventHandler::from(focus_changed_handler); + + automation.add_focus_changed_event_handler(None, &focus_changed_handler).unwrap(); + + let text_changed_handler: Box = Box::new(|sender, property, value| { + println!("Property changed: {}.{:?} = {}", sender, property, value); + Ok(()) + }); + let text_changed_handler = UIPropertyChangedEventHandler::from(text_changed_handler); + + automation.add_property_changed_event_handler(¬epad, uiautomation::types::TreeScope::Subtree, None, &text_changed_handler, &[UIProperty::ValueValue]).unwrap(); + } + + println!("waiting for notepad.exe..."); + note_proc.wait().unwrap(); +} +``` diff --git a/README.md b/README.md index 3f11bf7..dab8478 100644 --- a/README.md +++ b/README.md @@ -103,3 +103,41 @@ fn main() { root.send_keys("{Win}D", 10).unwrap(); } ``` + +### Add Event Handler + +``` rust +struct MyFocusChangedEventHandler{} + +impl CustomFocusChangedEventHandler for MyFocusChangedEventHandler { + fn handle(&self, sender: &uiautomation::UIElement) -> uiautomation::Result<()> { + println!("Focus changed: {}", sender); + Ok(()) + } +} + +fn main() { + let note_proc = Process::create("notepad.exe").unwrap(); + + let automation = UIAutomation::new().unwrap(); + let root = automation.get_root_element().unwrap(); + let matcher = automation.create_matcher().from(root).timeout(10000).classname("Notepad"); + if let Ok(notepad) = matcher.find_first() { + let focus_changed_handler = MyFocusChangedEventHandler {}; + let focus_changed_handler = UIFocusChangedEventHandler::from(focus_changed_handler); + + automation.add_focus_changed_event_handler(None, &focus_changed_handler).unwrap(); + + let text_changed_handler: Box = Box::new(|sender, property, value| { + println!("Property changed: {}.{:?} = {}", sender, property, value); + Ok(()) + }); + let text_changed_handler = UIPropertyChangedEventHandler::from(text_changed_handler); + + automation.add_property_changed_event_handler(¬epad, uiautomation::types::TreeScope::Subtree, None, &text_changed_handler, &[UIProperty::ValueValue]).unwrap(); + } + + println!("waiting for notepad.exe..."); + note_proc.wait().unwrap(); +} +``` diff --git a/crates/uiautomation/Cargo.toml b/crates/uiautomation/Cargo.toml index 4419238..60314ba 100644 --- a/crates/uiautomation/Cargo.toml +++ b/crates/uiautomation/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uiautomation" -version = "0.10.0" +version = "0.11.0" edition = "2021" license = "Apache-2.0" authors = ["Steven Lee "] @@ -19,7 +19,10 @@ targets = ["aarch64-pc-windows-msvc", "i686-pc-windows-msvc", "x86_64-pc-windows chrono = "0.4.38" phf = { version = "0.11.2", features = ["macros"] } -uiautomation_derive = { version = "0.3.0", path = "../uiautomation_derive" } +uiautomation_derive = { version = "0.3.2", path = "../uiautomation_derive" } + +[dependencies.windows-core] +version = "0.56.0" [dependencies.windows] version = "0.56.0" @@ -34,5 +37,6 @@ features = [ "Win32_System_Threading", "Win32_Security", "Win32_UI_Shell_PropertiesSystem", - "UI_UIAutomation" -] \ No newline at end of file + "UI_UIAutomation", + "implement" +] diff --git a/crates/uiautomation/src/core.rs b/crates/uiautomation/src/core.rs index 22c2da7..357f08c 100644 --- a/crates/uiautomation/src/core.rs +++ b/crates/uiautomation/src/core.rs @@ -24,8 +24,14 @@ 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; @@ -310,6 +316,83 @@ impl UIAutomation { let request = unsafe { self.automation.CreateCacheRequest()? }; Ok(request.into()) } + + /// Registers a method that handles Microsoft UI Automation events. + 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(()) + } + + /// Removes the specified UI Automation event handler. + 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(()) + } + + /// Registers a method that handles and array of property-changed events. + 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 = properties.iter().map(|p| (*p).into()).collect(); + unsafe { + self.automation.AddPropertyChangedEventHandlerNativeArray(element, scope.into(), cache_request, handler, &prop_arr)? + }; + Ok(()) + } + + /// Removes a property-changed event handler. + pub fn remove_property_changed_event_handler(&self, element: &UIElement, handler: &UIPropertyChangedEventHandler) -> Result<()> { + unsafe { + self.automation.RemovePropertyChangedEventHandler(element, handler)? + }; + Ok(()) + } + + /// Registers a method that handles structure-changed events. + 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(()) + } + + /// Removes a structure-changed event handler. + pub fn remove_structure_changed_event_handler(&self, element: &UIElement, handler: &UIStructureChangeEventHandler) -> Result<()> { + unsafe { + self.automation.RemoveStructureChangedEventHandler(element, handler)? + }; + Ok(()) + } + + /// Registers a method that handles focus-changed events. + 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(()) + } + + /// Removes a focus-changed event handler. + pub fn remove_focus_changed_event_handler(&self, handler: &UIFocusChangedEventHandler) -> Result<()> { + unsafe { + self.automation.RemoveFocusChangedEventHandler(handler)? + }; + Ok(()) + } + + /// Removes all registered Microsoft UI Automation event handlers. + pub fn remove_all_event_handlers(&self) -> Result<()> { + unsafe { + self.automation.RemoveAllEventHandlers()? + }; + Ok(()) + } } impl From for UIAutomation { @@ -1115,18 +1198,18 @@ impl From for UIElement { } } +impl From<&IUIAutomationElement> for UIElement { + fn from(value: &IUIAutomationElement) -> Self { + value.clone().into() + } +} + impl Into for UIElement { fn into(self) -> IUIAutomationElement { self.element } } -// impl IntoParam for UIElement { -// unsafe fn into_param(self) -> windows::core::Param { -// windows::core::Param::Owned(self.element) -// } -// } - impl Param for UIElement { unsafe fn param(self) -> windows::core::ParamValue { windows::core::ParamValue::Owned(self.element) @@ -2169,7 +2252,7 @@ mod tests { use std::thread::sleep; use std::time::Duration; - use windows::Win32::UI::Accessibility::IUIAutomationElement; + use windows::Win32::UI::Accessibility::*; use windows::Win32::UI::WindowsAndMessaging::GetForegroundWindow; use crate::UIAutomation; diff --git a/crates/uiautomation/src/errors.rs b/crates/uiautomation/src/errors.rs index 9cac343..e827849 100644 --- a/crates/uiautomation/src/errors.rs +++ b/crates/uiautomation/src/errors.rs @@ -2,6 +2,7 @@ use std::fmt::Display; use windows::Win32::Foundation::GetLastError; use windows::core::HRESULT; +use windows::Win32::Foundation::E_FAIL; /// Error caused by unknown reason. pub const ERR_NONE: i32 = 0; @@ -89,6 +90,21 @@ impl From for Error { } } +impl Into for Error { + fn into(self) -> windows::core::Error { + // if self.code < 0 { + // windows::core::Error::new(HRESULT(self.code), self.message) + // } else { + // windows::core::Error::new(E_FAIL, self.message) + // } + if let Some(result) = self.result() { + windows::core::Error::from_hresult(result) + } else { + windows::core::Error::new(E_FAIL, self.message) + } + } +} + impl From for Error { fn from(result: HRESULT) -> Self { Self { diff --git a/crates/uiautomation/src/events/functions.rs b/crates/uiautomation/src/events/functions.rs new file mode 100644 index 0000000..2536f72 --- /dev/null +++ b/crates/uiautomation/src/events/functions.rs @@ -0,0 +1,132 @@ +use windows::Win32::UI::Accessibility::IUIAutomationElement; +use windows::Win32::UI::Accessibility::IUIAutomationEventHandler; +use windows::Win32::UI::Accessibility::IUIAutomationEventHandler_Impl; +use windows::Win32::UI::Accessibility::IUIAutomationFocusChangedEventHandler; +use windows::Win32::UI::Accessibility::IUIAutomationFocusChangedEventHandler_Impl; +use windows::Win32::UI::Accessibility::IUIAutomationPropertyChangedEventHandler; +use windows::Win32::UI::Accessibility::IUIAutomationPropertyChangedEventHandler_Impl; +use windows::Win32::UI::Accessibility::IUIAutomationStructureChangedEventHandler; +use windows::Win32::UI::Accessibility::IUIAutomationStructureChangedEventHandler_Impl; +use windows::Win32::UI::Accessibility::UIA_EVENT_ID; +use windows::Win32::UI::Accessibility::UIA_PROPERTY_ID; +use windows_core::implement; + +use crate::variants::SafeArray; +use crate::variants::Variant; +use crate::UIElement; + +use super::CustomEventHandlerFn; +use super::CustomFocusChangedEventHandlerFn; +use super::CustomPropertyChangedEventHandlerFn; +use super::CustomStructureChangedEventHandlerFn; + +#[implement(IUIAutomationEventHandler)] +pub struct AutomationEventHandler { + handler: Box +} + +impl IUIAutomationEventHandler_Impl for AutomationEventHandler { + fn HandleAutomationEvent(&self, sender: Option<&IUIAutomationElement>, eventid: UIA_EVENT_ID) -> windows_core::Result<()> { + if let Some(e) = sender { + let element = UIElement::from(e); + let handler = &self.handler; + handler(&element, eventid.into()).map_err(|e| e.into()) + } else { + Ok(()) + } + } +} + +impl From> for AutomationEventHandler { + fn from(handler: Box) -> Self { + Self { + handler + } + } +} + +#[implement(IUIAutomationPropertyChangedEventHandler)] +pub struct AutomationPropertyChangedEventHandler { + handler: Box +} + +impl IUIAutomationPropertyChangedEventHandler_Impl for AutomationPropertyChangedEventHandler { + fn HandlePropertyChangedEvent(&self, sender: Option<&IUIAutomationElement>, propertyid: UIA_PROPERTY_ID, newvalue: &windows_core::VARIANT) -> windows_core::Result<()> { + if let Some(e) = sender { + let element = UIElement::from(e); + let value = Variant::from(newvalue); + let handler = &self.handler; + handler(&element, propertyid.into(), value).map_err(|e| e.into()) + } else { + Ok(()) + } + } +} + +impl From> for AutomationPropertyChangedEventHandler { + fn from(handler: Box) -> Self { + Self { + handler + } + } +} + +#[implement(IUIAutomationStructureChangedEventHandler)] +pub struct AutomationStructureChangedEventHandler { + handler: Box +} + +impl IUIAutomationStructureChangedEventHandler_Impl for AutomationStructureChangedEventHandler { + fn HandleStructureChangedEvent(&self, sender: Option<&IUIAutomationElement>, changetype: windows::Win32::UI::Accessibility::StructureChangeType, runtimeid: *const windows::Win32::System::Com::SAFEARRAY) -> windows_core::Result<()> { + if let Some(e) = sender { + let handler = &self.handler; + let element = UIElement::from(e); + let arr = SafeArray::from(runtimeid); + let ret = if arr.is_null() { + handler(&element, changetype.into(), None) + } else { + let runtime_id: Vec = match arr.try_into() { + Ok(arr) => arr, + Err(e) => return Err(e.into()) + }; + handler(&element, changetype.into(), Some(&runtime_id)) + }; + ret.map_err(|e| e.into()) + } else { + Ok(()) + } + } +} + +impl From> for AutomationStructureChangedEventHandler { + fn from(handler: Box) -> Self { + Self { + handler + } + } +} + +#[implement(IUIAutomationFocusChangedEventHandler)] +pub struct AutomationFocusChangedEventHandler { + handler: Box +} + +impl IUIAutomationFocusChangedEventHandler_Impl for AutomationFocusChangedEventHandler { + fn HandleFocusChangedEvent(&self, sender: Option<&IUIAutomationElement>) -> windows_core::Result<()> { + if let Some(e) = sender { + let element = UIElement::from(e); + let handler = &self.handler; + handler(&element).map_err(|e| e.into()) + } else { + Ok(()) + } + } +} + +impl From> for AutomationFocusChangedEventHandler { + fn from(handler: Box) -> Self { + Self { + handler + } + } +} diff --git a/crates/uiautomation/src/events/handlers.rs b/crates/uiautomation/src/events/handlers.rs new file mode 100644 index 0000000..43622d5 --- /dev/null +++ b/crates/uiautomation/src/events/handlers.rs @@ -0,0 +1,128 @@ +use windows::Win32::UI::Accessibility::IUIAutomationElement; +use windows::Win32::UI::Accessibility::IUIAutomationEventHandler; +use windows::Win32::UI::Accessibility::IUIAutomationEventHandler_Impl; +use windows::Win32::UI::Accessibility::IUIAutomationFocusChangedEventHandler; +use windows::Win32::UI::Accessibility::IUIAutomationFocusChangedEventHandler_Impl; +use windows::Win32::UI::Accessibility::IUIAutomationPropertyChangedEventHandler; +use windows::Win32::UI::Accessibility::IUIAutomationPropertyChangedEventHandler_Impl; +use windows::Win32::UI::Accessibility::IUIAutomationStructureChangedEventHandler; +use windows::Win32::UI::Accessibility::IUIAutomationStructureChangedEventHandler_Impl; +use windows::Win32::UI::Accessibility::UIA_EVENT_ID; +use windows::Win32::UI::Accessibility::UIA_PROPERTY_ID; +use windows_core::implement; + +use crate::variants::SafeArray; +use crate::variants::Variant; +use crate::UIElement; + +use super::CustomEventHandler; +use super::CustomFocusChangedEventHandler; +use super::CustomPropertyChangedEventHandler; +use super::CustomStructureChangedEventHandler; + +#[implement(IUIAutomationEventHandler)] +pub struct AutomationEventHandler { + handler: Box +} + +impl IUIAutomationEventHandler_Impl for AutomationEventHandler { + fn HandleAutomationEvent(&self, sender: Option<&IUIAutomationElement>, eventid: UIA_EVENT_ID) -> windows::core::Result<()> { + if let Some(e) = sender { + let element = UIElement::from(e); + self.handler.handle(&element, eventid.into()).map_err(|e| e.into()) + } else { + Ok(()) + } + } +} + +impl From for AutomationEventHandler where T: CustomEventHandler + 'static { + fn from(value: T) -> Self { + Self { + handler: Box::new(value) + } + } +} + +#[implement(IUIAutomationPropertyChangedEventHandler)] +pub struct AutomationPropertyChangedHandler { + handler: Box +} + +impl IUIAutomationPropertyChangedEventHandler_Impl for AutomationPropertyChangedHandler { + fn HandlePropertyChangedEvent(&self, sender: Option<&IUIAutomationElement>, propertyid: UIA_PROPERTY_ID, newvalue: &windows::core::VARIANT) -> windows::core::Result<()> { + if let Some(e) = sender { + let element = UIElement::from(e); + let value = Variant::from(newvalue); + self.handler.handle(&element, propertyid.into(), value).map_err(|e| e.into()) + } else { + Ok(()) + } + } +} + +impl From for AutomationPropertyChangedHandler where T: CustomPropertyChangedEventHandler + 'static { + fn from(value: T) -> Self { + Self { + handler: Box::new(value) + } + } +} + +#[implement(IUIAutomationStructureChangedEventHandler)] +pub struct AutomationStructureChangedEventHandler { + handler: Box +} + +impl IUIAutomationStructureChangedEventHandler_Impl for AutomationStructureChangedEventHandler { + fn HandleStructureChangedEvent(&self, sender: Option<&IUIAutomationElement>, changetype: windows::Win32::UI::Accessibility::StructureChangeType, runtimeid: *const windows::Win32::System::Com::SAFEARRAY) -> windows_core::Result<()> { + if let Some(e) = sender { + let element = UIElement::from(e); + let arr = SafeArray::from(runtimeid); + let ret = if arr.is_null() { + self.handler.handle(&element, changetype.into(), None) + } else { + let runtime_id: Vec = match arr.try_into() { + Ok(arr) => arr, + Err(e) => return Err(e.into()) + }; + self.handler.handle(&element, changetype.into(), Some(&runtime_id)) + }; + ret.map_err(|e| e.into()) + } else { + Ok(()) + } + } +} + +impl From for AutomationStructureChangedEventHandler where T: CustomStructureChangedEventHandler + 'static { + fn from(value: T) -> Self { + Self { + handler: Box::new(value) + } + } +} + +#[implement(IUIAutomationFocusChangedEventHandler)] +pub struct AutomationFocusChangedEventHandler { + handler: Box +} + +impl IUIAutomationFocusChangedEventHandler_Impl for AutomationFocusChangedEventHandler { + fn HandleFocusChangedEvent(&self, sender: Option<&IUIAutomationElement>) -> windows_core::Result<()> { + if let Some(e) = sender { + let element = UIElement::from(e); + self.handler.handle(&element).map_err(|e| e.into()) + } else { + Ok(()) + } + } +} + +impl From for AutomationFocusChangedEventHandler where T: CustomFocusChangedEventHandler + 'static { + fn from(value: T) -> Self { + Self { + handler: Box::new(value) + } + } +} diff --git a/crates/uiautomation/src/events/mod.rs b/crates/uiautomation/src/events/mod.rs new file mode 100644 index 0000000..0fca843 --- /dev/null +++ b/crates/uiautomation/src/events/mod.rs @@ -0,0 +1,501 @@ +mod handlers; +mod functions; + +use uiautomation_derive::map_as; +use uiautomation_derive::EnumConvert; +use windows::Win32::UI::Accessibility::IUIAutomationEventHandler; +use windows::Win32::UI::Accessibility::IUIAutomationFocusChangedEventHandler; +use windows::Win32::UI::Accessibility::IUIAutomationPropertyChangedEventHandler; +use windows::Win32::UI::Accessibility::IUIAutomationStructureChangedEventHandler; +use windows_core::Param; + +use crate::types::StructureChangeType; +use crate::types::UIProperty; +use crate::variants::SafeArray; +use crate::variants::Variant; +use crate::Result; +use crate::UIElement; + +/// `UIEventType` is an enum wrapper for `windows::Win32::UI::Accessibility::UIA_EVENT_ID`. +/// +/// Describes the named constants used to identify Microsoft UI Automation events. +#[repr(i32)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumConvert)] +#[map_as(windows::Win32::UI::Accessibility::UIA_EVENT_ID)] +#[allow(non_camel_case_types)] +pub enum UIEventType { + /// Identifies the event that is raised when a tooltip is opened. + ToolTipOpened = 20000i32, + /// Identifies the event that is raised when a tooltip is closed. + ToolTipClosed = 20001i32, + /// Identifies the event that is raised when the UI Automation tree structure is changed. + StructureChanged = 20002i32, + /// Identifies the event that is raised when a menu is opened. + MenuOpened = 20003i32, + /// Identifies the event that is raised when the value of a property has changed. + AutomationPropertyChanged = 20004i32, + /// Identifies the event that is raised when the focus has changed from one element to another. + AutomationFocusChanged = 20005i32, + /// Identifies the event that is raised when asynchronous content is being loaded. This event is used mainly by providers to indicate that asynchronous content-loading events have occurred. + AsyncContentLoaded = 20006i32, + /// Identifies the event that is raised when a menu is closed. + MenuClosed = 20007i32, + /// Identifies the event that is raised when the layout of child items within a control has changed. This event is also used for Auto-suggest accessibility. + LayoutInvalidated = 20008i32, + /// Identifies the event that is raised when a control is invoked or activated. + Invoke_Invoked = 20009i32, + /// Identifies the event raised when an item is added to a collection of selected items. + SelectionItem_ElementAddedToSelection = 20010i32, + /// Identifies the event raised when an item is removed from a collection of selected items. + SelectionItem_ElementRemovedFromSelection = 20011i32, + /// Identifies the event that is raised when a call to the Select, AddToSelection, or RemoveFromSelection method results in a single item being selected. + SelectionItem_ElementSelected = 20012i32, + /// Identifies the event that is raised when a selection in a container has changed significantly. + Selection_Invalidated = 20013i32, + /// Identifies the event that is raised when the text selection is modified. + Text_TextSelectionChanged = 20014i32, + /// Identifies the event that is raised whenever textual content is modified. + Text_TextChanged = 20015i32, + /// Identifies the event that is raised when a window is opened. + Window_WindowOpened = 20016i32, + /// Identifies the event that is raised when a window is closed. + Window_WindowClosed = 20017i32, + /// Identifies the event that is raised when a menu mode is started. + MenuModeStart = 20018i32, + /// Identifies the event that is raised when a menu mode is ended. + MenuModeEnd = 20019i32, + /// Identifies the event that is raised when the specified mouse or keyboard input reaches the element for which the StartListening method was called. + InputReachedTarget = 20020i32, + /// Identifies the event that is raised when the specified input reached an element other than the element for which the StartListening method was called. + InputReachedOtherElement = 20021i32, + /// Identifies the event that is raised when the specified input was discarded or otherwise failed to reach any element. + InputDiscarded = 20022i32, + /// Identifies the event that is raised when a provider issues a system alert. + /// + /// Supported starting with Windows 8. + SystemAlert = 20023i32, + /// Identifies the event that is raised when the content of a live region has changed. + /// + /// Supported starting with Windows 8. + LiveRegionChanged = 20024i32, + /// Identifies the event that is raised when a change is made to the root node of a UI Automation fragment that is hosted in another element. + /// + /// Supported starting with Windows 8. + HostedFragmentRootsInvalidated = 20025i32, + /// Identifies the event that is raised when the user starts to drag an element. This event is raised by the element being dragged. + /// + /// Supported starting with Windows 8. + Drag_DragStart = 20026i32, + /// Identifies the event that is raised when the user ends a drag operation before dropping an element on a drop target. This event is raised by the element being dragged. + /// + /// Supported starting with Windows 8. + Drag_DragCancel = 20027i32, + /// Identifies the event that is raised when the user drops an element on a drop target. This event is raised by the element being dragged. + /// Supported starting with Windows 8. + Drag_DragComplete = 20028i32, + /// Identifies the event that is raised when the user drags an element into a drop target's boundary. This event is raised by the drop target element. + /// + /// Supported starting with Windows 8. + DropTarget_DragEnter = 20029i32, + /// Identifies the event that is raised when the user drags an element out of a drop target's boundary. This event is raised by the drop target element. + /// + /// Supported starting with Windows 8. + DropTarget_DragLeave = 20030i32, + /// Identifies the event that is raised when the user drops an element on a drop target. This event is raised by the drop target element. + /// + /// Supported starting with Windows 8. + DropTarget_Dropped = 20031i32, + /// Identifies the event that is raised whenever text auto-correction is performed by a control. + /// + /// Supported starting with Windows 8.1. + TextEdit_TextChanged = 20032i32, + /// Identifies the event that is raised whenever a composition replacement is performed by a control. + /// + /// Supported starting with Windows 8.1. + TextEdit_ConversionTargetChanged = 20033i32, + /// Identifies the event that is raised when a provider calls the UiaRaiseChangesEvent function. + Changes = 20034i32, + /// Identifies the event that is raised when a provider calls the UiaRaiseNotificationEvent method. + Notification = 20035i32, + /// Identifies the event that is raised when the active text position changes, indicated by a navigation event within or between read-only text elements + /// (such as web browsers, PDF documents, or EPUB documents) using bookmarks (fragment identifiers that refer to a location within a resource). + ActiveTextPositionChanged = 20036i32, +} + +/// A wrapper for windows `IUIAutomationEventHandler` interface. +/// +/// Exposes a method to handle Microsoft UI Automation events. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UIEventHandler { + handler: IUIAutomationEventHandler +} + +impl UIEventHandler { + /// Handles a Microsoft UI Automation event. + pub fn handle_automation_event(&self, sender: &UIElement, event: UIEventType) -> Result<()> { + unsafe { + self.handler.HandleAutomationEvent(sender, event.into())?; + } + Ok(()) + } +} + +impl From for UIEventHandler { + fn from(handler: IUIAutomationEventHandler) -> Self { + Self{ + handler + } + } +} + +impl From<&IUIAutomationEventHandler> for UIEventHandler { + fn from(value: &IUIAutomationEventHandler) -> Self { + value.clone().into() + } +} + +impl Into for UIEventHandler { + fn into(self) -> IUIAutomationEventHandler { + self.handler + } +} + +impl AsRef for UIEventHandler { + fn as_ref(&self) -> &IUIAutomationEventHandler { + &self.handler + } +} + +impl Param for UIEventHandler { + unsafe fn param(self) -> windows::core::ParamValue { + // windows::core::ParamValue::Owned(self.handler) + self.handler.param() + } +} + +impl Param for &UIEventHandler { + unsafe fn param(self) -> windows::core::ParamValue { + // windows::core::ParamValue::Borrowed(self.handler.as_raw()) + (&self.handler).param() + } +} + +/// A wrapper for windows `IUIAutomationPropertyChangedEventHandler` interface. +/// +/// Exposes a method to handle Microsoft UI Automation events that occur when a property is changed. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UIPropertyChangedEventHandler { + handler: IUIAutomationPropertyChangedEventHandler +} + +impl UIPropertyChangedEventHandler { + /// Handles a Microsoft UI Automation property-changed event. + pub fn handle_property_changed_event(&self, sender: &UIElement, property_id: UIProperty, new_value: Variant) -> Result<()> { + unsafe { + self.handler.HandlePropertyChangedEvent(sender, property_id.into(), new_value)? + }; + Ok(()) + } +} + +impl From for UIPropertyChangedEventHandler { + fn from(handler: IUIAutomationPropertyChangedEventHandler) -> Self { + Self { + handler + } + } +} + +impl From<&IUIAutomationPropertyChangedEventHandler> for UIPropertyChangedEventHandler { + fn from(value: &IUIAutomationPropertyChangedEventHandler) -> Self { + value.clone().into() + } +} + +impl Into for UIPropertyChangedEventHandler { + fn into(self) -> IUIAutomationPropertyChangedEventHandler { + self.handler + } +} + +impl AsRef for UIPropertyChangedEventHandler { + fn as_ref(&self) -> &IUIAutomationPropertyChangedEventHandler { + &self.handler + } +} + +impl Param for UIPropertyChangedEventHandler { + unsafe fn param(self) -> windows::core::ParamValue { + self.handler.param() + } +} + +impl Param for &UIPropertyChangedEventHandler { + unsafe fn param(self) -> windows::core::ParamValue { + (&self.handler).param() + } +} + +/// A wrapper for windows `IUIAutomationStructureChangedEventHandler` interface. +/// +/// Handles an event that is raised when the Microsoft UI Automation tree structure has changed. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UIStructureChangeEventHandler { + handler: IUIAutomationStructureChangedEventHandler +} + +impl UIStructureChangeEventHandler { + /// Handles an event that is raised when the Microsoft UI Automation tree structure has changed. + pub fn handle_structure_changed_event(&self, sender: &UIElement, change_type: StructureChangeType, runtime_id: Option<&[i32]>) -> Result<()> { + let runtime_id = if let Some(arr) = runtime_id { + arr.try_into()? + } else { + SafeArray::default() + }; + + unsafe { + self.handler.HandleStructureChangedEvent(sender, change_type.into(), runtime_id.get_array())? + } + + Ok(()) + } +} + +impl From for UIStructureChangeEventHandler { + fn from(handler: IUIAutomationStructureChangedEventHandler) -> Self { + Self { + handler + } + } +} + +impl From<&IUIAutomationStructureChangedEventHandler> for UIStructureChangeEventHandler { + fn from(value: &IUIAutomationStructureChangedEventHandler) -> Self { + value.clone().into() + } +} + +impl Into for UIStructureChangeEventHandler { + fn into(self) -> IUIAutomationStructureChangedEventHandler { + self.handler + } +} + +impl AsRef for UIStructureChangeEventHandler { + fn as_ref(&self) -> &IUIAutomationStructureChangedEventHandler { + &self.handler + } +} + +impl Param for UIStructureChangeEventHandler { + unsafe fn param(self) -> windows::core::ParamValue { + self.handler.param() + } +} + +impl Param for &UIStructureChangeEventHandler { + unsafe fn param(self) -> windows::core::ParamValue { + (&self.handler).param() + } +} + +/// A wrapper for windows `IUIAutomationFocusChangedEventHandler` interface. +/// +/// Exposes a method to handle events that are raised when the keyboard focus moves to another UI Automation element. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UIFocusChangedEventHandler { + handler: IUIAutomationFocusChangedEventHandler +} + +impl UIFocusChangedEventHandler { + /// Handles the event raised when the keyboard focus moves to a different UI Automation element. + pub fn handle_focus_changed_event(&self, sender: &UIElement) -> Result<()> { + unsafe { + self.handler.HandleFocusChangedEvent(sender)? + }; + Ok(()) + } +} + +impl From for UIFocusChangedEventHandler { + fn from(handler: IUIAutomationFocusChangedEventHandler) -> Self { + Self { + handler + } + } +} + +impl From<&IUIAutomationFocusChangedEventHandler> for UIFocusChangedEventHandler { + fn from(value: &IUIAutomationFocusChangedEventHandler) -> Self { + value.clone().into() + } +} + +impl Into for UIFocusChangedEventHandler { + fn into(self) -> IUIAutomationFocusChangedEventHandler { + self.handler + } +} + +impl AsRef for UIFocusChangedEventHandler { + fn as_ref(&self) -> &IUIAutomationFocusChangedEventHandler { + &self.handler + } +} + +impl Param for UIFocusChangedEventHandler { + unsafe fn param(self) -> windows::core::ParamValue { + self.handler.param() + } +} + +impl Param for &UIFocusChangedEventHandler { + unsafe fn param(self) -> windows::core::ParamValue { + (&self.handler).param() + } +} + +/// Defines a custom handler for `IUIAutomationEventHandler`. +pub trait CustomEventHandler { + fn handle(&self, sender: &UIElement, event_type: UIEventType) -> Result<()>; +} + +impl From for UIEventHandler where T: CustomEventHandler + 'static { + fn from(value: T) -> Self { + let handler = handlers::AutomationEventHandler::from(value); + let handler: IUIAutomationEventHandler = handler.into(); + handler.into() + } +} + +/// Defines a custom handler function for `IUIAutomationEventHandler`. +pub type CustomEventHandlerFn = dyn Fn(&UIElement, UIEventType) -> Result<()>; + +impl From> for UIEventHandler { + fn from(value: Box) -> Self { + let handler = functions::AutomationEventHandler::from(value); + let handler: IUIAutomationEventHandler = handler.into(); + handler.into() + } +} + +/// Defines a custom handler for `IUIAutomationPropertyChangedEventHandler`. +pub trait CustomPropertyChangedEventHandler { + fn handle(&self, sender: &UIElement, property: UIProperty, new_value: Variant) -> Result<()>; +} + +impl From for UIPropertyChangedEventHandler where T: CustomPropertyChangedEventHandler + 'static { + fn from(value: T) -> Self { + let handler = handlers::AutomationPropertyChangedHandler::from(value); + let handler: IUIAutomationPropertyChangedEventHandler = handler.into(); + handler.into() + } +} + +/// Defines a custom handler function for `IUIAutomationPropertyChangedEventHandler`. +pub type CustomPropertyChangedEventHandlerFn = dyn Fn(&UIElement, UIProperty, Variant) -> Result<()>; + +impl From> for UIPropertyChangedEventHandler { + fn from(value: Box) -> Self { + let handler = functions::AutomationPropertyChangedEventHandler::from(value); + let handler: IUIAutomationPropertyChangedEventHandler = handler.into(); + handler.into() + } +} + +/// Defines a custom handler for `IUIAutomationStructureChangedEventHandler`. +pub trait CustomStructureChangedEventHandler { + fn handle(&self, sender: &UIElement, change_type: StructureChangeType, runtime_id: Option<&[i32]>) -> Result<()>; +} + +impl From for UIStructureChangeEventHandler where T: CustomStructureChangedEventHandler + 'static { + fn from(value: T) -> Self { + let handler = handlers::AutomationStructureChangedEventHandler::from(value); + let handler: IUIAutomationStructureChangedEventHandler = handler.into(); + handler.into() + } +} + +/// Defines a custom handler function for `IUIAutomationStructureChangedEventHandler`. +pub type CustomStructureChangedEventHandlerFn = dyn Fn(&UIElement, StructureChangeType, Option<&[i32]>) -> Result<()>; + +impl From> for UIStructureChangeEventHandler { + fn from(value: Box) -> Self { + let handler = functions::AutomationStructureChangedEventHandler::from(value); + let handler: IUIAutomationStructureChangedEventHandler = handler.into(); + handler.into() + } +} + +/// Defines a custom handler for `IUIAutomationFocusChangedEventHandler`. +pub trait CustomFocusChangedEventHandler { + fn handle(&self, sender: &UIElement) -> Result<()>; +} + +impl From for UIFocusChangedEventHandler where T: CustomFocusChangedEventHandler + 'static { + fn from(value: T) -> Self { + let handler = handlers::AutomationFocusChangedEventHandler::from(value); + let handler: IUIAutomationFocusChangedEventHandler = handler.into(); + handler.into() + } +} + +/// Defines a custom handler function for `IUIAutomationFocusChangedEventHandler`. +pub type CustomFocusChangedEventHandlerFn = dyn Fn(&UIElement) -> Result<()>; + +impl From> for UIFocusChangedEventHandler { + fn from(value: Box) -> Self { + let handler = functions::AutomationFocusChangedEventHandler::from(value); + let handler: IUIAutomationFocusChangedEventHandler = handler.into(); + handler.into() + } +} + +#[cfg(test)] +mod tests { + use windows::Win32::UI::Accessibility::UIA_DropTarget_DroppedEventId; + + use crate::types::TreeScope; + use crate::UIAutomation; + + use super::CustomEventHandler; + use super::CustomEventHandlerFn; + use super::UIEventHandler; + use super::UIEventType; + + #[test] + fn test_uievent_types() { + let t = UIEventType::try_from(UIA_DropTarget_DroppedEventId.0).unwrap(); + assert_eq!(t, UIEventType::DropTarget_Dropped); + } + + struct MyEventHandler { + } + + impl CustomEventHandler for MyEventHandler { + fn handle(&self, sender: &crate::UIElement, event_type: UIEventType) -> crate::Result<()> { + println!("event: {:?}, element: {}", event_type, sender); + Ok(()) + } + } + + #[test] + fn test_event_handler_trait() { + let automation = UIAutomation::new().unwrap(); + + let root = automation.get_root_element().unwrap(); + + let handler = UIEventHandler::from(MyEventHandler {}); + automation.add_automation_event_handler(UIEventType::TextEdit_TextChanged, &root, TreeScope::Subtree, None, &handler).unwrap(); + automation.remove_automation_event_handler(UIEventType::TextEdit_TextChanged, &root, &handler).unwrap(); + + let handle_fn: Box = Box::new(|sender, event_type| { + println!("event: {:?}, element: {}", event_type, sender); + Ok(()) + }); + + let handler = UIEventHandler::from(handle_fn); + automation.add_automation_event_handler(UIEventType::Text_TextChanged, &root, TreeScope::Subtree, None, &handler).unwrap(); + automation.remove_automation_event_handler(UIEventType::Text_TextChanged, &root, &handler).unwrap(); + } +} \ No newline at end of file diff --git a/crates/uiautomation/src/lib.rs b/crates/uiautomation/src/lib.rs index 8c8a115..ff01342 100644 --- a/crates/uiautomation/src/lib.rs +++ b/crates/uiautomation/src/lib.rs @@ -9,6 +9,7 @@ pub mod actions; pub mod inputs; pub mod processes; pub mod dialogs; +pub mod events; pub use self::errors::Error; pub use self::errors::Result; diff --git a/crates/uiautomation/src/patterns.rs b/crates/uiautomation/src/patterns.rs index decabbe..f7ecf81 100644 --- a/crates/uiautomation/src/patterns.rs +++ b/crates/uiautomation/src/patterns.rs @@ -121,7 +121,7 @@ pub enum UIPatternType { /// Identifies the Annotation control pattern. Supported starting with Windows 8. Annotation = 10023i32, /// Identifies the second version of the Text control pattern. Supported starting with Windows 8. - TextP = 10024i32, + Text2 = 10024i32, /// Identifies the Styles control pattern. Supported starting with Windows 8. Styles = 10025i32, /// Identifies the Spreadsheet control pattern. Supported starting with Windows 8. @@ -129,7 +129,7 @@ pub enum UIPatternType { /// Identifies the SpreadsheetItem control pattern. Supported starting with Windows 8. SpreadsheetItem = 10027i32, /// Identifies the second version of the Transform control pattern. Supported starting with Windows 8. - TransformP = 10028i32, + Transform2 = 10028i32, /// Identifies the TextChild control pattern. Supported starting with Windows 8. TextChild = 10029i32, /// Identifies the Drag control pattern. Supported starting with Windows 8. @@ -142,18 +142,6 @@ pub enum UIPatternType { CustomNavigation = 10033i32 } -// impl From for UIPatternType { -// fn from(value: windows::Win32::UI::Accessibility::UIA_PATTERN_ID) -> Self { -// value.0.try_into().unwrap() -// } -// } - -// impl Into for UIPatternType { -// fn into(self) -> windows::Win32::UI::Accessibility::UIA_PATTERN_ID { -// windows::Win32::UI::Accessibility::UIA_PATTERN_ID(self as _) -// } -// } - /// `UIPattern` is the wrapper trait for patterns. pub trait UIPattern { /// Defines the pattern type id. diff --git a/crates/uiautomation/src/processes.rs b/crates/uiautomation/src/processes.rs index 47a88fb..f682bdc 100644 --- a/crates/uiautomation/src/processes.rs +++ b/crates/uiautomation/src/processes.rs @@ -145,14 +145,6 @@ impl Process { } } - // /// Exit the process with `exit_code` by force. - // pub fn terminate(&self, exit_code: u32) -> Result<()> { - // unsafe { - // TerminateProcess(self.proc_info.hProcess, exit_code)? - // }; - // Ok(()) - // } - /// Wait for the process to exit. /// /// `timeout` is the milliseconds to wait for. diff --git a/crates/uiautomation/src/types.rs b/crates/uiautomation/src/types.rs index 422d9ac..a18f3d2 100644 --- a/crates/uiautomation/src/types.rs +++ b/crates/uiautomation/src/types.rs @@ -1094,6 +1094,31 @@ pub enum ElementMode { Full = 1i32 } +/// `StructureChangeType` is an enum wrapper for `windows::Win32::UI::Accessibility::StructureChangeType`. +/// +/// Contains values that specify the type of change in the Microsoft UI Automation tree structure. +#[repr(i32)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumConvert)] +#[map_as(windows::Win32::UI::Accessibility::StructureChangeType)] +pub enum StructureChangeType { + /// A child element was added to the UI Automation element tree. + ChildAdded = 0i32, + /// A child element was removed from the UI Automation element tree. + ChildRemoved = 1i32, + /// Child elements were invalidated in the UI Automation element tree. + /// This might mean that one or more child elements were added or removed, or a combination of both. + /// This value can also indicate that one subtree in the UI was substituted for another. + /// For example, the entire contents of a dialog box changed at once, or the view of a list changed because an Explorer-type application navigated to another location. + /// The exact meaning depends on the UI Automation provider implementation. + ChildrenInvalidated = 2i32, + /// Child elements were added in bulk to the UI Automation element tree. + ChildrenBulkAdded = 3i32, + /// Child elements were removed in bulk from the UI Automation element tree. + ChildrenBulkRemoved = 4i32, + /// The order of child elements has changed in the UI Automation element tree. Child elements may or may not have been added or removed. + ChildrenReordered = 5i32 +} + #[cfg(test)] mod tests { use windows::Win32::Foundation::HWND; diff --git a/crates/uiautomation/src/variants.rs b/crates/uiautomation/src/variants.rs index c180794..7d84a31 100644 --- a/crates/uiautomation/src/variants.rs +++ b/crates/uiautomation/src/variants.rs @@ -351,6 +351,12 @@ impl From for Variant { } } +impl From<&VARIANT> for Variant { + fn from(value: &VARIANT) -> Self { + value.clone().into() + } +} + impl Into for Variant { fn into(self) -> VARIANT { self.value @@ -1275,6 +1281,11 @@ impl SafeArray { } } + /// Determines whether the array is null. + pub fn is_null(&self) -> bool { + self.array.is_null() + } + /// Creates a vector array. pub fn new_vector(var_type: VARENUM, len: u32) -> Result { unsafe { @@ -1417,6 +1428,20 @@ impl SafeArray { /// Creates a `SafeArray` value from `Vec`. pub fn from_vector(var_type: VARENUM, src: &Vec) -> Result { + // let arr = Self::new_vector(var_type, src.len() as _)?; + // for i in 0..src.len() { + // let indices: [i32; 1] = [i as _]; + // let v_ref: *const T = &src[i]; + // unsafe { + // SafeArrayPutElement(arr.array, indices.as_ptr(), v_ref as _)? + // }; + // }; + // Ok(arr) + Self::from_slice(var_type, &src) + } + + /// Creates a `SafeArray` value from `[T]`. + pub fn from_slice(var_type: VARENUM, src: &[T]) -> Result { let arr = Self::new_vector(var_type, src.len() as _)?; for i in 0..src.len() { let indices: [i32; 1] = [i as _]; @@ -1435,26 +1460,17 @@ impl SafeArray { } } +impl Default for SafeArray { + fn default() -> Self { + Self { + array: null_mut(), + owned: false + } + } +} + impl From<*mut imp::SAFEARRAY> for SafeArray { fn from(value: *mut imp::SAFEARRAY) -> Self { - // let mut array = unsafe { - // SAFEARRAY { - // cDims: (*value).cDims, - // fFeatures: ADVANCED_FEATURE_FLAGS((*value).fFeatures), - // cbElements: (*value).cbElements, - // cLocks: (*value).cLocks, - // pvData: (*value).pvData, - // rgsabound: [SAFEARRAYBOUND { - // cElements: (*value).rgsabound[0].cElements, - // lLbound: (*value).rgsabound[0].lLbound - // }] - // } - // }; - - // Self { - // array: &mut array as *mut SAFEARRAY, - // owned: true - // } let array: *mut SAFEARRAY = unsafe { std::mem::transmute(value) }; @@ -1465,24 +1481,20 @@ impl From<*mut imp::SAFEARRAY> for SafeArray { } } +impl From<*const imp::SAFEARRAY> for SafeArray { + fn from(value: *const imp::SAFEARRAY) -> Self { + let array: *mut SAFEARRAY = unsafe { + std::mem::transmute(value) + }; + Self { + array, + owned: false + } + } +} + impl Into<*mut imp::SAFEARRAY> for SafeArray { fn into(mut self) -> *mut imp::SAFEARRAY { - // let value = self.array; - // let mut array = unsafe { - // imp::SAFEARRAY { - // cDims: (*value).cDims, - // fFeatures: (*value).fFeatures.0, - // cbElements: (*value).cbElements, - // cLocks: (*value).cLocks, - // pvData: (*value).pvData, - // rgsabound: [imp::SAFEARRAYBOUND { - // cElements: (*value).rgsabound[0].cElements, - // lLbound: (*value).rgsabound[0].lLbound - // }] - // } - // }; - // self.owned = false; - // &mut array as *mut imp::SAFEARRAY self.owned = false; unsafe { std::mem::transmute(self.array) @@ -1490,6 +1502,7 @@ impl Into<*mut imp::SAFEARRAY> for SafeArray { } } + impl From<*mut SAFEARRAY> for SafeArray { fn from(array: *mut SAFEARRAY) -> Self { Self { @@ -1499,6 +1512,25 @@ impl From<*mut SAFEARRAY> for SafeArray { } } +impl From<*const SAFEARRAY> for SafeArray { + fn from(value: *const SAFEARRAY) -> Self { + let array: *mut SAFEARRAY = unsafe { + std::mem::transmute(value) + }; + Self { + array, + owned: false + } + } +} + +impl Into<*mut SAFEARRAY> for SafeArray { + fn into(mut self) -> *mut SAFEARRAY { + self.owned = false; + self.array + } +} + macro_rules! fmt_safe_array { ($vec_type:ty, $self:ident, $f:ident) => { { @@ -1573,333 +1605,397 @@ impl Drop for SafeArray { } } -impl TryFrom<&Vec> for SafeArray { - type Error = Error; +macro_rules! define_array_convertor { + ($val_type: ty, $var_type: ident) => { + impl TryFrom<&Vec<$val_type>> for SafeArray { + type Error = Error; - fn try_from(value: &Vec) -> Result { - Self::from_vector(VT_I1, value) - } -} + fn try_from(value: &Vec<$val_type>) -> Result { + Self::from_vector($var_type, value) + } + } -impl TryFrom> for SafeArray { - type Error = Error; + impl TryFrom> for SafeArray { + type Error = Error; - fn try_from(value: Vec) -> Result { - Self::from_vector(VT_I1, &value) - } -} + fn try_from(value: Vec<$val_type>) -> Result { + Self::from_vector($var_type, &value) + } + } -impl TryInto> for &SafeArray { - type Error = Error; + impl TryFrom<&[$val_type]> for SafeArray { + type Error = Error; - fn try_into(self) -> Result> { - self.into_vector(VT_I1) - } -} + fn try_from(value: &[$val_type]) -> Result { + Self::from_slice($var_type, value) + } + } -impl TryInto> for SafeArray { - type Error = Error; + impl TryInto> for SafeArray { + type Error = Error; - fn try_into(self) -> Result> { - self.into_vector(VT_I1) - } -} + fn try_into(self) -> Result> { + self.into_vector($var_type) + } + } -impl TryFrom<&Vec> for SafeArray { - type Error = Error; + impl TryInto> for &SafeArray { + type Error = Error; - fn try_from(value: &Vec) -> Result { - Self::from_vector(VT_I2, value) - } + fn try_into(self) -> Result> { + self.into_vector($var_type) + } + } + }; } -impl TryFrom> for SafeArray { - type Error = Error; +define_array_convertor!(i8, VT_I1); - fn try_from(value: Vec) -> Result { - Self::from_vector(VT_I2, &value) - } -} +// impl TryFrom<&Vec> for SafeArray { +// type Error = Error; -impl TryInto> for &SafeArray { - type Error = Error; +// fn try_from(value: &Vec) -> Result { +// Self::from_vector(VT_I1, value) +// } +// } - fn try_into(self) -> Result> { - self.into_vector(VT_I2) - } -} +// impl TryFrom> for SafeArray { +// type Error = Error; -impl TryInto> for SafeArray { - type Error = Error; +// fn try_from(value: Vec) -> Result { +// Self::from_vector(VT_I1, &value) +// } +// } - fn try_into(self) -> Result> { - self.into_vector(VT_I2) - } -} +// impl TryInto> for &SafeArray { +// type Error = Error; -impl TryFrom<&Vec> for SafeArray { - type Error = Error; +// fn try_into(self) -> Result> { +// self.into_vector(VT_I1) +// } +// } - fn try_from(value: &Vec) -> Result { - Self::from_vector(VT_I4, value) - } -} +// impl TryInto> for SafeArray { +// type Error = Error; -impl TryFrom> for SafeArray { - type Error = Error; +// fn try_into(self) -> Result> { +// self.into_vector(VT_I1) +// } +// } - fn try_from(value: Vec) -> Result { - Self::from_vector(VT_I4, &value) - } -} +define_array_convertor!(i16, VT_I2); -impl TryInto> for &SafeArray { - type Error = Error; +// impl TryFrom<&Vec> for SafeArray { +// type Error = Error; - fn try_into(self) -> Result> { - if self.get_var_type()? == VT_INT { - self.into_vector(VT_INT) - } else { - self.into_vector(VT_I4) - } - } -} +// fn try_from(value: &Vec) -> Result { +// Self::from_vector(VT_I2, value) +// } +// } -impl TryInto> for SafeArray { - type Error = Error; +// impl TryFrom> for SafeArray { +// type Error = Error; - fn try_into(self) -> Result> { - (&self).try_into() - } -} +// fn try_from(value: Vec) -> Result { +// Self::from_vector(VT_I2, &value) +// } +// } -impl TryFrom<&Vec> for SafeArray { - type Error = Error; +// impl TryInto> for &SafeArray { +// type Error = Error; - fn try_from(value: &Vec) -> Result { - Self::from_vector(VT_I8, value) - } -} +// fn try_into(self) -> Result> { +// self.into_vector(VT_I2) +// } +// } -impl TryFrom> for SafeArray { - type Error = Error; +// impl TryInto> for SafeArray { +// type Error = Error; - fn try_from(value: Vec) -> Result { - Self::from_vector(VT_I8, &value) - } -} +// fn try_into(self) -> Result> { +// self.into_vector(VT_I2) +// } +// } -impl TryInto> for &SafeArray { - type Error = Error; +define_array_convertor!(i32, VT_I4); - fn try_into(self) -> Result> { - self.into_vector(VT_I8) - } -} +// impl TryFrom<&Vec> for SafeArray { +// type Error = Error; -impl TryInto> for SafeArray { - type Error = Error; +// fn try_from(value: &Vec) -> Result { +// Self::from_vector(VT_I4, value) +// } +// } - fn try_into(self) -> Result> { - self.into_vector(VT_I8) - } -} +// impl TryFrom> for SafeArray { +// type Error = Error; -impl TryFrom<&Vec> for SafeArray { - type Error = Error; +// fn try_from(value: Vec) -> Result { +// Self::from_vector(VT_I4, &value) +// } +// } - fn try_from(value: &Vec) -> Result { - Self::from_vector(VT_UI1, value) - } -} +// impl TryInto> for &SafeArray { +// type Error = Error; -impl TryFrom> for SafeArray { - type Error = Error; +// fn try_into(self) -> Result> { +// if self.get_var_type()? == VT_INT { +// self.into_vector(VT_INT) +// } else { +// self.into_vector(VT_I4) +// } +// } +// } - fn try_from(value: Vec) -> Result { - Self::from_vector(VT_UI1, &value) - } -} +// impl TryInto> for SafeArray { +// type Error = Error; -impl TryInto> for &SafeArray { - type Error = Error; +// fn try_into(self) -> Result> { +// (&self).try_into() +// } +// } - fn try_into(self) -> Result> { - self.into_vector(VT_UI1) - } -} +define_array_convertor!(i64, VT_I8); -impl TryInto> for SafeArray { - type Error = Error; +// impl TryFrom<&Vec> for SafeArray { +// type Error = Error; - fn try_into(self) -> Result> { - self.into_vector(VT_UI1) - } -} +// fn try_from(value: &Vec) -> Result { +// Self::from_vector(VT_I8, value) +// } +// } -impl TryFrom<&Vec> for SafeArray { - type Error = Error; +// impl TryFrom> for SafeArray { +// type Error = Error; - fn try_from(value: &Vec) -> Result { - Self::from_vector(VT_UI2, value) - } -} +// fn try_from(value: Vec) -> Result { +// Self::from_vector(VT_I8, &value) +// } +// } -impl TryFrom> for SafeArray { - type Error = Error; +// impl TryInto> for &SafeArray { +// type Error = Error; - fn try_from(value: Vec) -> Result { - Self::from_vector(VT_UI2, &value) - } -} +// fn try_into(self) -> Result> { +// self.into_vector(VT_I8) +// } +// } -impl TryInto> for &SafeArray { - type Error = Error; +// impl TryInto> for SafeArray { +// type Error = Error; - fn try_into(self) -> Result> { - self.into_vector(VT_UI2) - } -} +// fn try_into(self) -> Result> { +// self.into_vector(VT_I8) +// } +// } -impl TryInto> for SafeArray { - type Error = Error; +define_array_convertor!(u8, VT_UI1); - fn try_into(self) -> Result> { - self.into_vector(VT_UI2) - } -} +// impl TryFrom<&Vec> for SafeArray { +// type Error = Error; -impl TryFrom<&Vec> for SafeArray { - type Error = Error; +// fn try_from(value: &Vec) -> Result { +// Self::from_vector(VT_UI1, value) +// } +// } - fn try_from(value: &Vec) -> Result { - Self::from_vector(VT_UI4, value) - } -} +// impl TryFrom> for SafeArray { +// type Error = Error; -impl TryFrom> for SafeArray { - type Error = Error; +// fn try_from(value: Vec) -> Result { +// Self::from_vector(VT_UI1, &value) +// } +// } - fn try_from(value: Vec) -> Result { - Self::from_vector(VT_UI4, &value) - } -} +// impl TryInto> for &SafeArray { +// type Error = Error; -impl TryInto> for &SafeArray { - type Error = Error; +// fn try_into(self) -> Result> { +// self.into_vector(VT_UI1) +// } +// } - fn try_into(self) -> Result> { - if self.get_var_type()? == VT_UINT { - self.into_vector(VT_UINT) - } else { - self.into_vector(VT_UI4) - } - } -} +// impl TryInto> for SafeArray { +// type Error = Error; -impl TryInto> for SafeArray { - type Error = Error; +// fn try_into(self) -> Result> { +// self.into_vector(VT_UI1) +// } +// } - fn try_into(self) -> Result> { - (&self).try_into() - } -} +define_array_convertor!(u16, VT_UI2); -impl TryFrom<&Vec> for SafeArray { - type Error = Error; +// impl TryFrom<&Vec> for SafeArray { +// type Error = Error; - fn try_from(value: &Vec) -> Result { - Self::from_vector(VT_UI8, value) - } -} +// fn try_from(value: &Vec) -> Result { +// Self::from_vector(VT_UI2, value) +// } +// } -impl TryFrom> for SafeArray { - type Error = Error; +// impl TryFrom> for SafeArray { +// type Error = Error; - fn try_from(value: Vec) -> Result { - Self::from_vector(VT_UI8, &value) - } -} +// fn try_from(value: Vec) -> Result { +// Self::from_vector(VT_UI2, &value) +// } +// } -impl TryInto> for &SafeArray { - type Error = Error; +// impl TryInto> for &SafeArray { +// type Error = Error; - fn try_into(self) -> Result> { - self.into_vector(VT_UI8) - } -} +// fn try_into(self) -> Result> { +// self.into_vector(VT_UI2) +// } +// } -impl TryInto> for SafeArray { - type Error = Error; +// impl TryInto> for SafeArray { +// type Error = Error; - fn try_into(self) -> Result> { - self.into_vector(VT_UI8) - } -} +// fn try_into(self) -> Result> { +// self.into_vector(VT_UI2) +// } +// } -impl TryFrom<&Vec> for SafeArray { - type Error = Error; +define_array_convertor!(u32, VT_UI4); - fn try_from(value: &Vec) -> Result { - Self::from_vector(VT_R4, value) - } -} +// impl TryFrom<&Vec> for SafeArray { +// type Error = Error; -impl TryFrom> for SafeArray { - type Error = Error; +// fn try_from(value: &Vec) -> Result { +// Self::from_vector(VT_UI4, value) +// } +// } - fn try_from(value: Vec) -> Result { - Self::from_vector(VT_R4, &value) - } -} +// impl TryFrom> for SafeArray { +// type Error = Error; -impl TryInto> for &SafeArray { - type Error = Error; +// fn try_from(value: Vec) -> Result { +// Self::from_vector(VT_UI4, &value) +// } +// } - fn try_into(self) -> Result> { - self.into_vector(VT_R4) - } -} +// impl TryInto> for &SafeArray { +// type Error = Error; -impl TryInto> for SafeArray { - type Error = Error; +// fn try_into(self) -> Result> { +// if self.get_var_type()? == VT_UINT { +// self.into_vector(VT_UINT) +// } else { +// self.into_vector(VT_UI4) +// } +// } +// } - fn try_into(self) -> Result> { - self.into_vector(VT_R4) - } -} +// impl TryInto> for SafeArray { +// type Error = Error; -impl TryFrom<&Vec> for SafeArray { - type Error = Error; +// fn try_into(self) -> Result> { +// (&self).try_into() +// } +// } - fn try_from(value: &Vec) -> Result { - Self::from_vector(VT_R8, value) - } -} +define_array_convertor!(u64, VT_UI8); -impl TryFrom> for SafeArray { - type Error = Error; +// impl TryFrom<&Vec> for SafeArray { +// type Error = Error; - fn try_from(value: Vec) -> Result { - Self::from_vector(VT_R8, &value) - } -} +// fn try_from(value: &Vec) -> Result { +// Self::from_vector(VT_UI8, value) +// } +// } -impl TryInto> for &SafeArray { - type Error = Error; +// impl TryFrom> for SafeArray { +// type Error = Error; - fn try_into(self) -> Result> { - self.into_vector(VT_R8) - } -} +// fn try_from(value: Vec) -> Result { +// Self::from_vector(VT_UI8, &value) +// } +// } -impl TryInto> for SafeArray { - type Error = Error; +// impl TryInto> for &SafeArray { +// type Error = Error; - fn try_into(self) -> Result> { - self.into_vector(VT_R8) - } -} +// fn try_into(self) -> Result> { +// self.into_vector(VT_UI8) +// } +// } + +// impl TryInto> for SafeArray { +// type Error = Error; + +// fn try_into(self) -> Result> { +// self.into_vector(VT_UI8) +// } +// } + +define_array_convertor!(f32, VT_R4); + +// impl TryFrom<&Vec> for SafeArray { +// type Error = Error; + +// fn try_from(value: &Vec) -> Result { +// Self::from_vector(VT_R4, value) +// } +// } + +// impl TryFrom> for SafeArray { +// type Error = Error; + +// fn try_from(value: Vec) -> Result { +// Self::from_vector(VT_R4, &value) +// } +// } + +// impl TryInto> for &SafeArray { +// type Error = Error; + +// fn try_into(self) -> Result> { +// self.into_vector(VT_R4) +// } +// } + +// impl TryInto> for SafeArray { +// type Error = Error; + +// fn try_into(self) -> Result> { +// self.into_vector(VT_R4) +// } +// } + +define_array_convertor!(f64, VT_R8); + +// impl TryFrom<&Vec> for SafeArray { +// type Error = Error; + +// fn try_from(value: &Vec) -> Result { +// Self::from_vector(VT_R8, value) +// } +// } + +// impl TryFrom> for SafeArray { +// type Error = Error; + +// fn try_from(value: Vec) -> Result { +// Self::from_vector(VT_R8, &value) +// } +// } + +// impl TryInto> for &SafeArray { +// type Error = Error; + +// fn try_into(self) -> Result> { +// self.into_vector(VT_R8) +// } +// } + +// impl TryInto> for SafeArray { +// type Error = Error; + +// fn try_into(self) -> Result> { +// self.into_vector(VT_R8) +// } +// } impl TryFrom<&Vec<&str>> for SafeArray { type Error = Error; diff --git a/crates/uiautomation_derive/Cargo.toml b/crates/uiautomation_derive/Cargo.toml index 22729fa..5686ca7 100644 --- a/crates/uiautomation_derive/Cargo.toml +++ b/crates/uiautomation_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uiautomation_derive" -version = "0.3.0" +version = "0.3.2" edition = "2021" license = "Apache-2.0" authors = ["Steven Lee "] @@ -18,6 +18,6 @@ default-target = "x86_64-pc-windows-msvc" targets = ["aarch64-pc-windows-msvc", "i686-pc-windows-msvc", "x86_64-pc-windows-msvc"] [dependencies] -syn = { version = "2.0.60", features = ["full"] } +syn = { version = "2.0.66", features = ["full"] } quote = "1.0.36" -proc-macro2 = "1.0.81" +proc-macro2 = "1.0.84" diff --git a/samples/uia_async/Cargo.toml b/samples/uia_async/Cargo.toml new file mode 100644 index 0000000..8456feb --- /dev/null +++ b/samples/uia_async/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "uia_async" +version = "1.0.0" +edition = "2021" + +[dependencies] + +tokio = { version = "1.37.0", features = ["full"] } + +uiautomation = { path = "../../crates/uiautomation" } diff --git a/samples/uia_async/src/main.rs b/samples/uia_async/src/main.rs new file mode 100644 index 0000000..35014b5 --- /dev/null +++ b/samples/uia_async/src/main.rs @@ -0,0 +1,17 @@ +use uiautomation::UIAutomation; + +#[tokio::main] +async fn main() { + let handler = tokio::spawn(async { + let automation = UIAutomation::new().unwrap(); + let root = automation.get_root_element().unwrap(); + + //tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + + println!("Found it: {}", root); + }); + + let result = handler.await; + + println!("Got: {:?}", result); +} \ No newline at end of file diff --git a/samples/uia_event_handler/Cargo.toml b/samples/uia_event_handler/Cargo.toml new file mode 100644 index 0000000..1f8638e --- /dev/null +++ b/samples/uia_event_handler/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "uia_event_handler" +version = "0.1.0" +edition = "2021" + +[dependencies] + +uiautomation = { path = "../../crates/uiautomation" } diff --git a/samples/uia_event_handler/src/main.rs b/samples/uia_event_handler/src/main.rs new file mode 100644 index 0000000..cb3a386 --- /dev/null +++ b/samples/uia_event_handler/src/main.rs @@ -0,0 +1,41 @@ +use uiautomation::events::CustomFocusChangedEventHandler; +use uiautomation::events::CustomPropertyChangedEventHandlerFn; +use uiautomation::events::UIFocusChangedEventHandler; +use uiautomation::events::UIPropertyChangedEventHandler; +use uiautomation::processes::Process; +use uiautomation::types::UIProperty; +use uiautomation::UIAutomation; + +struct MyFocusChangedEventHandler{} + +impl CustomFocusChangedEventHandler for MyFocusChangedEventHandler { + fn handle(&self, sender: &uiautomation::UIElement) -> uiautomation::Result<()> { + println!("Focus changed: {}", sender); + Ok(()) + } +} + +fn main() { + let note_proc = Process::create("notepad.exe").unwrap(); + + let automation = UIAutomation::new().unwrap(); + let root = automation.get_root_element().unwrap(); + let matcher = automation.create_matcher().from(root).timeout(10000).classname("Notepad"); + if let Ok(notepad) = matcher.find_first() { + let focus_changed_handler = MyFocusChangedEventHandler {}; + let focus_changed_handler = UIFocusChangedEventHandler::from(focus_changed_handler); + + automation.add_focus_changed_event_handler(None, &focus_changed_handler).unwrap(); + + let text_changed_handler: Box = Box::new(|sender, property, value| { + println!("Property changed: {}.{:?} = {}", sender, property, value); + Ok(()) + }); + let text_changed_handler = UIPropertyChangedEventHandler::from(text_changed_handler); + + automation.add_property_changed_event_handler(¬epad, uiautomation::types::TreeScope::Subtree, None, &text_changed_handler, &[UIProperty::ValueValue]).unwrap(); + } + + println!("waiting for notepad.exe..."); + note_proc.wait().unwrap(); +} diff --git a/tests/event_test/Cargo.toml b/tests/event_test/Cargo.toml new file mode 100644 index 0000000..1a0c311 --- /dev/null +++ b/tests/event_test/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "event_test" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +uiautomation = { path = "../../crates/uiautomation" } +windows-core = "0.56.0" + +[dependencies.windows] +version = "0.56.0" +features = [ + "implement", + "UI_UIAutomation", + "Win32_UI_Accessibility", +] diff --git a/tests/event_test/src/lib.rs b/tests/event_test/src/lib.rs new file mode 100644 index 0000000..3658447 --- /dev/null +++ b/tests/event_test/src/lib.rs @@ -0,0 +1,27 @@ +use uiautomation::UIElement; +use windows::core::*; +use windows::Win32::UI::Accessibility::*; + +#[implement(IUIAutomationEventHandler)] +pub struct MyEventHandler { +} + +impl IUIAutomationEventHandler_Impl for MyEventHandler { + fn HandleAutomationEvent(&self,sender:Option<&IUIAutomationElement>,eventid:UIA_EVENT_ID) -> windows::core::Result<()> { + if let Some(element) = sender { + let element = UIElement::from(element); + println!("event: {}, element: {}", eventid.0, element); + } else { + println!("event: {}", eventid.0); + } + + Ok(()) + } +} + +// impl windows::core::Param for MyEventHandler { +// unsafe fn param(self) -> ParamValue { +// let handler: IUIAutomationEventHandler = self.into(); +// ParamValue::Owned(handler) +// } +// } \ No newline at end of file diff --git a/tests/event_test/tests/test.rs b/tests/event_test/tests/test.rs new file mode 100644 index 0000000..1b304c3 --- /dev/null +++ b/tests/event_test/tests/test.rs @@ -0,0 +1,34 @@ +#[cfg(test)] +mod tests { + use std::thread::sleep; + use std::time::Duration; + + use event_test::MyEventHandler; + use uiautomation::UIAutomation; + use windows::Win32::UI::Accessibility::IUIAutomation; + use windows::Win32::UI::Accessibility::IUIAutomationEventHandler; + use windows::Win32::UI::Accessibility::TreeScope_Subtree; + use windows::Win32::UI::Accessibility::UIA_Text_TextChangedEventId; + + #[test] + fn test_event() { + let automation = UIAutomation::new().unwrap(); + let auto_ref: &IUIAutomation = automation.as_ref(); + + let matcher = automation.create_matcher().classname("Notepad"); + let notepad = matcher.find_first().unwrap(); + + let handler = MyEventHandler {}; + let handler: IUIAutomationEventHandler = handler.into(); + + unsafe { + auto_ref.AddAutomationEventHandler(UIA_Text_TextChangedEventId, + ¬epad, + TreeScope_Subtree, + None, + &handler).unwrap(); + } + + sleep(Duration::from_secs(60)); + } +} \ No newline at end of file