// RemoveAllListeners - Removes all listeners from this client and notifies the // UIAccess server internal static void RemoveAllListeners() { lock (_classLock) { if (_listeners == null) { return; } // Stop all WinEvent tracking StopWinEventTracking(); // Must remove from back to front when calling RemoveAt because ArrayList // elements are compressed after each RemoveAt. for (int i = _listeners.Count - 1; i >= 0; i--) { EventListenerClientSide ec = (EventListenerClientSide)_listeners[i]; // Notify the server-side UIAccess that this event is no longer interesting EventListener l = ec.EventListener; // Only advise UI contexts of events being removed if the event might be raised by // a provider. TopLevelWindow event is raised by UI Automation framework. if (ShouldAdviseProviders(l.EventId)) { ec.EventHandle.Dispose(); // Calls RemoveEvent } // delete this one _listeners.RemoveAt(i); } _listeners = null; CheckStopCallbackQueueing(); } }
// AddListener - Adds a listener for this client and notifies the UIA server. All event // handler additions call through to this method. internal static void AddListener(AutomationElement rawEl, Delegate eventCallback, EventListener l) { lock (_classLock) { // If we are adding a listener then a proxy could be created as a result of an event so make sure they are loaded ProxyManager.LoadDefaultProxies(); if (_listeners == null) { // enough space for 16 AddXxxListeners (100 bytes) _listeners = new ArrayList(16); } // Start the callback queue that gets us off the server's // UI thread when events arrive cross-proc CheckStartCallbackQueueing(); // // The framework handles some events on behalf of providers; do those here // // If listening for BoundingRectangleProperty then may need to start listening on the // client-side for LocationChange WinEvent (only use *one* BoundingRectTracker instance). if (_winEventTrackers[(int)Tracker.BoundingRect] == null && HasProperty(AutomationElement.BoundingRectangleProperty, l.Properties)) { // AddWinEventListener(Tracker.BoundingRect, new BoundingRectTracker()); } // Start listening for menu event in order to raise MenuOpened/Closed events. if ( _winEventTrackers [(int)Tracker.MenuOpenedOrClosed] == null && (l.EventId == AutomationElement.MenuOpenedEvent || l.EventId == AutomationElement.MenuClosedEvent) ) { AddWinEventListener( Tracker.MenuOpenedOrClosed, new MenuTracker( new MenuHandler( OnMenuEvent ) ) ); } // Begin watching for hwnd open/close/show/hide so can advise of what events are being listened for. // Only advise UI contexts of events being added if the event might be raised by a provider. // TopLevelWindow event is raised by UI Automation framework so no need to track new UI. // if (_winEventTrackers[(int)Tracker.WindowShowOrOpen] == null ) { AddWinEventListener( Tracker.WindowShowOrOpen, new WindowShowOrOpenTracker( new WindowShowOrOpenHandler( OnWindowShowOrOpen ) ) ); AddWinEventListener( Tracker.WindowHideOrClose, new WindowHideOrCloseTracker( new WindowHideOrCloseHandler( OnWindowHideOrClose ) ) ); } // If listening for WindowInteractionStateProperty then may need to start listening on the // client-side for ObjectStateChange WinEvent. if (_winEventTrackers[(int)Tracker.WindowInteractionState] == null && HasProperty(WindowPattern.WindowInteractionStateProperty, l.Properties)) { AddWinEventListener(Tracker.WindowInteractionState, new WindowInteractionStateTracker()); } // If listening for WindowVisualStateProperty then may need to start listening on the // client-side for ObjectLocationChange WinEvent. if (_winEventTrackers[(int)Tracker.WindowVisualState] == null && HasProperty(WindowPattern.WindowVisualStateProperty, l.Properties)) { AddWinEventListener(Tracker.WindowVisualState, new WindowVisualStateTracker()); } // Wrap and store this record on the client... EventListenerClientSide ec = new EventListenerClientSide(rawEl, eventCallback, l); _listeners.Add(ec); // Only advise UI contexts of events being added if the event might be raised by // a provider. TopLevelWindow event is raised by UI Automation framework. if (ShouldAdviseProviders( l.EventId )) { // .. then let the server know about this listener ec.EventHandle = UiaCoreApi.UiaAddEvent(rawEl.RawNode, l.EventId.Id, ec.CallbackDelegate, l.TreeScope, PropertyArrayToIntArray(l.Properties), l.CacheRequest); } } }
// OnWindowShowOrOpen - Called by the WindowShowOrOpenTracker class when UI is shown or created private static void OnWindowShowOrOpen(IntPtr hwnd, AutomationElement rawEl) { bool doWindowOpenedEvent = false; bool doStructureChangedEvent = false; lock ( _classLock ) { if (_listeners != null) { // if rawEl is w/in the scope of any listeners then register for events in the new UI for (int i = 0; i < _listeners.Count; i++) { EventListenerClientSide ec = (EventListenerClientSide)_listeners[i]; EventListener l = ec.EventListener; if (l.EventId == WindowPattern.WindowOpenedEvent) { doWindowOpenedEvent = true; } if (l.EventId == AutomationElement.StructureChangedEvent) { doStructureChangedEvent = true; } // Only advise UI contexts if the provider might raise that event. if (!ShouldAdviseProviders(l.EventId)) { continue; } // Only advise UI contexts if the element is w/in scope of the reference element if (!ec.WithinScope(rawEl)) { continue; } // Notify the server side UiaCoreApi.UiaEventAddWindow(ec.EventHandle, hwnd); } } } // Piggy-back on the listener for Windows hiding or closing to raise WindowClosed and StructureChanged events. if (doWindowOpenedEvent) { if (HwndProxyElementProvider.IsWindowPatternWindow(NativeMethods.HWND.Cast(hwnd))) { // Go ahead and raise a client-side only WindowOpenedEvent (if anyone is listening) AutomationEventArgs e = new AutomationEventArgs(WindowPattern.WindowOpenedEvent); RaiseEventInThisClientOnly(WindowPattern.WindowOpenedEvent, rawEl, e); } } if (doStructureChangedEvent) { // Filter on the control elements. Otherwise, this is extremely noisy. Consider not filtering if there is feedback. //ControlType ct = (ControlType)rawEl.GetPropertyValue( AutomationElement.ControlTypeProperty ); //if ( ct != null ) { // Last,raise an event for structure changed StructureChangedEventArgs e = new StructureChangedEventArgs(StructureChangeType.ChildAdded, rawEl.GetRuntimeId()); RaiseEventInThisClientOnly(AutomationElement.StructureChangedEvent, rawEl, e); } } }
// AddListener - Adds a listener for this client and notifies the UIA server. All event // handler additions call through to this method. internal static void AddListener(AutomationElement rawEl, Delegate eventCallback, EventListener l) { lock (_classLock) { // If we are adding a listener then a proxy could be created as a result of an event so make sure they are loaded ProxyManager.LoadDefaultProxies(); if (_listeners == null) { // enough space for 16 AddXxxListeners (100 bytes) _listeners = new ArrayList(16); } // Start the callback queue that gets us off the server's // UI thread when events arrive cross-proc CheckStartCallbackQueueing(); // // The framework handles some events on behalf of providers; do those here // // If listening for BoundingRectangleProperty then may need to start listening on the // client-side for LocationChange WinEvent (only use *one* BoundingRectTracker instance). if (_winEventTrackers[(int)Tracker.BoundingRect] == null && HasProperty(AutomationElement.BoundingRectangleProperty, l.Properties)) { // There may be special cases to not map BoundingRect to WinEvent. One may be // where rawEl is a non-top-level native implementation and TreeScope == Element. // Another may be if rawEl is a native impl and TreeScope includes descendents then // may not need to worry about BoundingRect prop change on hosted Win32 UI? I think the // answer is 'yes' because BoundingRectangleProperty should fire events for the top-most // element that moved in the hierarchy. So if hosted Win32 content Rect changes w/o // the host changing then it would fire this event. // Note: Part of cleaning up WinEvent listeners is to move away from WinEvent handlers calling UIA client handlers AddWinEventListener(Tracker.BoundingRect, new BoundingRectTracker()); } // Start listening for menu event in order to raise MenuOpened/Closed events. if (_winEventTrackers [(int)Tracker.MenuOpenedOrClosed] == null && (l.EventId == AutomationElement.MenuOpenedEvent || l.EventId == AutomationElement.MenuClosedEvent)) { AddWinEventListener(Tracker.MenuOpenedOrClosed, new MenuTracker(new MenuHandler(OnMenuEvent))); } // Begin watching for hwnd open/close/show/hide so can advise of what events are being listened for. // Only advise UI contexts of events being added if the event might be raised by a provider. // TopLevelWindow event is raised by UI Automation framework so no need to track new UI. // Are there other events like this where Advise can be skipped? if (_winEventTrackers[(int)Tracker.WindowShowOrOpen] == null) { AddWinEventListener(Tracker.WindowShowOrOpen, new WindowShowOrOpenTracker(new WindowShowOrOpenHandler(OnWindowShowOrOpen))); AddWinEventListener(Tracker.WindowHideOrClose, new WindowHideOrCloseTracker(new WindowHideOrCloseHandler(OnWindowHideOrClose))); } // If listening for WindowInteractionStateProperty then may need to start listening on the // client-side for ObjectStateChange WinEvent. if (_winEventTrackers[(int)Tracker.WindowInteractionState] == null && HasProperty(WindowPattern.WindowInteractionStateProperty, l.Properties)) { AddWinEventListener(Tracker.WindowInteractionState, new WindowInteractionStateTracker()); } // If listening for WindowVisualStateProperty then may need to start listening on the // client-side for ObjectLocationChange WinEvent. if (_winEventTrackers[(int)Tracker.WindowVisualState] == null && HasProperty(WindowPattern.WindowVisualStateProperty, l.Properties)) { AddWinEventListener(Tracker.WindowVisualState, new WindowVisualStateTracker()); } // Wrap and store this record on the client... EventListenerClientSide ec = new EventListenerClientSide(rawEl, eventCallback, l); _listeners.Add(ec); // Only advise UI contexts of events being added if the event might be raised by // a provider. TopLevelWindow event is raised by UI Automation framework. if (ShouldAdviseProviders(l.EventId)) { // .. then let the server know about this listener ec.EventHandle = UiaCoreApi.UiaAddEvent(rawEl.RawNode, l.EventId.Id, ec.CallbackDelegate, l.TreeScope, PropertyArrayToIntArray(l.Properties), l.CacheRequest); } } }
// OnWindowHideOrClose - Called by the WindowHideOrCloseTracker class when UI is hidden or destroyed private static void OnWindowHideOrClose(IntPtr hwnd, AutomationElement rawEl, int [] runtimeId) { bool doWindowClosedEvent = false; bool doStructureChangedEvent = false; lock ( _classLock ) { if (_listeners != null) { // if an hwnd is hidden or closed remove event listeners for the window's provider for (int i = 0; i < _listeners.Count; i++) { EventListenerClientSide ec = (EventListenerClientSide)_listeners[i]; EventListener l = ec.EventListener; if (l.EventId == WindowPattern.WindowClosedEvent) { doWindowClosedEvent = true; } if (l.EventId == AutomationElement.StructureChangedEvent) { doStructureChangedEvent = true; } // Only advise UI contexts if the provider still exists // (but keep looking to see if need to do a WindowClosedEvent) if (rawEl == null) { continue; } // Only advise UI contexts if the provider might raise that event. if (!ShouldAdviseProviders(l.EventId)) { continue; } // Only advise UI contexts if the element is w/in scope of the reference element if (!ec.WithinScope(rawEl)) { continue; } // Notify the server-side that this event is no longer interesting UiaCoreApi.UiaEventRemoveWindow(ec.EventHandle, hwnd); } } } // Piggy-back on the listener for Windows hiding or closing to raise WindowClosed and StructureChanged events. // When the hwnd behind rawEl is being destroyed, it can't be determined that rawEl once had the // WindowPattern interface. Therefore raise an event for any window close. if (doWindowClosedEvent) { // When the hwnd is just hidden, rawEl will not be null, so can test if this would support WindowPattern // and throw this event away if the window doesn't support that CP if (rawEl != null && !HwndProxyElementProvider.IsWindowPatternWindow(NativeMethods.HWND.Cast(hwnd))) { return; } // Go ahead and raise a client-side only WindowClosedEvent (if anyone is listening) WindowClosedEventArgs e = new WindowClosedEventArgs(runtimeId); RaiseEventInThisClientOnly(WindowPattern.WindowClosedEvent, runtimeId, e); } if (doStructureChangedEvent) { // Raise an event for structure changed. This element has essentially gone away so there isn't an // opportunity to do filtering here. So, just like WindowClosed, this event will be very noisy. StructureChangedEventArgs e = new StructureChangedEventArgs(StructureChangeType.ChildRemoved, runtimeId); RaiseEventInThisClientOnly(AutomationElement.StructureChangedEvent, runtimeId, e); } }
// RemoveListener - Removes a listener from this client and notifies the UIAutomation server-side internal static void RemoveListener(AutomationEvent eventId, AutomationElement el, Delegate eventCallback) { lock ( _classLock ) { if (_listeners != null) { bool boundingRectListeners = false; // if not removing BoundingRect listeners no need to do check below bool menuListeners = false; // if not removing MenuOpenedOrClosed listeners no need to do check below bool windowInteracationListeners = false; // if not removing WindowsIntercation listeners no need to do check below bool windowVisualListeners = false; // if not removing WindowsVisual listeners no need to do check below for (int i = _listeners.Count - 1; i >= 0; i--) { EventListenerClientSide ec = (EventListenerClientSide)_listeners[i]; if (ec.IsListeningFor(eventId, el, eventCallback)) { EventListener l = ec.EventListener; // Only advise UI contexts of events being removed if the event might be raised by // a provider. TopLevelWindow event is raised by UI Automation framework. if (ShouldAdviseProviders(eventId)) { // Notify the server-side that this event is no longer interesting try { ec.EventHandle.Dispose(); // Calls UiaCoreApi.UiaRemoveEvent } // PRESHARP: Warning - Catch statements should not have empty bodies #pragma warning disable 6502 catch (ElementNotAvailableException) { // the element is gone already; continue on and remove the listener } #pragma warning restore 6502 finally { ec.Dispose(); } } // before removing, check if this delegate was listening for the below events // and see if we can stop clientside WinEvent trackers. if (HasProperty(AutomationElement.BoundingRectangleProperty, l.Properties)) { boundingRectListeners = true; } if (eventId == AutomationElement.MenuOpenedEvent || eventId == AutomationElement.MenuClosedEvent) { menuListeners = true; } if (HasProperty(WindowPattern.WindowInteractionStateProperty, l.Properties)) { windowInteracationListeners = true; } if (HasProperty(WindowPattern.WindowVisualStateProperty, l.Properties)) { windowVisualListeners = true; } // delete this one _listeners.RemoveAt(i); } } // Check listeners bools to see if clientside listeners can be removed if (boundingRectListeners) { RemovePropertyTracker(AutomationElement.BoundingRectangleProperty, Tracker.BoundingRect); } if (menuListeners) { RemoveMenuListeners(); } if (windowInteracationListeners) { RemovePropertyTracker(WindowPattern.WindowInteractionStateProperty, Tracker.WindowInteractionState); } if (windowVisualListeners) { RemovePropertyTracker(WindowPattern.WindowVisualStateProperty, Tracker.WindowVisualState); } // See if we can cleanup completely if (_listeners.Count == 0) { // as long as OnWindowShowOrOpen is static can just use new here and get same object instance // (if there's no WindowShowOrOpen listener, this method just returns) RemoveWinEventListener(Tracker.WindowShowOrOpen, new WindowShowOrOpenHandler(OnWindowShowOrOpen)); RemoveWinEventListener(Tracker.WindowHideOrClose, new WindowHideOrCloseHandler(OnWindowHideOrClose)); _listeners = null; } } } }
// AddListener - Adds a listener for this client and notifies the UIA server. All event // handler additions call through to this method. internal static void AddListener(AutomationElement rawEl, Delegate eventCallback, EventListener l) { lock (_classLock) { // If we are adding a listener then a proxy could be created as a result of an event so make sure they are loaded ProxyManager.LoadDefaultProxies(); if (_listeners == null) { // enough space for 16 AddXxxListeners (100 bytes) _listeners = new ArrayList(16); } // Start the callback queue that gets us off the server's // UI thread when events arrive cross-proc CheckStartCallbackQueueing(); // // The framework handles some events on behalf of providers; do those here // // If listening for BoundingRectangleProperty then may need to start listening on the // client-side for LocationChange WinEvent (only use *one* BoundingRectTracker instance). if (_winEventTrackers[(int)Tracker.BoundingRect] == null && HasProperty(AutomationElement.BoundingRectangleProperty, l.Properties)) { // AddWinEventListener(Tracker.BoundingRect, new BoundingRectTracker()); } // Start listening for menu event in order to raise MenuOpened/Closed events. if (_winEventTrackers [(int)Tracker.MenuOpenedOrClosed] == null && (l.EventId == AutomationElement.MenuOpenedEvent || l.EventId == AutomationElement.MenuClosedEvent)) { AddWinEventListener(Tracker.MenuOpenedOrClosed, new MenuTracker(new MenuHandler(OnMenuEvent))); } // Begin watching for hwnd open/close/show/hide so can advise of what events are being listened for. // Only advise UI contexts of events being added if the event might be raised by a provider. // TopLevelWindow event is raised by UI Automation framework so no need to track new UI. // if (_winEventTrackers[(int)Tracker.WindowShowOrOpen] == null) { AddWinEventListener(Tracker.WindowShowOrOpen, new WindowShowOrOpenTracker(new WindowShowOrOpenHandler(OnWindowShowOrOpen))); AddWinEventListener(Tracker.WindowHideOrClose, new WindowHideOrCloseTracker(new WindowHideOrCloseHandler(OnWindowHideOrClose))); } // If listening for WindowInteractionStateProperty then may need to start listening on the // client-side for ObjectStateChange WinEvent. if (_winEventTrackers[(int)Tracker.WindowInteractionState] == null && HasProperty(WindowPattern.WindowInteractionStateProperty, l.Properties)) { AddWinEventListener(Tracker.WindowInteractionState, new WindowInteractionStateTracker()); } // If listening for WindowVisualStateProperty then may need to start listening on the // client-side for ObjectLocationChange WinEvent. if (_winEventTrackers[(int)Tracker.WindowVisualState] == null && HasProperty(WindowPattern.WindowVisualStateProperty, l.Properties)) { AddWinEventListener(Tracker.WindowVisualState, new WindowVisualStateTracker()); } // Wrap and store this record on the client... EventListenerClientSide ec = new EventListenerClientSide(rawEl, eventCallback, l); _listeners.Add(ec); // Only advise UI contexts of events being added if the event might be raised by // a provider. TopLevelWindow event is raised by UI Automation framework. if (ShouldAdviseProviders(l.EventId)) { // .. then let the server know about this listener ec.EventHandle = UiaCoreApi.UiaAddEvent(rawEl.RawNode, l.EventId.Id, ec.CallbackDelegate, l.TreeScope, PropertyArrayToIntArray(l.Properties), l.CacheRequest); } } }