Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / UIAutomation / Win32Providers / MS / Internal / AutomationProxies / Accessible.cs / 1 / Accessible.cs
//---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: Wraps some of IAccessible to support getting basic properties // and default action // // History: // 06/02/2003 : BrendanM Ported to WCP // 03/15/2004 : Micw enhanced to support a native MSAA fallback proxy // //--------------------------------------------------------------------------- // PRESHARP: In order to avoid generating warnings about unkown message numbers and unknown pragmas. #pragma warning disable 1634, 1691 using System; using System.Diagnostics; using System.Collections; using System.Globalization; using System.Threading; using System.Windows.Automation; using System.Windows; using Accessibility; using System.Text; using System.Runtime.InteropServices; using MS.Win32; namespace MS.Internal.AutomationProxies { // values return from IAccessible.get_accRole. internal enum AccessibleRole: int { TitleBar = 0x1, MenuBar = 0x2, ScrollBar = 0x3, Grip = 0x4, Sound = 0x5, Cursor = 0x6, Caret = 0x7, Alert = 0x8, Window = 0x9, Client = 0xa, MenuPopup = 0xb, MenuItem = 0xc, Tooltip = 0xd, Application = 0xe, Document = 0xf, Pane = 0x10, Chart = 0x11, Dialog = 0x12, Border = 0x13, Grouping = 0x14, Separator = 0x15, ToolBar = 0x16, StatusBar = 0x17, Table = 0x18, ColumnHeader = 0x19, RowHeader = 0x1a, Column = 0x1b, Row = 0x1c, Cell = 0x1d, Link = 0x1e, HelpBalloon = 0x1f, Character = 0x20, List = 0x21, ListItem = 0x22, Outline = 0x23, OutlineItem = 0x24, PageTab = 0x25, PropertyPage = 0x26, Indicator = 0x27, Graphic = 0x28, StaticText = 0x29, Text = 0x2a, PushButton = 0x2b, CheckButton = 0x2c, RadioButton = 0x2d, Combobox = 0x2e, DropList = 0x2f, ProgressBar = 0x30, Dial = 0x31, HotKeyField = 0x32, Slider = 0x33, SpinButton = 0x34, Diagram = 0x35, Animation = 0x36, Equation = 0x37, ButtonDropDown = 0x38, ButtonMenu = 0x39, ButtonDropDownGrid = 0x3a, Whitespace = 0x3b, PageTabList = 0x3c, Clock = 0x3d, SplitButton = 0x3e, IpAddress = 0x3f, OutlineButton = 0x40, } // values returned from IAccessible.get_accState. [Flags] internal enum AccessibleState: int { Normal = 0x00000000, Unavailable = 0x00000001, Selected = 0x00000002, Focused = 0x00000004, Pressed = 0x00000008, Checked = 0x00000010, Mixed = 0x00000020, ReadOnly = 0x00000040, HotTracked = 0x00000080, Default = 0x00000100, Expanded = 0x00000200, Collapsed = 0x00000400, Busy = 0x00000800, Floating = 0x00001000, Marqueed = 0x00002000, Animated = 0x00004000, Invisible = 0x00008000, Offscreen = 0x00010000, Sizeable = 0x00020000, Moveable = 0x00040000, SelfVoicing = 0x00080000, Focusable = 0x00100000, Selectable = 0x00200000, Linked = 0x00400000, Traversed = 0x00800000, Multiselectable = 0x01000000, ExtSelectable = 0x02000000, AlertLow = 0x04000000, AlertMedium = 0x08000000, AlertHigh = 0x10000000, Protected = 0x20000000, } internal class Accessible { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors // // called by Wrap to create a node Accessible that manages its children private Accessible(IAccessible acc, int idChild) { Debug.Assert(acc != null, "null IAccessible"); _acc = acc; _idChild = idChild; _accessibleChildrenIndex = -1; } // Here we are re-implementing AccessibleObjectFromEvent with a critical difference. // AccessibleObjectFromEvent, via AccessibleObjectFromWindow, will default to a standard implementation // of IAccessible if the window doesn't have a native implementation. // However we only want to succeed constructing the object if the window has a native implementation. internal static Accessible CreateNativeFromEvent(IntPtr hwnd, int idObject, int idChild) { // On Vista, pass PID as wParam - allows credUI scenario to work (OLEACC needs to know our PID to // DuplicateHandle back to this process.) IntPtr wParam = IntPtr.Zero; if(Environment.OSVersion.Version.Major >= 6) wParam = new IntPtr(UnsafeNativeMethods.GetCurrentProcessId()); // send the window a WM_GETOBJECT message requesting the specific object id. IntPtr lResult = Misc.ProxySendMessage(hwnd, NativeMethods.WM_GETOBJECT, wParam, new IntPtr(idObject)); if (lResult == IntPtr.Zero) { return null; } // unwrap the pointer that was returned IAccessible acc = null; int hr = NativeMethods.S_FALSE; try { hr = UnsafeNativeMethods.ObjectFromLresult(lResult, ref UnsafeNativeMethods.IID_IAccessible, wParam, ref acc); } catch (InvalidCastException) { // CLR remoting appears to be interfering in cases where the remote IAccessible is a Winforms control - // the object we get back is a __Transparent proxy, and casting that to IAccessible fails with an exception // (which is caught and ignored here). See bug #1110719 for details. // One way around this is to use AccessibleObjectFromWindow - that returns IAccessible instead of IUnknown - // in effect it does the case in unmanaged code, and seems to avoid this issue. Other winforms code in the // proxies uses this approach. AccessibleObjectFromWindow will return an OLEACC proxy if one is available, // however, so we can't use it here, as this code only wants to deal with native IAccessibles. return null; } // ObjectFromLresult returns an IAccessible from the remote process. If that impl is managed, however, then // the local CLR will set up a CLR-Remoting-based connection instead and bypass COM - this causes problems // because CLR Remoting typically isn't initalized, so calls fail with a RemotingException: "This remoting // proxy has no channel sink which means either the server has no registered server channels that are listening, // or this application has no suitable client channel to talk to the server." - see bug #1519030. The local CLR // detects that the remote object is a managed impl by QI'ing for a specifc interface (IManagedObject?). // We can prevent this from happening by dropping the IAccessible we've gotten back from ObjectFromLresult, // and instead use AccessibleObjectFromWindow: AOFW wraps the real IAccessible in a DynamicAnnotation wrapper // that passes through all IAccessible (and related interface) calls - but it doesn't pass through the // IManagedObject interface, so the CLR treats it as a COM object, and continues to use plain COM to access it, // avoiding the CLR Remoting issues. // // In effect, we're sending WM_GETOBJECT to the HWND to see if there's a native impl there, using ObjectFromLresult // just to free that object, and then using AccessibleObjectFromWindow on the window to get the IAccessible // that we actually use. (Can't just use AccessibleObjectFromWindow from the start, since that would return // an oleacc proxy for hwnds that don't support IAccessible natively, and we only care about actual IAccessible // impls here.) // // We used to do use AOFW below only if the acc we got back above was a managed object (checked using // !Marshal.IsComObject()) - that only protects us from managed IAccessibles we get back directly; we could // still hit the above issue if we get back a remote unmanaged impl that then returns a maanged impl via // navigation (Media Center does this). So we now use AOFW all the time. if(hr == NativeMethods.S_OK && acc != null) { object obj = null; hr = UnsafeNativeMethods.AccessibleObjectFromWindow(hwnd, idObject, ref UnsafeNativeMethods.IID_IUnknown, ref obj); acc = obj as IAccessible; } if (hr != NativeMethods.S_OK || acc == null) { return null; } // This takes care of calling get_accChild, if necessary... return AccessibleFromObject(idChild, acc); } #if DEBUG /* // strictly for debugging purposes: public override string ToString() { try { return string.Format( "{0} \"{1}\" {2} {3}", RoleText, Name, _idChild, Window ); } catch (Exception e) { return e.Message; } } [DllImport( "oleacc.dll" )] internal static extern int GetRoleText( int dwRole, StringBuilder lpszRole, int cchRoleMax ); private string RoleText { get { const int cch = 64; StringBuilder sb = new StringBuilder( cch ); int len = GetRoleText( (int)Role, sb, cch ); return sb.ToString().Substring( 0, len ); } } */ #endif #endregion Constructors //------------------------------------------------------ // // Internal Methods // //----------------------------------------------------- #region Internal Methods // returns a new Accessible for the IAccessible, or null if IAccessible is null internal static Accessible Wrap(IAccessible acc) { return Wrap(acc, NativeMethods.CHILD_SELF); } // returns a new Accessible for the IAccessible + child id, or null if IAccessible is null internal static Accessible Wrap(IAccessible acc, int idChild) { return acc != null ? new Accessible(acc, idChild) : null; } internal IAccessible IAccessible { get { return _acc; } } internal int ChildCount { get { return GetChildCount(_acc); } } internal int ChildId { get { return _idChild; } } internal string Description { get { return GetDescription(_acc, _idChild); } } internal string KeyboardShortcut { get { return GetKeyboardShortcut(_acc, _idChild); } } internal string Name { get { return GetName(_acc, _idChild); } } internal string DefaultAction { get { return GetDefaultAction(_acc, _idChild); } } internal AccessibleRole Role { get { return GetRole(_acc, _idChild); } } internal bool IsPassword { get { return HasState(AccessibleState.Protected); } } internal bool IsSelected { get { return HasState(AccessibleState.Selected); } } internal bool IsMultiSelectable { get { return HasState(AccessibleState.Multiselectable); } } internal bool IsIndeterminate { get { return HasState(AccessibleState.Mixed); } } internal bool IsChecked { get { return HasState(AccessibleState.Checked); } } internal bool IsReadOnly { get { return HasState(AccessibleState.ReadOnly); } } internal bool IsEnabled { get { return ! HasState(AccessibleState.Unavailable); } } internal bool IsFocused { get { return HasState(AccessibleState.Focused); } } internal bool IsOffScreen { get { return HasState(AccessibleState.Offscreen); } } internal Accessible FirstChild { get { return _idChild == NativeMethods.CHILD_SELF ? GetChildAt(_acc, null, 0) : null; } } internal Accessible LastChild { get { return _idChild == NativeMethods.CHILD_SELF ? GetChildAt(_acc, null, Accessible.GetChildCount(_acc) - 1) : null; } } internal Accessible NextSibling(Accessible parent) { Debug.Assert(parent != null); // if this object doesn't yet have an index into parent's children find it object[] children = null; // if we need to get children to find an index; re-use them if (_accessibleChildrenIndex == -1) { children = SetAccessibleChildrenIndexAndGetChildren(parent._acc); // if unable to find this child (broken IAccessible impl?) bail ( if (_accessibleChildrenIndex == -1) { Debug.Assert(false); return null; } } Accessible rval = null; if (_accessibleChildrenIndex + 1 < Accessible.GetChildCount(parent._acc)) { rval = GetChildAt(parent._acc, children, _accessibleChildrenIndex + 1); } return rval; } internal Accessible PreviousSibling(Accessible parent) { Debug.Assert(parent != null); // if this object doesn't yet have an index into parent's children find it object[] children = null; // if we need to get children to find an index; re-use them if (_accessibleChildrenIndex == -1) { children = SetAccessibleChildrenIndexAndGetChildren(parent._acc); // if unable to find this child (broken IAccessible impl?) bail ( if (_accessibleChildrenIndex == -1) { Debug.Assert(false); return null; } } Accessible rval = null; if (_accessibleChildrenIndex - 1 >= 0) { rval = GetChildAt(parent._acc, children, _accessibleChildrenIndex - 1); } return rval; } internal Accessible Parent { get { // // review: I think it might be better to throw an exception here than return a bogus value. IAccessible rval; if (_idChild != NativeMethods.CHILD_SELF) { rval = _acc; // parent is managing this child } else { try { rval = (IAccessible)_acc.accParent; } catch (Exception e) { if (HandleIAccessibleException(e)) { // PerSharp/PreFast will flag this as a warning, 6503/56503: Property get methods should not throw exceptions. // We are communicate with the underlying control to get the information. // The control may not be able to give us the information we need. // Throw the correct exception to communicate the failure. #pragma warning suppress 6503 throw; } return null; } } return Wrap(rval); } } internal int AccessibleChildrenIndex(Accessible parent) { // if this is the first time we are called then compute the value and cache it. if (_accessibleChildrenIndex < 0) { SetAccessibleChildrenIndexAndGetChildren(parent._acc); } return _accessibleChildrenIndex; } internal bool IsAvailableToUser { get { AccessibleState state = State; // From MSDN: // STATE_SYSTEM_INVISIBLE means the object is programmatically hidden. For example, menu items // are programmatically hidden until a user activates the menu. Because objects with this // state are not available to users, client applications should not communicate information // about the object to users. However, if client applications find an object with this state, // they should check to see if STATE_SYSTEM_OFFSCREEN is also set. If this second state is // defined, then clients can communicate the information about the object to users. // // We're not dealing with menus [in this version of NativeMsaaProxy] so won't worry about them // here. To "clean up" the tree we'll skip over IAccessibles that have the above states. // May revisit per user feedback. if (Accessible.HasState(state, AccessibleState.Invisible) && !Accessible.HasState(state, AccessibleState.Offscreen)) return false; return true; } } internal bool InSameHwnd(IntPtr hwnd) { bool inSameHwnd = Window != IntPtr.Zero && Window == hwnd; return inSameHwnd; } // returns true if accessible object should be exposed to UIA clients. // accessible objects are exposed UIA if they are visible, not offscreen, and do not correspond to a child window. internal bool IsExposedToUIA { get { bool rval = false; // if the accessible object is "available"... if (IsAvailableToUser) { // ... and is not a child window... // AccessibleRole role = Role; if (role != AccessibleRole.Window) { // ... and is not a child window that is trident ... // (special case since trident doesn't have a ROLE_SYSTEM_WINDOW object for its "Internet Explorer_Server" window. if (role != AccessibleRole.Client || Description != "MSAAHTML Registered Handler") { // then it is visible rval = true; } } } return rval; } } internal AccessibleState State { get { try { return (AccessibleState)_acc.get_accState(_idChild); } catch (Exception e) { if (HandleIAccessibleException(e)) { // PerSharp/PreFast will flag this as a warning, 6503/56503: Property get methods should not throw exceptions. // We are communicate with the underlying control to get the information. // The control may not be able to give us the information we need. // Throw the correct exception to communicate the failure. #pragma warning suppress 6503 throw; } return AccessibleState.Unavailable; } } } internal string Value { get { try { string value = FixBstr(_acc.get_accValue(_idChild)); // PerSharp/PreFast will flag this as warning 6507/56507: Prefer 'string.IsNullOrEmpty(value)' over checks for null and/or emptiness. // Need to convert nulls into an empty string, so need to just test for a null. // Therefore we can not use IsNullOrEmpty() here, suppress the warning. #pragma warning suppress 6507 return value != null ? value : ""; } catch (Exception e) { if (HandleIAccessibleException(e)) { // PerSharp/PreFast will flag this as a warning, 6503/56503: Property get methods should not throw exceptions. // We are communicate with the underlying control to get the information. // The control may not be able to give us the information we need. // Throw the correct exception to communicate the failure. #pragma warning suppress 6503 throw; } return ""; } } set { try { _acc.set_accValue(_idChild, value); } catch (Exception e) { if (HandleIAccessibleException(e)) { throw; } // throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed), e); } } } // Return the Rect that bounds this element in screen coordinates internal Rect Location { get { NativeMethods.Win32Rect rcW32 = GetLocation(_acc, _idChild); return rcW32.ToRect(false); } } internal static Accessible GetFullAccessibleChildByIndex(Accessible accParent, int index) { int childCount = 0; object[] accChildren = Accessible.GetAccessibleChildren(accParent.IAccessible, out childCount); if (accChildren != null && 0 <= index && index < accChildren.Length) { object child = accChildren[index]; IAccessible accChild = child as IAccessible; if (accChild != null) { return Accessible.Wrap(accChild); } else if (child is int) { int idChild = (int)child; return Accessible.Wrap(accParent.IAccessible, idChild); } } return null; } internal static AccessibleRole GetRole(IAccessible acc, int idChild) { AccessibleRole rval; try { object role = acc.get_accRole(idChild); // get_accRole can return a non-int! for example, Outlook 2003 SUPERGRID entries // can return the string "Table View". so we return an int if we got one otherwise // we convert it to the generic "client" role. rval = (role is int) ? (AccessibleRole)(int)role : AccessibleRole.Client; } catch (Exception e) { if (HandleIAccessibleException(e)) { throw; } rval = AccessibleRole.Client; } return rval; } // Get the selected children in a container internal Accessible [] GetSelection() { // object obj = null; try { obj = _acc.accSelection; } catch (Exception e) { if (HandleIAccessibleException(e)) { throw; } obj = null; } if (obj == null) { return null; } Accessible [] children = null; if (obj is int) { children = new Accessible[1]; children[0] = AccessibleFromObject(obj, _acc); } else if (obj is object) { children = new Accessible[1]; children[0] = AccessibleFromObject(obj, _acc); } else if (obj is object []) { object [] objs = (object [])obj; children = new Accessible[objs.Length]; for (int i=0;i0) { aChildren = new object[childCount]; // Get the raw children because accNavigate doesn't work if (UnsafeNativeMethods.AccessibleChildren(accessibleObject, 0, childCount, aChildren, out childrenReturned) == NativeMethods.E_INVALIDARG) { System.Diagnostics.Debug.Assert(false, "Call to AccessibleChildren() returned E_INVALIDARG."); throw new ElementNotAvailableException(); } } return aChildren; } catch (Exception e) { if (HandleIAccessibleException(e)) { throw; } throw new ElementNotAvailableException(); } } #endregion #endregion Internal Methods //------------------------------------------------------ // // Private Methods // //------------------------------------------------------ #region Private Methods // Returns a child by index private static Accessible GetChildAt(IAccessible parent, object [] children, int index) { // note: calling AccessibleChildren on Trident and asking for a single child can // result in a Fatal Execution Engine Error (79697ADA)(80121506). // Perhaps the implementation ignores the cChildren param? // To avoid the problem we always ask for all of the children and just // use the specific one we are interested in. if (children == null) { children = GetChildren(parent); } if (children == null) { return null; } // Paranoia that between calls accChildCount returns different counts if (index >= children.Length) index = children.Length - 1; Accessible nav = AccessibleFromObject(children[index], parent); if (nav != null) { nav._accessibleChildrenIndex = index; } return nav; } // Find _acc's index among its siblings with optimization to return children collection // if that would be needed later by the caller private object [] SetAccessibleChildrenIndexAndGetChildren(IAccessible parent) { // this is only called if the index hasn't been set yet. Debug.Assert(_accessibleChildrenIndex < 0); object [] children = GetChildren(parent); if (children == null) { return null; // unlikely to happen but... } // Try to figure out which child in this array '_acc' is for (int i=0;i // Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: Wraps some of IAccessible to support getting basic properties // and default action // // History: // 06/02/2003 : BrendanM Ported to WCP // 03/15/2004 : Micw enhanced to support a native MSAA fallback proxy // //--------------------------------------------------------------------------- // PRESHARP: In order to avoid generating warnings about unkown message numbers and unknown pragmas. #pragma warning disable 1634, 1691 using System; using System.Diagnostics; using System.Collections; using System.Globalization; using System.Threading; using System.Windows.Automation; using System.Windows; using Accessibility; using System.Text; using System.Runtime.InteropServices; using MS.Win32; namespace MS.Internal.AutomationProxies { // values return from IAccessible.get_accRole. internal enum AccessibleRole: int { TitleBar = 0x1, MenuBar = 0x2, ScrollBar = 0x3, Grip = 0x4, Sound = 0x5, Cursor = 0x6, Caret = 0x7, Alert = 0x8, Window = 0x9, Client = 0xa, MenuPopup = 0xb, MenuItem = 0xc, Tooltip = 0xd, Application = 0xe, Document = 0xf, Pane = 0x10, Chart = 0x11, Dialog = 0x12, Border = 0x13, Grouping = 0x14, Separator = 0x15, ToolBar = 0x16, StatusBar = 0x17, Table = 0x18, ColumnHeader = 0x19, RowHeader = 0x1a, Column = 0x1b, Row = 0x1c, Cell = 0x1d, Link = 0x1e, HelpBalloon = 0x1f, Character = 0x20, List = 0x21, ListItem = 0x22, Outline = 0x23, OutlineItem = 0x24, PageTab = 0x25, PropertyPage = 0x26, Indicator = 0x27, Graphic = 0x28, StaticText = 0x29, Text = 0x2a, PushButton = 0x2b, CheckButton = 0x2c, RadioButton = 0x2d, Combobox = 0x2e, DropList = 0x2f, ProgressBar = 0x30, Dial = 0x31, HotKeyField = 0x32, Slider = 0x33, SpinButton = 0x34, Diagram = 0x35, Animation = 0x36, Equation = 0x37, ButtonDropDown = 0x38, ButtonMenu = 0x39, ButtonDropDownGrid = 0x3a, Whitespace = 0x3b, PageTabList = 0x3c, Clock = 0x3d, SplitButton = 0x3e, IpAddress = 0x3f, OutlineButton = 0x40, } // values returned from IAccessible.get_accState. [Flags] internal enum AccessibleState: int { Normal = 0x00000000, Unavailable = 0x00000001, Selected = 0x00000002, Focused = 0x00000004, Pressed = 0x00000008, Checked = 0x00000010, Mixed = 0x00000020, ReadOnly = 0x00000040, HotTracked = 0x00000080, Default = 0x00000100, Expanded = 0x00000200, Collapsed = 0x00000400, Busy = 0x00000800, Floating = 0x00001000, Marqueed = 0x00002000, Animated = 0x00004000, Invisible = 0x00008000, Offscreen = 0x00010000, Sizeable = 0x00020000, Moveable = 0x00040000, SelfVoicing = 0x00080000, Focusable = 0x00100000, Selectable = 0x00200000, Linked = 0x00400000, Traversed = 0x00800000, Multiselectable = 0x01000000, ExtSelectable = 0x02000000, AlertLow = 0x04000000, AlertMedium = 0x08000000, AlertHigh = 0x10000000, Protected = 0x20000000, } internal class Accessible { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors // // called by Wrap to create a node Accessible that manages its children private Accessible(IAccessible acc, int idChild) { Debug.Assert(acc != null, "null IAccessible"); _acc = acc; _idChild = idChild; _accessibleChildrenIndex = -1; } // Here we are re-implementing AccessibleObjectFromEvent with a critical difference. // AccessibleObjectFromEvent, via AccessibleObjectFromWindow, will default to a standard implementation // of IAccessible if the window doesn't have a native implementation. // However we only want to succeed constructing the object if the window has a native implementation. internal static Accessible CreateNativeFromEvent(IntPtr hwnd, int idObject, int idChild) { // On Vista, pass PID as wParam - allows credUI scenario to work (OLEACC needs to know our PID to // DuplicateHandle back to this process.) IntPtr wParam = IntPtr.Zero; if(Environment.OSVersion.Version.Major >= 6) wParam = new IntPtr(UnsafeNativeMethods.GetCurrentProcessId()); // send the window a WM_GETOBJECT message requesting the specific object id. IntPtr lResult = Misc.ProxySendMessage(hwnd, NativeMethods.WM_GETOBJECT, wParam, new IntPtr(idObject)); if (lResult == IntPtr.Zero) { return null; } // unwrap the pointer that was returned IAccessible acc = null; int hr = NativeMethods.S_FALSE; try { hr = UnsafeNativeMethods.ObjectFromLresult(lResult, ref UnsafeNativeMethods.IID_IAccessible, wParam, ref acc); } catch (InvalidCastException) { // CLR remoting appears to be interfering in cases where the remote IAccessible is a Winforms control - // the object we get back is a __Transparent proxy, and casting that to IAccessible fails with an exception // (which is caught and ignored here). See bug #1110719 for details. // One way around this is to use AccessibleObjectFromWindow - that returns IAccessible instead of IUnknown - // in effect it does the case in unmanaged code, and seems to avoid this issue. Other winforms code in the // proxies uses this approach. AccessibleObjectFromWindow will return an OLEACC proxy if one is available, // however, so we can't use it here, as this code only wants to deal with native IAccessibles. return null; } // ObjectFromLresult returns an IAccessible from the remote process. If that impl is managed, however, then // the local CLR will set up a CLR-Remoting-based connection instead and bypass COM - this causes problems // because CLR Remoting typically isn't initalized, so calls fail with a RemotingException: "This remoting // proxy has no channel sink which means either the server has no registered server channels that are listening, // or this application has no suitable client channel to talk to the server." - see bug #1519030. The local CLR // detects that the remote object is a managed impl by QI'ing for a specifc interface (IManagedObject?). // We can prevent this from happening by dropping the IAccessible we've gotten back from ObjectFromLresult, // and instead use AccessibleObjectFromWindow: AOFW wraps the real IAccessible in a DynamicAnnotation wrapper // that passes through all IAccessible (and related interface) calls - but it doesn't pass through the // IManagedObject interface, so the CLR treats it as a COM object, and continues to use plain COM to access it, // avoiding the CLR Remoting issues. // // In effect, we're sending WM_GETOBJECT to the HWND to see if there's a native impl there, using ObjectFromLresult // just to free that object, and then using AccessibleObjectFromWindow on the window to get the IAccessible // that we actually use. (Can't just use AccessibleObjectFromWindow from the start, since that would return // an oleacc proxy for hwnds that don't support IAccessible natively, and we only care about actual IAccessible // impls here.) // // We used to do use AOFW below only if the acc we got back above was a managed object (checked using // !Marshal.IsComObject()) - that only protects us from managed IAccessibles we get back directly; we could // still hit the above issue if we get back a remote unmanaged impl that then returns a maanged impl via // navigation (Media Center does this). So we now use AOFW all the time. if(hr == NativeMethods.S_OK && acc != null) { object obj = null; hr = UnsafeNativeMethods.AccessibleObjectFromWindow(hwnd, idObject, ref UnsafeNativeMethods.IID_IUnknown, ref obj); acc = obj as IAccessible; } if (hr != NativeMethods.S_OK || acc == null) { return null; } // This takes care of calling get_accChild, if necessary... return AccessibleFromObject(idChild, acc); } #if DEBUG /* // strictly for debugging purposes: public override string ToString() { try { return string.Format( "{0} \"{1}\" {2} {3}", RoleText, Name, _idChild, Window ); } catch (Exception e) { return e.Message; } } [DllImport( "oleacc.dll" )] internal static extern int GetRoleText( int dwRole, StringBuilder lpszRole, int cchRoleMax ); private string RoleText { get { const int cch = 64; StringBuilder sb = new StringBuilder( cch ); int len = GetRoleText( (int)Role, sb, cch ); return sb.ToString().Substring( 0, len ); } } */ #endif #endregion Constructors //------------------------------------------------------ // // Internal Methods // //----------------------------------------------------- #region Internal Methods // returns a new Accessible for the IAccessible, or null if IAccessible is null internal static Accessible Wrap(IAccessible acc) { return Wrap(acc, NativeMethods.CHILD_SELF); } // returns a new Accessible for the IAccessible + child id, or null if IAccessible is null internal static Accessible Wrap(IAccessible acc, int idChild) { return acc != null ? new Accessible(acc, idChild) : null; } internal IAccessible IAccessible { get { return _acc; } } internal int ChildCount { get { return GetChildCount(_acc); } } internal int ChildId { get { return _idChild; } } internal string Description { get { return GetDescription(_acc, _idChild); } } internal string KeyboardShortcut { get { return GetKeyboardShortcut(_acc, _idChild); } } internal string Name { get { return GetName(_acc, _idChild); } } internal string DefaultAction { get { return GetDefaultAction(_acc, _idChild); } } internal AccessibleRole Role { get { return GetRole(_acc, _idChild); } } internal bool IsPassword { get { return HasState(AccessibleState.Protected); } } internal bool IsSelected { get { return HasState(AccessibleState.Selected); } } internal bool IsMultiSelectable { get { return HasState(AccessibleState.Multiselectable); } } internal bool IsIndeterminate { get { return HasState(AccessibleState.Mixed); } } internal bool IsChecked { get { return HasState(AccessibleState.Checked); } } internal bool IsReadOnly { get { return HasState(AccessibleState.ReadOnly); } } internal bool IsEnabled { get { return ! HasState(AccessibleState.Unavailable); } } internal bool IsFocused { get { return HasState(AccessibleState.Focused); } } internal bool IsOffScreen { get { return HasState(AccessibleState.Offscreen); } } internal Accessible FirstChild { get { return _idChild == NativeMethods.CHILD_SELF ? GetChildAt(_acc, null, 0) : null; } } internal Accessible LastChild { get { return _idChild == NativeMethods.CHILD_SELF ? GetChildAt(_acc, null, Accessible.GetChildCount(_acc) - 1) : null; } } internal Accessible NextSibling(Accessible parent) { Debug.Assert(parent != null); // if this object doesn't yet have an index into parent's children find it object[] children = null; // if we need to get children to find an index; re-use them if (_accessibleChildrenIndex == -1) { children = SetAccessibleChildrenIndexAndGetChildren(parent._acc); // if unable to find this child (broken IAccessible impl?) bail ( if (_accessibleChildrenIndex == -1) { Debug.Assert(false); return null; } } Accessible rval = null; if (_accessibleChildrenIndex + 1 < Accessible.GetChildCount(parent._acc)) { rval = GetChildAt(parent._acc, children, _accessibleChildrenIndex + 1); } return rval; } internal Accessible PreviousSibling(Accessible parent) { Debug.Assert(parent != null); // if this object doesn't yet have an index into parent's children find it object[] children = null; // if we need to get children to find an index; re-use them if (_accessibleChildrenIndex == -1) { children = SetAccessibleChildrenIndexAndGetChildren(parent._acc); // if unable to find this child (broken IAccessible impl?) bail ( if (_accessibleChildrenIndex == -1) { Debug.Assert(false); return null; } } Accessible rval = null; if (_accessibleChildrenIndex - 1 >= 0) { rval = GetChildAt(parent._acc, children, _accessibleChildrenIndex - 1); } return rval; } internal Accessible Parent { get { // // review: I think it might be better to throw an exception here than return a bogus value. IAccessible rval; if (_idChild != NativeMethods.CHILD_SELF) { rval = _acc; // parent is managing this child } else { try { rval = (IAccessible)_acc.accParent; } catch (Exception e) { if (HandleIAccessibleException(e)) { // PerSharp/PreFast will flag this as a warning, 6503/56503: Property get methods should not throw exceptions. // We are communicate with the underlying control to get the information. // The control may not be able to give us the information we need. // Throw the correct exception to communicate the failure. #pragma warning suppress 6503 throw; } return null; } } return Wrap(rval); } } internal int AccessibleChildrenIndex(Accessible parent) { // if this is the first time we are called then compute the value and cache it. if (_accessibleChildrenIndex < 0) { SetAccessibleChildrenIndexAndGetChildren(parent._acc); } return _accessibleChildrenIndex; } internal bool IsAvailableToUser { get { AccessibleState state = State; // From MSDN: // STATE_SYSTEM_INVISIBLE means the object is programmatically hidden. For example, menu items // are programmatically hidden until a user activates the menu. Because objects with this // state are not available to users, client applications should not communicate information // about the object to users. However, if client applications find an object with this state, // they should check to see if STATE_SYSTEM_OFFSCREEN is also set. If this second state is // defined, then clients can communicate the information about the object to users. // // We're not dealing with menus [in this version of NativeMsaaProxy] so won't worry about them // here. To "clean up" the tree we'll skip over IAccessibles that have the above states. // May revisit per user feedback. if (Accessible.HasState(state, AccessibleState.Invisible) && !Accessible.HasState(state, AccessibleState.Offscreen)) return false; return true; } } internal bool InSameHwnd(IntPtr hwnd) { bool inSameHwnd = Window != IntPtr.Zero && Window == hwnd; return inSameHwnd; } // returns true if accessible object should be exposed to UIA clients. // accessible objects are exposed UIA if they are visible, not offscreen, and do not correspond to a child window. internal bool IsExposedToUIA { get { bool rval = false; // if the accessible object is "available"... if (IsAvailableToUser) { // ... and is not a child window... // AccessibleRole role = Role; if (role != AccessibleRole.Window) { // ... and is not a child window that is trident ... // (special case since trident doesn't have a ROLE_SYSTEM_WINDOW object for its "Internet Explorer_Server" window. if (role != AccessibleRole.Client || Description != "MSAAHTML Registered Handler") { // then it is visible rval = true; } } } return rval; } } internal AccessibleState State { get { try { return (AccessibleState)_acc.get_accState(_idChild); } catch (Exception e) { if (HandleIAccessibleException(e)) { // PerSharp/PreFast will flag this as a warning, 6503/56503: Property get methods should not throw exceptions. // We are communicate with the underlying control to get the information. // The control may not be able to give us the information we need. // Throw the correct exception to communicate the failure. #pragma warning suppress 6503 throw; } return AccessibleState.Unavailable; } } } internal string Value { get { try { string value = FixBstr(_acc.get_accValue(_idChild)); // PerSharp/PreFast will flag this as warning 6507/56507: Prefer 'string.IsNullOrEmpty(value)' over checks for null and/or emptiness. // Need to convert nulls into an empty string, so need to just test for a null. // Therefore we can not use IsNullOrEmpty() here, suppress the warning. #pragma warning suppress 6507 return value != null ? value : ""; } catch (Exception e) { if (HandleIAccessibleException(e)) { // PerSharp/PreFast will flag this as a warning, 6503/56503: Property get methods should not throw exceptions. // We are communicate with the underlying control to get the information. // The control may not be able to give us the information we need. // Throw the correct exception to communicate the failure. #pragma warning suppress 6503 throw; } return ""; } } set { try { _acc.set_accValue(_idChild, value); } catch (Exception e) { if (HandleIAccessibleException(e)) { throw; } // throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed), e); } } } // Return the Rect that bounds this element in screen coordinates internal Rect Location { get { NativeMethods.Win32Rect rcW32 = GetLocation(_acc, _idChild); return rcW32.ToRect(false); } } internal static Accessible GetFullAccessibleChildByIndex(Accessible accParent, int index) { int childCount = 0; object[] accChildren = Accessible.GetAccessibleChildren(accParent.IAccessible, out childCount); if (accChildren != null && 0 <= index && index < accChildren.Length) { object child = accChildren[index]; IAccessible accChild = child as IAccessible; if (accChild != null) { return Accessible.Wrap(accChild); } else if (child is int) { int idChild = (int)child; return Accessible.Wrap(accParent.IAccessible, idChild); } } return null; } internal static AccessibleRole GetRole(IAccessible acc, int idChild) { AccessibleRole rval; try { object role = acc.get_accRole(idChild); // get_accRole can return a non-int! for example, Outlook 2003 SUPERGRID entries // can return the string "Table View". so we return an int if we got one otherwise // we convert it to the generic "client" role. rval = (role is int) ? (AccessibleRole)(int)role : AccessibleRole.Client; } catch (Exception e) { if (HandleIAccessibleException(e)) { throw; } rval = AccessibleRole.Client; } return rval; } // Get the selected children in a container internal Accessible [] GetSelection() { // object obj = null; try { obj = _acc.accSelection; } catch (Exception e) { if (HandleIAccessibleException(e)) { throw; } obj = null; } if (obj == null) { return null; } Accessible [] children = null; if (obj is int) { children = new Accessible[1]; children[0] = AccessibleFromObject(obj, _acc); } else if (obj is object) { children = new Accessible[1]; children[0] = AccessibleFromObject(obj, _acc); } else if (obj is object []) { object [] objs = (object [])obj; children = new Accessible[objs.Length]; for (int i=0;i 0) { aChildren = new object[childCount]; // Get the raw children because accNavigate doesn't work if (UnsafeNativeMethods.AccessibleChildren(accessibleObject, 0, childCount, aChildren, out childrenReturned) == NativeMethods.E_INVALIDARG) { System.Diagnostics.Debug.Assert(false, "Call to AccessibleChildren() returned E_INVALIDARG."); throw new ElementNotAvailableException(); } } return aChildren; } catch (Exception e) { if (HandleIAccessibleException(e)) { throw; } throw new ElementNotAvailableException(); } } #endregion #endregion Internal Methods //------------------------------------------------------ // // Private Methods // //------------------------------------------------------ #region Private Methods // Returns a child by index private static Accessible GetChildAt(IAccessible parent, object [] children, int index) { // note: calling AccessibleChildren on Trident and asking for a single child can // result in a Fatal Execution Engine Error (79697ADA)(80121506). // Perhaps the implementation ignores the cChildren param? // To avoid the problem we always ask for all of the children and just // use the specific one we are interested in. if (children == null) { children = GetChildren(parent); } if (children == null) { return null; } // Paranoia that between calls accChildCount returns different counts if (index >= children.Length) index = children.Length - 1; Accessible nav = AccessibleFromObject(children[index], parent); if (nav != null) { nav._accessibleChildrenIndex = index; } return nav; } // Find _acc's index among its siblings with optimization to return children collection // if that would be needed later by the caller private object [] SetAccessibleChildrenIndexAndGetChildren(IAccessible parent) { // this is only called if the index hasn't been set yet. Debug.Assert(_accessibleChildrenIndex < 0); object [] children = GetChildren(parent); if (children == null) { return null; // unlikely to happen but... } // Try to figure out which child in this array '_acc' is for (int i=0;i
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- Utility.cs
- DataComponentGenerator.cs
- RenderData.cs
- WindowsFormsHelpers.cs
- TextElementEnumerator.cs
- PlaceHolder.cs
- XmlSchemaComplexContent.cs
- DataGridViewCheckBoxColumn.cs
- StorageInfo.cs
- GeometryDrawing.cs
- CompletionCallbackWrapper.cs
- DataBoundControlHelper.cs
- ObjectKeyFrameCollection.cs
- EffectiveValueEntry.cs
- Screen.cs
- QueryCacheEntry.cs
- RegexMatch.cs
- ToolStripLabel.cs
- PagePropertiesChangingEventArgs.cs
- GroupItem.cs
- ValidatedControlConverter.cs
- XPathEmptyIterator.cs
- ColorDialog.cs
- FreeFormDesigner.cs
- ProfileGroupSettings.cs
- VarRemapper.cs
- DataSourceSelectArguments.cs
- DSASignatureDeformatter.cs
- OverflowException.cs
- PictureBox.cs
- ParseHttpDate.cs
- ToolStripPanelRenderEventArgs.cs
- BinaryObjectInfo.cs
- RelatedEnd.cs
- StorageComplexTypeMapping.cs
- LocalFileSettingsProvider.cs
- PeerObject.cs
- DataServiceCollectionOfT.cs
- ColorTransformHelper.cs
- RandomNumberGenerator.cs
- AssemblyInfo.cs
- CallbackBehaviorAttribute.cs
- SymmetricAlgorithm.cs
- BindingOperations.cs
- xml.cs
- FileChangesMonitor.cs
- DecoratedNameAttribute.cs
- OverflowException.cs
- RepeatButtonAutomationPeer.cs
- XmlEncoding.cs
- ByteAnimationBase.cs
- ReflectEventDescriptor.cs
- RecipientInfo.cs
- DiscriminatorMap.cs
- DataSysAttribute.cs
- LockedActivityGlyph.cs
- BitmapEffectvisualstate.cs
- TaskResultSetter.cs
- _LazyAsyncResult.cs
- GridViewUpdatedEventArgs.cs
- DbConnectionHelper.cs
- OleDbPermission.cs
- WebEvents.cs
- XmlSubtreeReader.cs
- SslStreamSecurityBindingElement.cs
- RefreshEventArgs.cs
- Attribute.cs
- GetWinFXPath.cs
- DefaultPrintController.cs
- MouseCaptureWithinProperty.cs
- WebException.cs
- PageAdapter.cs
- SqlBuffer.cs
- FontStretches.cs
- Assert.cs
- BrowserDefinitionCollection.cs
- HtmlListAdapter.cs
- KeyConstraint.cs
- RequestStatusBarUpdateEventArgs.cs
- WebPartUserCapability.cs
- ResetableIterator.cs
- Dictionary.cs
- RegisterResponseInfo.cs
- BackStopAuthenticationModule.cs
- DesignerOptionService.cs
- MatrixAnimationUsingPath.cs
- HttpCapabilitiesBase.cs
- ChineseLunisolarCalendar.cs
- Interop.cs
- XmlExceptionHelper.cs
- NativeMethods.cs
- DataPointer.cs
- HostingEnvironmentSection.cs
- EventLogQuery.cs
- BevelBitmapEffect.cs
- GeneratedCodeAttribute.cs
- UnsafeNativeMethods.cs
- ResourcePermissionBaseEntry.cs
- BitmapEffectvisualstate.cs
- EmptyEnumerator.cs