Skip to content

Commit

Permalink
Merge pull request #61 from leexgone/dev
Browse files Browse the repository at this point in the history
v0.11.0
  • Loading branch information
leexgone authored Jun 1, 2024
2 parents 896d777 + 7c7e00a commit 06f267f
Show file tree
Hide file tree
Showing 23 changed files with 1,521 additions and 324 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
10 changes: 3 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
38 changes: 38 additions & 0 deletions README.cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<CustomPropertyChangedEventHandlerFn> = 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(&notepad, uiautomation::types::TreeScope::Subtree, None, &text_changed_handler, &[UIProperty::ValueValue]).unwrap();
}

println!("waiting for notepad.exe...");
note_proc.wait().unwrap();
}
```
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<CustomPropertyChangedEventHandlerFn> = 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(&notepad, uiautomation::types::TreeScope::Subtree, None, &text_changed_handler, &[UIProperty::ValueValue]).unwrap();
}

println!("waiting for notepad.exe...");
note_proc.wait().unwrap();
}
```
12 changes: 8 additions & 4 deletions crates/uiautomation/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "uiautomation"
version = "0.10.0"
version = "0.11.0"
edition = "2021"
license = "Apache-2.0"
authors = ["Steven Lee <[email protected]>"]
Expand All @@ -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"
Expand All @@ -34,5 +37,6 @@ features = [
"Win32_System_Threading",
"Win32_Security",
"Win32_UI_Shell_PropertiesSystem",
"UI_UIAutomation"
]
"UI_UIAutomation",
"implement"
]
97 changes: 90 additions & 7 deletions crates/uiautomation/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<UIA_PROPERTY_ID> = 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<IUIAutomation> for UIAutomation {
Expand Down Expand Up @@ -1115,18 +1198,18 @@ impl From<IUIAutomationElement> for UIElement {
}
}

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 IntoParam<IUIAutomationElement> for UIElement {
// unsafe fn into_param(self) -> windows::core::Param<IUIAutomationElement> {
// windows::core::Param::Owned(self.element)
// }
// }

impl Param<IUIAutomationElement> for UIElement {
unsafe fn param(self) -> windows::core::ParamValue<IUIAutomationElement> {
windows::core::ParamValue::Owned(self.element)
Expand Down Expand Up @@ -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;
Expand Down
16 changes: 16 additions & 0 deletions crates/uiautomation/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -89,6 +90,21 @@ impl From<windows::core::Error> for Error {
}
}

impl Into<windows::core::Error> 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<HRESULT> for Error {
fn from(result: HRESULT) -> Self {
Self {
Expand Down
Loading

0 comments on commit 06f267f

Please sign in to comment.