// We need to treat MSAA's FOCUS winevents differently depending on the OBJID - // OBJID_CLIENT gets routed to the proxies; _MENU and _SYSMENU get speical treatment. private AutomationElement GetFocusedElementFromWinEvent(IntPtr hwnd, int idObject, int idChild) { try { IRawElementProviderSimple provider = null; // These are the only object types that oleacc proxies allow to take focus. // (Native IAccessibles can send focus for other custom OBJID valus, but those are no use // to us.) // Try and get providers for them ourself - if we don't get anything, then // defer to core to get the element for the HWND itself. if (idObject == UnsafeNativeMethods.OBJID_CLIENT) { // regular focus - pass it off to a proxy... provider = ProxyManager.ProxyProviderFromHwnd(NativeMethods.HWND.Cast(hwnd), idChild, UnsafeNativeMethods.OBJID_CLIENT); } else if (idObject == UnsafeNativeMethods.OBJID_MENU) { // menubar focus - see if there's a menubar pseudo-proxy registered... ClientSideProviderFactoryCallback factory = ProxyManager.NonClientMenuBarProxyFactory; if (factory != null) { provider = factory(hwnd, idChild, idObject); } } else if (idObject == UnsafeNativeMethods.OBJID_SYSMENU) { // system menu box focus - see if there's a sysmenu pseudo-proxy registered... ClientSideProviderFactoryCallback factory = ProxyManager.NonClientSysMenuProxyFactory; if (factory != null) { provider = factory(hwnd, idChild, idObject); } } else if (idObject <= 0) { return(null); } else { // This covers OBJID_CLIENT and custom OBJID cases. // Pass it to the proxy manager: most proxies will just handle OBJID_CLIENT, // but the MSAA proxy can potentally handle other OBJID values. provider = ProxyManager.ProxyProviderFromHwnd(NativeMethods.HWND.Cast(hwnd), idChild, idObject); } if (provider != null) { // Ask the fragment root if any of its children really have the focus IRawElementProviderFragmentRoot fragment = provider as IRawElementProviderFragmentRoot; if (fragment != null) { // if we get back something that is different than what we started with and its not null // use that instead. This is here to get the subset link in the listview but could be usefull // for listview subitems as well. IRawElementProviderSimple realFocus = fragment.GetFocus(); if (realFocus != null && !Object.ReferenceEquals(realFocus, provider)) { provider = realFocus; } } SafeNodeHandle hnode = UiaCoreApi.UiaNodeFromProvider(provider); return(AutomationElement.Wrap(hnode)); } else { // Didn't find a proxy to handle this hwnd - pass off to core... return(AutomationElement.FromHandle(hwnd)); } } catch (Exception e) { if (Misc.IsCriticalException(e)) { throw; } return(null); } }
// miscellaneous functions used with Accessible internal static bool CompareClass(IntPtr hwnd, string szClass) { return(ProxyManager.GetClassName(NativeMethods.HWND.Cast(hwnd)) == szClass); }
// 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); } } }
// 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); } } }