Skip to content

IUIAutomationPropertyChangedEventHandler - registering against array type property events causes heap corruption #3818

@nunomluz

Description

@nunomluz

Summary

After running multiple tests, I've managed to narrow down the heap corruption crashes to specific array-typed properties. If the registration doesn't include any of those, the heap corruption doesn't seem to happen.

How to reproduce:

  1. Run the provided code
  2. Let it run and interact with the UI (e.g., an UI element size change)
  3. Heap corruption should occur immediately or in a few seconds
  4. Remove the 1st two properties from the list and run it again. Now there are no crashes (at least I didn't get any)

It's possible I'm misusing the library but I couldn't find much information for Rust, specifically.
Could you please check and let me know if I'm missing something, or if there might be a bug on how the library handles the memory management of array variants?

Crate manifest

[package]
name = "uiautomation-crash-repro"
version = "0.1.0"
edition = "2021"

[dependencies]
windows = { version = "0.62.2", features = [
    "Win32_UI_Accessibility",
    "Win32_System_Com",
    "Win32_System_Variant",
    "Win32_System_Ole",
    "Win32_UI_WindowsAndMessaging",
]}
windows-core = { version = "0.62.2" }

Crate code

use windows::Win32::System::Com::{CoInitializeEx, CoUninitialize, COINIT_MULTITHREADED};
use windows::Win32::System::Com::{CoCreateInstance, CLSCTX_INPROC_SERVER};
use windows::Win32::System::Variant::VARIANT;
use windows::Win32::UI::Accessibility::*;
use windows::Win32::UI::WindowsAndMessaging::{GetMessageW, TranslateMessage, DispatchMessageW, MSG};

#[windows::core::implement(IUIAutomationPropertyChangedEventHandler)]
struct PropertyHandler();

impl IUIAutomationPropertyChangedEventHandler_Impl for PropertyHandler_Impl {
    fn HandlePropertyChangedEvent(
        &self,
        _sender: windows_core::Ref<'_, IUIAutomationElement>,
        property_id: UIA_PROPERTY_ID,
        _new_value: &VARIANT,
    ) -> windows::core::Result<()> {
        println!("Property changed: {}, thread: {:?}", property_id.0, std::thread::current().id());
        Ok(())
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("Initializing COM...");
    let hr = unsafe { CoInitializeEx(None, COINIT_MULTITHREADED) };
    if hr.is_err() {
        println!("Failed to initialize COM: {:?}", hr);
        return Err(std::io::Error::other("Failed to initialize COM").into());
    }

    unsafe {
        println!("Creating UI Automation...");
        let automation: IUIAutomation = CoCreateInstance(&CUIAutomation, None, CLSCTX_INPROC_SERVER)?;
        
        println!("Getting root element...");
        let root = automation.GetRootElement()?;
        
        println!("Creating property handler...");
        let handler: IUIAutomationPropertyChangedEventHandler = PropertyHandler().into();
        
        // Property that contains SAFEARRAY in VARIANT
        let properties = vec![
            UIA_BoundingRectanglePropertyId,  // Causes heap corruption
            UIA_RuntimeIdPropertyId,    // Causes heap corruption
            // These properties do not cause heap corruption
            UIA_ControlTypePropertyId,
            UIA_NamePropertyId,
            UIA_ValueValuePropertyId,
            UIA_IsEnabledPropertyId,
            UIA_IsKeyboardFocusablePropertyId,
            UIA_HasKeyboardFocusPropertyId,
            UIA_IsContentElementPropertyId,
            UIA_IsControlElementPropertyId,
            UIA_IsOffscreenPropertyId,
            UIA_IsPasswordPropertyId,
        ];
        
        println!("Registering property handler...");
        automation.AddPropertyChangedEventHandlerNativeArray(
            &root,
            TreeScope_Subtree,
            None,
            &handler,
            &properties,
        )?;
        
        println!("Handler registered. Hover over taskbar to trigger events...");
        println!("Running message loop for 30 seconds...");
        
        // Run Windows message loop
        let start = std::time::Instant::now();
        let mut msg: MSG = std::mem::zeroed();
        while start.elapsed().as_secs() < 30 {
            if GetMessageW(&mut msg, None, 0, 0).0 > 0 {
                let _ =TranslateMessage(&msg);
                DispatchMessageW(&msg);
            }
        }
        
        println!("Cleaning up...");
        automation.RemovePropertyChangedEventHandler(&root, &handler)?;
        CoUninitialize();
    }
    
    println!("Done!");
    Ok(())
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions