//------------------------------------------------------ // // Constructors // //------------------------------------------------------ #region Constructors // Private ctor, used mostly by CacheHelper when reconstructing AutomationElements from // a CacheResponse. internal AutomationElement(SafeNodeHandle hnode, object[,] cachedValues, int cachedValuesIndex, UiaCoreApi.UiaCacheRequest request) { _hnode = hnode; // Can be IntPtr.Zero for a lightweight object _cachedValues = cachedValues; // Can be null if there are no cached properties for this node _cachedValuesIndex = cachedValuesIndex; _request = request; // Set RuntimeId (if available; 'as int[]' filters out AutomationElement.NotAvailable) _runtimeId = LookupCachedValue(AutomationElement.RuntimeIdProperty, false, true) as int[]; // // One scenario that allows for null runtimeID - doing UpdatedCache() on a node and asking only // for children - gives us back a placeholder node that only has valid .CachedChildren, // the node itself doesn't have any cached properties or a node. // Since null is a valid value for these, we need another value to // indicate that they were not requested - it's a bit obscure, but // 'this' works well here, since these can never have it as legal value. _cachedParent = this; _cachedFirstChild = this; _cachedNextSibling = this; }
internal static object ConvertToElementArray(object value) { // Convert each item to an AutomationElement... object[] objArr = (object[])value; AutomationElement[] els = new AutomationElement[objArr.Length]; for (int i = 0; i < objArr.Length; i++) { if (objArr[i] == null) { els[i] = null; } else { SafeNodeHandle hnode = UiaCoreApi.UiaHUiaNodeFromVariant(objArr[i]); els[i] = AutomationElement.Wrap(hnode); } } return(els); }
// Used by methods that return non-cached AutomationElements - examples currently include // AutomationElements returned as properties (SelecitonContainer, RowHeaders). internal static AutomationElement Wrap(SafeNodeHandle hnode) { if (hnode == null || hnode.IsInvalid) { return null; } return new AutomationElement(hnode, null, 0, null); }
// 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); } }
private static object ConvertToElement(object value) { SafeNodeHandle hnode = UiaCoreApi.UiaHUiaNodeFromVariant(value); return(AutomationElement.Wrap(hnode)); }
//------------------------------------------------------ // // Private Methods // //------------------------------------------------------ #region Private Methods // Parses the string as returned from ElementSearcher - see ElementSearcher.cs // for a description of the format string. Summary is that it is a lisp-like // set of parens indicating tree structure (eg. "(()())" indicates a node containing // two child nodes), but uses 'P' instead of an open paran to indicate that the // corresonding node has a property array that needs to be associated with it. // // index is the current position in the tree strucure string, // propIndex is the current position in the array of property arrays // (an array of properties returned for each element that matches the // condition specified in the Searcher condition.) private static AutomationElement ParseTreeDescription(string treeDescription, object[,] properties, ref int index, ref int propIndex, UiaCoreApi.UiaCacheRequest cacheRequest, bool askedForChildren, bool askedForDescendants) { // Check that this is a 'begin node' tag (with or without properties)... if (string.IsNullOrEmpty(treeDescription)) { return(null); } char c = treeDescription[index]; if (c != '(' && c != 'P') { return(null); } bool havePropertiesForThisNode = c == 'P'; index++; SafeNodeHandle hnode = null; // If we have information for this node, and we want full remote // references back, then extract the hnode from the first slot of that // element's property row... if (havePropertiesForThisNode && cacheRequest.AutomationElementMode == AutomationElementMode.Full) { hnode = (SafeNodeHandle)properties[propIndex, 0]; } // Attach properties if present... object[,] cachedValues = null; int cachedValueIndex = 0; if (havePropertiesForThisNode) { cachedValues = properties; cachedValueIndex = propIndex; propIndex++; } AutomationElement node = new AutomationElement(hnode, cachedValues, cachedValueIndex, cacheRequest); if (askedForChildren || askedForDescendants) { // If we did request children or descendants at this level, then set the // cached first child to null - it may get overwritten with // an actual value later; but the key thing is that it gets // set so we can distinguish the "asked, but doesn't have one" from // the "didn't ask" case. (Internally, AutomationElement uses // 'this' to indicate the later case, and throws an exception if // you ask for the children without having previously asked // for them in a CacheRequest.) node.SetCachedFirstChild(null); } // Add in children... AutomationElement prevChild = null; for (; ;) { // Recursively parse the string... AutomationElement child = ParseTreeDescription(treeDescription, properties, ref index, ref propIndex, cacheRequest, askedForDescendants, askedForDescendants); if (child == null) { break; } // Then link child node into tree... child.SetCachedParent(node); if (prevChild == null) { node.SetCachedFirstChild(child); } else { prevChild.SetCachedNextSibling(child); } prevChild = child; } // Ensure that end node tag is present... if (treeDescription[index] != ')') { Debug.Assert(false, "Internal error: Got malformed tree description string, missing closing paren"); return(null); } index++; return(node); }