//------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods internal override void WinEventProc(int eventId, IntPtr hwnd, int idObject, int idChild, uint eventTime) { // ignore any event not pertaining directly to the window if (idObject != UnsafeNativeMethods.OBJID_WINDOW) { return; } // Ignore if this is a bogus hwnd (shouldn't happen) if (hwnd == IntPtr.Zero) { return; } NativeMethods.HWND nativeHwnd = NativeMethods.HWND.Cast(hwnd); // Purposefully including windows that have been destroyed (e.g. IsWindow will return // false here for EVENT_OBJECT_DESTROY) because we need that notification. if (eventId == NativeMethods.EVENT_OBJECT_HIDE && !SafeNativeMethods.IsWindow(nativeHwnd)) { return; } int[] runtimeId; AutomationElement rawEl; if (eventId == NativeMethods.EVENT_OBJECT_DESTROY) { // If the window has been destroyed just report the RuntimeId with the event. runtimeId = HwndProxyElementProvider.MakeRuntimeId(nativeHwnd); rawEl = null; } else { // If the window is just being hidden then can create (and return as event src) a real element rawEl = AutomationElement.FromHandle(hwnd); runtimeId = rawEl.GetRuntimeId(); } // Do the notify. Note that this handler is used to notify client-side UIAutomation providers of windows // being destroyed or hidden. The delegate called here is itself protected by a lock. This delegate may // call out to proxies but also calls ClientEventManager.RaiseEventInThisClientOnly which properly // queues the actual callout to client code. object[] handlers = GetHandlers(); Debug.Assert(handlers.Length <= 1, "handlers.Length"); if (handlers.Length > 0) { ((WindowHideOrCloseHandler)handlers [0])(hwnd, rawEl, runtimeId); } }
/// <summary> /// Called by a client to add a listener for pattern or custom events. /// </summary> /// <param name="eventId">A control pattern or custom event identifier.</param> /// <param name="element">Element on which to listen for control pattern or custom events.</param> /// <param name="scope">Specifies whether to listen to property changes events on the specified element, and/or its ancestors and children.</param> /// <param name="eventHandler">Delegate to call when the specified event occurs.</param> /// /// <outside_see conditional="false"> /// This API does not work inside the secure execution environment. /// <exception cref="System.Security.Permissions.SecurityPermission"/> /// </outside_see> public static void AddAutomationEventHandler( AutomationEvent eventId, AutomationElement element, TreeScope scope, AutomationEventHandler eventHandler ) { Misc.ValidateArgumentNonNull(element, "element"); Misc.ValidateArgumentNonNull(eventHandler, "eventHandler"); Misc.ValidateArgument(eventId != AutomationElement.AutomationFocusChangedEvent, SRID.EventIdMustNotBeAutomationFocusChanged); Misc.ValidateArgument(eventId != AutomationElement.StructureChangedEvent, SRID.EventIdMustNotBeStructureChanged); Misc.ValidateArgument(eventId != AutomationElement.AutomationPropertyChangedEvent, SRID.EventIdMustNotBeAutomationPropertyChanged); if (eventId == WindowPattern.WindowClosedEvent) { // Once a window closes and the hwnd is destroyed we won't be able to determine where it was in the // Automation tree; therefore only support WindowClosed events for all windows (eg. src==root and scope // is descendants) or a specific WindowPattern element (src==root of a Window and scope is the element). // Also handle odd combinations (eg. src==specific element and scope is subtree|ancestors). bool paramsValidated = false; if (Misc.Compare(element, AutomationElement.RootElement)) { // For root element need to have Descendants scope set (Note: Subtree includes Descendants) if ((scope & TreeScope.Descendants) == TreeScope.Descendants) { paramsValidated = true; } } else { // otherwise non-root elements must have the entire tree (Anscestors, Element and Descendants)... if ((scope & (TreeScope.Ancestors | TreeScope.Element | TreeScope.Descendants)) == (TreeScope.Ancestors | TreeScope.Element | TreeScope.Descendants)) { paramsValidated = true; } else if ((scope & TreeScope.Element) == TreeScope.Element) { // ...OR Element where the element implements WindowPattern // PRESHARP will flag this as warning 56506/6506:Parameter 'element' to this public method must be validated: A null-dereference can occur here. // False positive, element is checked, see above #pragma warning suppress 6506 object val = element.GetCurrentPropertyValue(AutomationElement.NativeWindowHandleProperty); if (val != null && val is int && (int)val != 0) { if (HwndProxyElementProvider.IsWindowPatternWindow(NativeMethods.HWND.Cast(new IntPtr((int)val)))) { paramsValidated = true; } } } } if (!paramsValidated) { throw new ArgumentException(SR.Get(SRID.ParamsNotApplicableToWindowClosedEvent)); } } // Add a client-side Handler for for this event request EventListener l = new EventListener(eventId, scope, null, CacheRequest.CurrentUiaCacheRequest); ClientEventManager.AddListener(element, eventHandler, l); }
// 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); } } }
// 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); } }