Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;
using Microsoft.Internal;

[assembly: InternalsVisibleTo($"PresentationCore.Tests, PublicKey={BuildInfo.WCP_PUBLIC_KEY_STRING}")]
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Diagnostics;
using System.Globalization;
using System.Windows.Automation;
using System.Windows.Automation.Provider;
using System.Windows;
using Accessibility;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -295,6 +296,82 @@ internal static Accessible Wrap(IAccessible acc, int idChild)
internal bool IsFocused { get { return HasState(AccessibleState.Focused); } }
internal bool IsOffScreen { get { return HasState(AccessibleState.Offscreen); } }

internal object GetPatternProvider(int patternId)
{
return GetPatternProvider(AccessibleExProvider, patternId);
}

internal object GetPropertyValue(int propertyId)
{
return GetPropertyValue(AccessibleExProvider, propertyId);
}

internal static object GetPatternProvider(IRawElementProviderSimple provider, int patternId)
{
if (provider == null)
{
return null;
}

try
{
return provider.GetPatternProvider(patternId);
}
catch (Exception e) when (IsAccessibleExUnavailable(e))
{
return null;
}
}

internal static object GetPropertyValue(IRawElementProviderSimple provider, int propertyId)
{
if (provider == null)
{
return null;
}

try
{
return provider.GetPropertyValue(propertyId);
}
catch (Exception e) when (IsAccessibleExUnavailable(e))
{
return null;
}
}

internal static IRawElementProviderSimple GetAccessibleExProvider(UnsafeNativeMethods.IServiceProvider serviceProvider, int childId)
{
if (serviceProvider == null)
{
return null;
}

try
{
Guid serviceId = typeof(UnsafeNativeMethods.IAccessibleEx).GUID;
Guid interfaceId = serviceId;
UnsafeNativeMethods.IAccessibleEx accessibleEx =
serviceProvider.QueryService(ref serviceId, ref interfaceId) as UnsafeNativeMethods.IAccessibleEx;

if (accessibleEx == null)
{
return null;
}

if (childId != NativeMethods.CHILD_SELF)
{
accessibleEx = accessibleEx.GetObjectForChild(childId);
}

return accessibleEx as IRawElementProviderSimple;
}
catch (Exception e) when (IsAccessibleExUnavailable(e))
{
return null;
}
}

internal Accessible FirstChild
{
get
Expand Down Expand Up @@ -1391,6 +1468,19 @@ private static bool HandleIAccessibleException(Exception e)
return true;
}

private static bool IsAccessibleExUnavailable(Exception e)
{
COMException comException = e as COMException;
return e is ArgumentException
|| e is InvalidCastException
|| e is NotImplementedException
|| comException != null
&& (comException.ErrorCode == NativeMethods.E_FAIL
|| comException.ErrorCode == NativeMethods.E_NOINTERFACE
|| comException.ErrorCode == NativeMethods.E_NOTIMPL
|| comException.ErrorCode == NativeMethods.E_INVALIDARG);
}

// IAccessibles that we get from Winforms apps in partial trust return failure
// code for some methods - notably accNavigate and accChild. The operation will
// succeed, however, if we first navigate up to the parent, and then back down
Expand Down Expand Up @@ -1476,9 +1566,25 @@ private enum NavDir
private IAccessible _acc; // a full IAccessible object or an IAccessible parent that is managing a ChildID
private int _idChild; // this is ChildID which is the ID a server gives this child (not related to child order!)
private int _accessibleChildrenIndex; // this is how many children to skip over when calling AccessibleChildren
private bool _accessibleExProviderInitialized;
private IRawElementProviderSimple _accessibleExProvider;

private IntPtr _hwnd;

private IRawElementProviderSimple AccessibleExProvider
{
get
{
if (!_accessibleExProviderInitialized)
{
_accessibleExProvider = GetAccessibleExProvider(_acc as UnsafeNativeMethods.IServiceProvider, _idChild);
_accessibleExProviderInitialized = true;
}

return _accessibleExProvider;
}
}

#endregion Private Fields
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@ internal class MSAAEventDispatcher : MSAAWinEventWrap
#region Constructors

private MSAAEventDispatcher()
: base(NativeMethods.EVENT_OBJECT_CREATE, NativeMethods.EVENT_OBJECT_ACCELERATORCHANGE)
: base(
NativeMethods.EVENT_OBJECT_CREATE,
NativeMethods.EVENT_OBJECT_ACCELERATORCHANGE,
ExpandCollapsePattern.ExpandCollapseStateProperty.Id,
ExpandCollapsePattern.ExpandCollapseStateProperty.Id,
TogglePattern.ToggleStateProperty.Id,
TogglePattern.ToggleStateProperty.Id)
{ }

#endregion Constructors
Expand Down Expand Up @@ -179,6 +185,13 @@ internal override void WinEventProc(int eventId, IntPtr hwnd, int idObject, int
// get the 2-nd level table of events and properties we are listening for in this window
Hashtable eventTable = (Hashtable)_hwndTable[hwnd];

AutomationProperty property = GetPatternPropertyFromWinEvent(eventId);
if (property != null)
{
MaybeFirePropertyChangeEvent(null, property, eventTable, hwnd, idObject, idChild, true);
return;
}

switch (eventId)
{
case NativeMethods.EVENT_OBJECT_CREATE:
Expand Down Expand Up @@ -242,6 +255,18 @@ internal override void WinEventProc(int eventId, IntPtr hwnd, int idObject, int
// break;
}
}

}

internal static AutomationProperty GetPatternPropertyFromWinEvent(int eventId)
{
if (eventId == ExpandCollapsePattern.ExpandCollapseStateProperty.Id)
return ExpandCollapsePattern.ExpandCollapseStateProperty;

if (eventId == TogglePattern.ToggleStateProperty.Id)
return TogglePattern.ToggleStateProperty;

return null;
}

#endregion Internal Methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,10 @@ ProviderOptions IRawElementProviderSimple.ProviderOptions

object IRawElementProviderSimple.GetPatternProvider(int patternId)
{
object provider = _acc.GetPatternProvider(patternId);
if (provider != null)
return provider;

AutomationPattern pattern = AutomationPattern.LookupById(patternId);
//Debug.WriteLine.WriteLine(string.Format(CultureInfo.CurrentCulture, "{0} IRawElementProviderSimple.GetPatternProvider {1}", this, pattern));

Expand All @@ -495,6 +499,10 @@ object IRawElementProviderSimple.GetPatternProvider(int patternId)

object IRawElementProviderSimple.GetPropertyValue(int propertyId)
{
object value = _acc.GetPropertyValue(propertyId);
if (value != null)
return value;

AutomationProperty idProp = AutomationProperty.LookupById(propertyId);
//Debug.WriteLine.WriteLine(string.Format(CultureInfo.CurrentCulture, "{0} IRawElementProviderSimple.GetPropertyValue {1}", this, idProp));

Expand Down Expand Up @@ -669,7 +677,13 @@ IRawElementProviderSimple ISelectionItemProvider.SelectionContainer
{
//Debug.WriteLine.WriteLine(string.Format(CultureInfo.CurrentCulture, "{0} ISelectionItemProvider.SelectionContainer", this));

return IsRoot ? null : Parent;
MsaaNativeProvider parent = IsRoot ? null : Parent;
while (parent != null && !parent.IsPatternSupported(SelectionPattern.Pattern))
{
parent = parent.IsRoot ? null : parent.Parent;
}

return parent;
}
}
#endregion ISelectionItemProvider
Expand Down Expand Up @@ -880,6 +894,10 @@ protected virtual object GetPatternProvider(AutomationPattern pattern)
// overridable implementation of IRawElementProviderSimple.GetPropertyValue
protected virtual object GetPropertyValue(AutomationProperty idProp)
{
object patternValue = GetPatternPropertyValue(this, idProp);
if (patternValue != null)
return patternValue;

// The following UIA properties need support: AcceleratorKeyProperty, AccessKeyProperty, AutomationIdProperty,
// HasKeyboardFocusProperty, IsContentElementProperty, IsControlElementProperty, IsKeyboardFocusableProperty,
// IsPasswordProperty, IsReadOnlyProperty, NativeObjectModelAccessProperty, SiblingIdProperty, TabIndexProperty?,
Expand Down Expand Up @@ -907,6 +925,7 @@ protected virtual object GetPropertyValue(AutomationProperty idProp)
else
return null;
}

else if (idProp == AutomationElement.IsEnabledProperty)
{
return _acc.IsEnabled;
Expand Down Expand Up @@ -953,6 +972,24 @@ protected virtual object GetPropertyValue(AutomationProperty idProp)
return null;
}

internal static object GetPatternPropertyValue(IRawElementProviderSimple provider, AutomationProperty property)
{
if (property == ExpandCollapsePattern.ExpandCollapseStateProperty)
{
IExpandCollapseProvider expandCollapse =
provider.GetPatternProvider(ExpandCollapsePattern.Pattern.Id) as IExpandCollapseProvider;
return expandCollapse?.ExpandCollapseState;
}

if (property == TogglePattern.ToggleStateProperty)
{
IToggleProvider toggle = provider.GetPatternProvider(TogglePattern.Pattern.Id) as IToggleProvider;
return toggle?.ToggleState;
}

return null;
}

// overridable method used by value pattern to retrieve the value.
protected virtual string GetValue()
{
Expand Down Expand Up @@ -1309,7 +1346,9 @@ public CtrlTypePatterns(ControlType ctrlType, params AutomationPattern[] pattern
new CtrlTypePatterns(ControlType.RadioButton, SelectionItemPattern.Pattern),
// ControlType.Slider: it is impossible to tell which of RangeValue or Selection patterns to support so we're not supporting either.
// ControlType.Spinner: it is impossible to tell which of RangeValue or Selection patterns to support so we're not supporting either.
new CtrlTypePatterns(ControlType.SplitButton, InvokePattern.Pattern)
new CtrlTypePatterns(ControlType.SplitButton, InvokePattern.Pattern),
new CtrlTypePatterns(ControlType.Tree, SelectionPattern.Pattern),
new CtrlTypePatterns(ControlType.TreeItem, SelectionItemPattern.Pattern)
};

private Accessible _acc; // the IAccessible we are representing. use Accessible to access.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ internal class MSAAWinEventWrap

#region Constructors

// ctor that takes a range of events
internal MSAAWinEventWrap(int eventMin, int eventMax)
// ctor that takes one or more event ranges as min/max pairs
internal MSAAWinEventWrap(params int[] eventRanges)
{
_eventMin = eventMin;
_eventMax = eventMax;
_hHooks = new IntPtr[1];
if (eventRanges == null || eventRanges.Length == 0 || eventRanges.Length % 2 != 0)
throw new ArgumentException(SR.InvalidParameter);

_eventRanges = (int[])eventRanges.Clone();
_hHooks = new IntPtr[eventRanges.Length / 2];
Init();
}

Expand Down Expand Up @@ -74,12 +76,21 @@ internal void StartListening()
{
_fBusy = true;

for (int i = 0; i < _hHooks.Length; i++)
{
// in a single hook, listen for a range of WinEvent types
_hHooks[0] = Misc.SetWinEventHook(_eventMin, _eventMax, IntPtr.Zero, _winEventProc, 0, 0, _fFlags);
if (_hHooks[0] == IntPtr.Zero)
int rangeIndex = i * 2;
_hHooks[i] = Misc.SetWinEventHook(
_eventRanges[rangeIndex],
_eventRanges[rangeIndex + 1],
IntPtr.Zero,
_winEventProc,
0,
0,
_fFlags);
if (_hHooks[i] == IntPtr.Zero)
{
StopListening();
return;
}
}
_fBusy = false;
Expand Down Expand Up @@ -213,8 +224,7 @@ internal WinEvent(int eventId, IntPtr hwnd, int idObject, int idChild)
}

private Queue _qEvents; // Queue of events waiting to be processed
private int _eventMin; // minimum WinEvent type in range
private int _eventMax; // maximium WinEventType in range
private int[] _eventRanges; // pairs of minimum and maximum WinEvent types
private IntPtr [] _hHooks; // the returned handles(s) from SetWinEventHook
private bool _fBusy; // Flag indicating if we're busy processing
private int _fFlags; // SetWinEventHook flags
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,7 @@ internal static int LODWORD(long n)

internal const int E_ACCESSDENIED = unchecked((int)0x80070005);
internal const int E_FAIL = unchecked((int)0x80004005);
internal const int E_NOINTERFACE = unchecked((int)0x80004002);
internal const int E_UNEXPECTED = unchecked((int)0x8000FFFF);
internal const int E_INVALIDARG = unchecked((int)0x80070057);
internal const int E_MEMBERNOTFOUND = unchecked((int)0x80020003);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Runtime.InteropServices;
using System.Text;
using System.Diagnostics;
using System.Windows.Automation.Provider;
using NativeMethodsSetLastError = MS.Internal.UIAutomationClientSideProviders.NativeMethodsSetLastError;

namespace MS.Win32
Expand Down Expand Up @@ -79,6 +80,28 @@ internal static class UnsafeNativeMethods
internal static Guid IID_IDispatch = new Guid(0x00020400, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
internal static Guid IID_IAccessible = new Guid(0x618736e0, 0x3c3d, 0x11cf, 0x81, 0x0c, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);

[ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IServiceProvider
{
[return: MarshalAs(UnmanagedType.IUnknown)]
object QueryService(ref Guid service, ref Guid riid);
}

[ComImport, Guid("F8B80ADA-2C44-48D0-89BE-5FF23C9CD875"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IAccessibleEx
{
[return: MarshalAs(UnmanagedType.Interface)]
IAccessibleEx GetObjectForChild(int idChild);

void GetIAccessiblePair([MarshalAs(UnmanagedType.Interface)] out IAccessible accessible, out int childId);

[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_I4)]
int[] GetRuntimeId();

[return: MarshalAs(UnmanagedType.Interface)]
IAccessibleEx ConvertReturnedElement([MarshalAs(UnmanagedType.Interface)] IRawElementProviderSimple provider);
}

[DllImport("oleacc.dll", SetLastError=true)]
internal static extern IntPtr GetProcessHandleFromHwnd(IntPtr hwnd);

Expand Down Expand Up @@ -474,4 +497,3 @@ public struct LHITTESTINFO
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="$(WpfSharedDir)RefAssemblyAttrs.cs" />
<Compile Include="LibraryAssemblyInfo.cs" />
<Compile Include="$(WpfSharedDir)MS\Win32\NativeMethodsSetLastError.cs" />
<Compile Include="$(WpfCommonDir)src\System\SR.cs">
<Link>Common\System\SR.cs</Link>
Expand Down
Loading