From f65b08d254da62dafcd99c768f45252dd002206c Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Tue, 20 May 2025 16:39:25 +0200 Subject: [PATCH 01/54] commit work --- .../MS/internal/Automation/ElementProxy.cs | 169 ++--- .../MS/internal/Automation/ElementUtil.cs | 385 ++++-------- .../Automation/GridProviderWrapper.cs | 43 +- .../System/Windows/Threading/Dispatcher.cs | 452 +++++++++----- .../Threading/DispatcherOperation.1Arg.cs | 25 + .../Threading/DispatcherOperation.2Arg.cs | 27 + .../Threading/DispatcherOperation.3Arg.cs | 29 + .../Threading/DispatcherOperation.Action.cs | 104 ++++ .../Threading/DispatcherOperation.Event.cs | 82 +++ .../Threading/DispatcherOperation.Frame.cs | 69 ++ .../Threading/DispatcherOperation.TResult.cs | 140 +++++ .../Windows/Threading/DispatcherOperation.cs | 587 +++--------------- .../Threading/DispatcherOperationCallback.cs | 9 + .../Threading/DispatcherOperationLegacy.cs | 88 +++ .../DispatcherOperationTaskSource.cs | 122 ++-- .../Windows/Threading/DispatcherUtils.cs | 28 + .../src/WindowsBase/WindowsBase.csproj | 11 +- .../Windows/Threading/DispatcherTests.cs | 60 ++ 18 files changed, 1306 insertions(+), 1124 deletions(-) create mode 100644 src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.1Arg.cs create mode 100644 src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.2Arg.cs create mode 100644 src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.3Arg.cs create mode 100644 src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.cs create mode 100644 src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Event.cs create mode 100644 src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Frame.cs create mode 100644 src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.cs create mode 100644 src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationCallback.cs create mode 100644 src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationLegacy.cs create mode 100644 src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherUtils.cs diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs index cbc1e67b909..85c256c333d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -72,24 +72,18 @@ private ElementProxy(AutomationPeer peer) // IRawElementProviderSimple methods... - public object GetPatternProvider ( int pattern ) + public object GetPatternProvider(int pattern) { - AutomationPeer peer = Peer; - if (peer == null) - { - throw new ElementNotAvailableException(); - } - return ElementUtil.Invoke(peer, new DispatcherOperationCallback( InContextGetPatternProvider ), pattern); + AutomationPeer peer = Peer ?? throw new ElementNotAvailableException(); + + return ElementUtil.Invoke(peer, static (state, pattern) => state.InContextGetPatternProvider(pattern), this, pattern); } public object GetPropertyValue(int property) { - AutomationPeer peer = Peer; - if (peer == null) - { - throw new ElementNotAvailableException(); - } - return ElementUtil.Invoke(peer, new DispatcherOperationCallback(InContextGetPropertyValue), property); + AutomationPeer peer = Peer ?? throw new ElementNotAvailableException(); + + return ElementUtil.Invoke(peer, static (state, property) => state.InContextGetPropertyValue(property), this, property); } public ProviderOptions ProviderOptions @@ -101,7 +95,7 @@ public ProviderOptions ProviderOptions { return ProviderOptions.ServerSideProvider; } - return (ProviderOptions)ElementUtil.Invoke(peer, state => ((ElementProxy)state).InContextGetProviderOptions(), this); + return ElementUtil.Invoke(peer, static (state) => state.InContextGetProviderOptions(), this); } } @@ -116,105 +110,81 @@ public IRawElementProviderSimple HostRawElementProvider { return null; } - hwndWrapper = (HostedWindowWrapper)ElementUtil.Invoke( - peer, - new DispatcherOperationCallback(InContextGetHostRawElementProvider), - null); + hwndWrapper = ElementUtil.Invoke(peer, static (state) => state.InContextGetHostRawElementProvider(), this); - if(hwndWrapper != null) + if (hwndWrapper != null) host = GetHostHelper(hwndWrapper); return host; } } - private IRawElementProviderSimple GetHostHelper(HostedWindowWrapper hwndWrapper) + private static IRawElementProviderSimple GetHostHelper(HostedWindowWrapper hwndWrapper) { return AutomationInteropProvider.HostProviderFromHandle(hwndWrapper.Handle); } // IRawElementProviderFragment methods... - public IRawElementProviderFragment Navigate( NavigateDirection direction ) + public IRawElementProviderFragment Navigate(NavigateDirection direction) { AutomationPeer peer = Peer; - if (peer == null) - { - return null; - } - return (IRawElementProviderFragment)ElementUtil.Invoke(peer, new DispatcherOperationCallback(InContextNavigate), direction); + + return peer is not null ? ElementUtil.Invoke(peer, static (state, direction) => state.InContextNavigate(direction), this, direction) : null; } - public int [ ] GetRuntimeId() + public int[] GetRuntimeId() { - AutomationPeer peer = Peer; - if (peer == null) - { - throw new ElementNotAvailableException(); - } - return (int []) ElementUtil.Invoke( peer, state => ((ElementProxy)state).InContextGetRuntimeId(), this); + AutomationPeer peer = Peer ?? throw new ElementNotAvailableException(); + + return ElementUtil.Invoke(peer, static (state) => state.InContextGetRuntimeId(), this); } public Rect BoundingRectangle { - get + get { - AutomationPeer peer = Peer; - if (peer == null) - { - throw new ElementNotAvailableException(); - } - return (Rect)ElementUtil.Invoke(peer, state => ((ElementProxy)state).InContextBoundingRectangle(), this); + AutomationPeer peer = Peer ?? throw new ElementNotAvailableException(); + + return ElementUtil.Invoke(peer, static (state) => state.InContextBoundingRectangle(), this); } } - public IRawElementProviderSimple [] GetEmbeddedFragmentRoots() + public IRawElementProviderSimple[] GetEmbeddedFragmentRoots() { return null; } public void SetFocus() { - AutomationPeer peer = Peer; - if (peer == null) - { - throw new ElementNotAvailableException(); - } + AutomationPeer peer = Peer ?? throw new ElementNotAvailableException(); + ElementUtil.Invoke(peer, state => ((ElementProxy)state).InContextSetFocus(), this); } public IRawElementProviderFragmentRoot FragmentRoot { - get + get { AutomationPeer peer = Peer; - if (peer == null) - { - return null; - } - return (IRawElementProviderFragmentRoot) ElementUtil.Invoke( peer, state => ((ElementProxy)state).InContextFragmentRoot(), this); + + return peer is not null ? ElementUtil.Invoke(peer, static (state) => state.InContextFragmentRoot(), this) : null; } } // IRawElementProviderFragmentRoot methods.. - public IRawElementProviderFragment ElementProviderFromPoint( double x, double y ) + public IRawElementProviderFragment ElementProviderFromPoint(double x, double y) { AutomationPeer peer = Peer; - if (peer == null) - { - return null; - } - return (IRawElementProviderFragment) ElementUtil.Invoke( peer, new DispatcherOperationCallback( InContextElementProviderFromPoint ), new Point( x, y ) ); + + return peer is not null ? ElementUtil.Invoke(peer, static (state, point) => state.InContextElementProviderFromPoint(point), this, new Point(x, y)) : null; } public IRawElementProviderFragment GetFocus() { AutomationPeer peer = Peer; - if (peer == null) - { - return null; - } - return (IRawElementProviderFragment) ElementUtil.Invoke( peer, state => ((ElementProxy)state).InContextGetFocus(), this); + + return peer is not null ? ElementUtil.Invoke(peer, static (state) => state.InContextGetFocus(), this) : null; } // Event support: EventMap is a static class and access is synchronized, so no need to access it in UI thread context. @@ -315,9 +285,9 @@ internal AutomationPeer Peer // The signature of most of the folling methods is "object func( object arg )", // since that's what the Conmtext.Invoke delegate requires. // Return the element at specified coords. - private object InContextElementProviderFromPoint( object arg ) + private IRawElementProviderFragment InContextElementProviderFromPoint(Point arg) { - Point point = (Point)arg; + Point point = arg; AutomationPeer peer = Peer; if (peer == null) { @@ -328,7 +298,7 @@ private object InContextElementProviderFromPoint( object arg ) } // Return proxy representing currently focused element (if any) - private object InContextGetFocus() + private IRawElementProviderFragment InContextGetFocus() { // Note: - what if a custom element - eg anchor in a text box - has focus? // won't have a UIElement there, can we even find the host? @@ -344,20 +314,16 @@ private object InContextGetFocus() } // redirect to AutomationPeer - private object InContextGetPatternProvider(object arg) + private int InContextGetPatternProvider(int arg) { - AutomationPeer peer = Peer; - if (peer == null) - { - throw new ElementNotAvailableException(); - } - return peer.GetWrappedPattern((int)arg); + AutomationPeer peer = Peer ?? throw new ElementNotAvailableException(); + + return (int)peer.GetWrappedPattern(arg); } // Return proxy representing element in specified direction (parent/next/firstchild/etc.) - private object InContextNavigate( object arg ) + private IRawElementProviderFragment InContextNavigate(NavigateDirection navigateDirection) { - NavigateDirection direction = (NavigateDirection) arg; AutomationPeer dest; AutomationPeer peer = Peer; if (peer == null) @@ -365,7 +331,7 @@ private object InContextNavigate( object arg ) return null; } - switch( direction ) + switch (navigateDirection) { case NavigateDirection.Parent: dest = peer.GetParent(); @@ -411,7 +377,7 @@ private object InContextNavigate( object arg ) // Return value for specified property; or null if not supported - private object InContextGetProviderOptions() + private ProviderOptions InContextGetProviderOptions() { ProviderOptions options = ProviderOptions.ServerSideProvider; AutomationPeer peer = Peer; @@ -426,63 +392,48 @@ private object InContextGetProviderOptions() } // Return value for specified property; or null if not supported - private object InContextGetPropertyValue ( object arg ) + private int InContextGetPropertyValue(int property) { - AutomationPeer peer = Peer; - if (peer == null) - { - throw new ElementNotAvailableException(); - } - return peer.GetPropertyValue((int)arg); + AutomationPeer peer = Peer ?? throw new ElementNotAvailableException(); + + return (int)peer.GetPropertyValue(property); } /// Returns whether this is the Root of the WCP tree or not - private object InContextGetHostRawElementProvider( object unused ) + private HostedWindowWrapper InContextGetHostRawElementProvider() { AutomationPeer peer = Peer; - if (peer == null) - { - return null; - } - return peer.GetHostRawElementProvider(); + + return peer?.GetHostRawElementProvider(); } // Return unique ID for this element... - private object InContextGetRuntimeId() + private int[] InContextGetRuntimeId() { - AutomationPeer peer = Peer; - if (peer == null) - { - throw new ElementNotAvailableException(); - } + AutomationPeer peer = Peer ?? throw new ElementNotAvailableException(); + return peer.GetRuntimeId(); } // Return bounding rectangle (screen coords) for this element... - private object InContextBoundingRectangle() + private Rect InContextBoundingRectangle() { - AutomationPeer peer = Peer; - if (peer == null) - { - throw new ElementNotAvailableException(); - } + AutomationPeer peer = Peer ?? throw new ElementNotAvailableException(); + return peer.GetBoundingRectangle(); } // Set focus to this element... private object InContextSetFocus() { - AutomationPeer peer = Peer; - if (peer == null) - { - throw new ElementNotAvailableException(); - } + AutomationPeer peer = Peer ?? throw new ElementNotAvailableException(); + peer.SetFocus(); return null; } // Return proxy representing the root of this WCP tree... - private object InContextFragmentRoot() + private IRawElementProviderFragmentRoot InContextFragmentRoot() { AutomationPeer peer = Peer; AutomationPeer root = peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs index 39a7e4bb601..8fbfea4f57e 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs @@ -1,315 +1,196 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Windows.Threading; -using System.Windows; -using System.Windows.Interop; -using System.Windows.Media; -using System.Windows.Automation; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using System.Windows.Automation.Peers; -using MS.Win32; -using MS.Internal.Media; -using System.Runtime.InteropServices; +using System.Windows.Automation; +using System.Windows.Threading; + +#nullable enable -using MS.Internal.PresentationCore; +namespace MS.Internal.Automation; -namespace MS.Internal.Automation +/// +/// Utility class for working with . +/// +internal static class ElementUtil { - // static class providing utility information for working with WCP elements - internal class ElementUtil + private readonly struct ReturnInfo { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + public Exception? StoredException { get; init; } + public bool Completed { get; init; } - // static class, so use private ctor - private ElementUtil() { } - - #endregion Constructors + public TReturn Value { get; init; } + } + ///// + ///// Provides a helper to invoke work on the UI thread, re-throwing all exceptions on the thread that invoked this execution. + ///// + //internal static TReturn Invoke(AutomationPeer peer, Func work) + //{ + // // Null dispatcher likely means the visual is in bad shape + // Dispatcher dispatcher = peer.Dispatcher ?? throw new ElementNotAvailableException(); + + // static ReturnInfo ExceptionWrapper(Func func) + // { + // try + // { + // return new ReturnInfo() { Value = func(), Completed = true }; + // } + // catch (Exception e) + // { + // return new ReturnInfo() { StoredException = e, Completed = true }; + // } + // } + + // ReturnInfo retVal = dispatcher.Invoke(ExceptionWrapper, DispatcherPriority.Send, TimeSpan.FromMinutes(3), work); + + // return HandleReturnValue(dispatcher, in retVal); + //} + + /// + /// Provides a helper to invoke work on the UI thread, re-throwing all exceptions on the thread that invoked this execution. + /// + internal static TReturn Invoke(AutomationPeer peer, Func work, TArg arg) + { + // Null dispatcher likely means the visual is in bad shape + Dispatcher dispatcher = peer.Dispatcher ?? throw new ElementNotAvailableException(); - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods + ReturnInfo retVal = dispatcher.Invoke(ExceptionWrapper, DispatcherPriority.Send, TimeSpan.FromMinutes(3), work, arg); - internal static Visual GetParent( Visual el ) + static ReturnInfo ExceptionWrapper(Func func, TArg arg) { - return VisualTreeHelper.GetParent(el) as Visual; - } - - internal static Visual GetFirstChild( Visual el ) - { - if (el == null) + try { - return null; + return new ReturnInfo() { Value = func(arg), Completed = true }; } - - return FindVisibleSibling ( el, 0, true ); - } - - internal static Visual GetLastChild( Visual el ) - { - if (el == null) + catch (Exception e) { - return null; + return new ReturnInfo() { StoredException = e, Completed = true }; } - - return FindVisibleSibling ( el, el.InternalVisualChildrenCount - 1, false ); } - // Warning: Method is O(N). See FindVisibleSibling function for more information. - internal static Visual GetNextSibling( Visual el ) - { - // To get next/previous sibling, have to find out where we - // are in our parent's children collection (ie. our siblings) - Visual parent = VisualTreeHelper.GetParent(el) as Visual; - // If parent is null, we're at root, so have no siblings - if (parent == null) - { - return null; - } - return FindVisibleSibling ( parent, el, searchForwards: true); - } + return HandleReturnValue(dispatcher, in retVal); + } - // Warning: Method is O(N). See FindVisibleSibling function for more information. - internal static Visual GetPreviousSibling( Visual el ) - { - // To get next/previous sibling, have to find out where we - // are in our parent's children collection (ie. our siblings) - Visual parent = VisualTreeHelper.GetParent(el) as Visual; - // If parent is null, we're at root, so have no siblings - if (parent == null) - { - return null; - } + /// + /// Provides a helper to invoke work on the UI thread, re-throwing all exceptions on the thread that invoked this execution. + /// + internal static TReturn Invoke(AutomationPeer peer, Func work, TArg1 arg1, TArg2 arg2) + { + // Null dispatcher likely means the visual is in bad shape + Dispatcher dispatcher = peer.Dispatcher ?? throw new ElementNotAvailableException(); - return FindVisibleSibling ( parent, el, searchForwards: false); - } + ReturnInfo retVal = dispatcher.Invoke(ExceptionWrapper, DispatcherPriority.Send, TimeSpan.FromMinutes(3), work, arg1, arg2); - internal static Visual GetRoot( Visual el ) + static ReturnInfo ExceptionWrapper(Func func, TArg1 arg1, TArg2 arg2) { - // Keep moving up parent chain till we reach the top... - Visual scan = el; - for( ; ; ) + try { - Visual test = VisualTreeHelper.GetParent(scan) as Visual; - if( test == null ) - break; - scan = test; + return new ReturnInfo() { Value = func(arg1, arg2), Completed = true }; + } + catch (Exception e) + { + return new ReturnInfo() { StoredException = e, Completed = true }; } - return scan; } - // Get bounding rectangle, in coords relative to root (not screen) - internal static Rect GetLocalRect( UIElement element ) - { - // Get top-most visual. - Visual parent = GetRoot( element ); - - // Get the points for the rectangle and transform them. - double height = element.RenderSize.Height; - double width = element.RenderSize.Width; - Rect rect = new Rect(0, 0, width, height); - - GeneralTransform g = element.TransformToAncestor(parent); - return g.TransformBounds(rect); - } + return HandleReturnValue(dispatcher, in retVal); + } - // Get bounding rectangle, relative to screen - internal static Rect GetScreenRect( IntPtr hwnd, UIElement el ) - { - Rect rc = GetLocalRect( el ); - - // Map from local to screen coords... - NativeMethods.RECT rcWin32 = new NativeMethods.RECT( (int) rc.Left, (int) rc.Top, (int) rc.Right, (int) rc.Bottom ); - try + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static TReturn HandleReturnValue(Dispatcher dispatcher, ref readonly ReturnInfo retVal) + { + if (!retVal.Completed) + { + if (dispatcher.HasShutdownStarted) { - SafeSecurityHelper.TransformLocalRectToScreen(new HandleRef(null, hwnd), ref rcWin32); + ThrowInvalidOperationException(); } - catch (System.ComponentModel.Win32Exception) + else { - return Rect.Empty; + ThrowTimeoutException(); } - - rc = new Rect( rcWin32.left, rcWin32.top, rcWin32.right - rcWin32.left, rcWin32.bottom - rcWin32.top ); - - return rc; } - // Get element at given point (screen coords) - internal static Visual GetElementFromPoint( IntPtr hwnd, Visual root, Point pointScreen ) - { - HwndSource hwndSource = HwndSource.CriticalFromHwnd(hwnd); + if (retVal.StoredException is not null) + UnwrapException(retVal.StoredException); - if(hwndSource == null) - return null; + return retVal.Value; + } - Point pointClient = PointUtil.ScreenToClient( pointScreen, hwndSource ); - Point pointRoot = PointUtil.ClientToRoot(pointClient, hwndSource); - PointHitTestResult result = VisualTreeUtils.AsNearestPointHitTestResult(VisualTreeHelper.HitTest(root, pointRoot)); - Visual visual = result?.VisualHit; + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowInvalidOperationException() => throw new InvalidOperationException(SR.AutomationDispatcherShutdown); + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowTimeoutException() => throw new TimeoutException(SR.AutomationDispatcherShutdown); - return visual; - } + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] + private static void UnwrapException(Exception exception) => throw exception; - // Ensures that an element is enabled; throws exception otherwise - internal static void CheckEnabled(Visual visual) - { - UIElement el = visual as UIElement; - - if( el != null && ! el.IsEnabled ) - { - throw new ElementNotEnabledException(); - } - } + /// + /// Provides a helper to invoke work on the UI thread, re-throwing all exceptions on the thread that invoked this execution. + /// + [OverloadResolutionPriority(1)] // We raise the priority here to avoid generic explosion when it's not needed + internal static object Invoke(AutomationPeer peer, DispatcherOperationCallback work, object arg) + { + // Null dispatcher likely means the visual is in bad shape + Dispatcher dispatcher = peer.Dispatcher ?? throw new ElementNotAvailableException(); - internal static object Invoke(AutomationPeer peer, DispatcherOperationCallback work, object arg) - { - Dispatcher dispatcher = peer.Dispatcher; + Exception? remoteException = null; + bool completed = false; - // Null dispatcher likely means the visual is in bad shape! - if( dispatcher == null ) + object retVal = dispatcher.Invoke( + DispatcherPriority.Send, + TimeSpan.FromMinutes(3), + (DispatcherOperationCallback)delegate (object workArg) { - throw new ElementNotAvailableException(); - } - - Exception remoteException = null; - bool completed = false; - - object retVal = dispatcher.Invoke( - DispatcherPriority.Send, - TimeSpan.FromMinutes(3), - (DispatcherOperationCallback) delegate(object unused) + try { - try - { - return work(arg); - } - catch(Exception e) - { - remoteException = e; - return null; - } - catch //for non-CLS Compliant exceptions - { - remoteException = null; - return null; - } - finally - { - completed = true; - } -}, - null); - - if(completed) - { - if(remoteException != null) + return work(workArg); + } + catch (Exception e) { - throw remoteException; + remoteException = e; + return null; } - } - else - { - bool dispatcherInShutdown = dispatcher.HasShutdownStarted; - - if(dispatcherInShutdown) + catch //for non-CLS Compliant exceptions { - throw new InvalidOperationException(SR.AutomationDispatcherShutdown); + remoteException = null; + return null; } - else + finally { - throw new TimeoutException(SR.AutomationTimeout); + completed = true; } - } - - return retVal; -} - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ + }, + arg); - // Potential enhancement: Consider Visual3Ds in this walk? - // Does this walk need to continue through the Visual3D tree once - // we have UIElement3D? - private static Visual FindVisibleSibling ( Visual parent, int start, bool searchForwards) + if (completed) { - int index = start; - int childrenCount = parent.InternalVisualChildrenCount; - - while ( index >= 0 && index < childrenCount ) + if (remoteException is not null) { - Visual sibling = parent.InternalGetVisualChild(index); - - // if its visible or something other than a UIElement keep it - if ( !(sibling is UIElement) || (((UIElement)sibling).Visibility == Visibility.Visible ) ) - return sibling; - - index += searchForwards ? 1 : -1; + throw remoteException; } - - return null; } - - // Potential enhancement - Consider Visual3Ds in this walk? - // Does this walk need to continue through the Visual3D tree once - // we have UIElement3D? - // - // WARNING: This method is O(N) and can therefore lead to O(N^2) algorithms. - private static Visual FindVisibleSibling(Visual parent, Visual child, bool searchForwards) + else { - // - // First we figure out the index of the specified child Visual. This is why the runtime - // of this method is O(n). - - int childrenCount = parent.InternalVisualChildrenCount; - int childIndex; + bool dispatcherInShutdown = dispatcher.HasShutdownStarted; - for (childIndex = 0; childIndex < childrenCount; childIndex++) + if (dispatcherInShutdown) { - Visual current = parent.InternalGetVisualChild(childIndex); - if (current == child) - { - // Found the child. - break; - } + throw new InvalidOperationException(SR.AutomationDispatcherShutdown); } - - // - // Now that we have the child index, we can go and lookup the sibling. - if(searchForwards) - return FindVisibleSibling(parent, childIndex+1, searchForwards); // (FindVisibleSibling can deal with out of range indices). else - return FindVisibleSibling(parent, childIndex-1, searchForwards); // (FindVisibleSibling can deal with out of range indices). + { + throw new TimeoutException(SR.AutomationTimeout); + } } - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - // static class, so no private fields - - #endregion Private Fields + return retVal; } } - - - diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs index 112e0192bf1..7f001157606 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -56,23 +56,17 @@ private GridProviderWrapper( AutomationPeer peer, IGridProvider iface ) public IRawElementProviderSimple GetItem(int row, int column) { - return (IRawElementProviderSimple) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetItem ), new int [ ] { row, column } ); - } + return ElementUtil.Invoke(_peer, static (state, rowColumn) => state.GetItem(rowColumn[0], rowColumn[1]), this, new int[] { row, column }); + } public int RowCount { - get - { - return (int) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetRowCount ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.RowCount, this); } public int ColumnCount { - get - { - return (int) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetColumnCount ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.ColumnCount, this); } #endregion Interface IGridProvider @@ -93,33 +87,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object GetItem( object arg ) - { - int [ ] coords = (int [ ]) arg; - return _iface.GetItem( coords[ 0 ], coords[ 1 ] ); - } - - private object GetRowCount( object unused ) - { - return _iface.RowCount; - } - - private object GetColumnCount( object unused ) - { - return _iface.ColumnCount; - } - - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs index 9b6b1dd8d90..530c2c3c06e 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs @@ -11,6 +11,7 @@ using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using System.Globalization; namespace System.Windows.Threading { @@ -189,7 +190,7 @@ public Thread Thread /// /// True if the calling thread has access to this object. /// - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Never)] public bool CheckAccess() { return Thread == Thread.CurrentThread; @@ -204,7 +205,7 @@ public bool CheckAccess() /// This method is public so that derived classes can probe to /// see if the calling thread has access to itself. /// - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Never)] public void VerifyAccess() { if(!CheckAccess()) @@ -298,7 +299,7 @@ public static void PushFrame(DispatcherFrame frame) { ArgumentNullException.ThrowIfNull(frame); - Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + Dispatcher dispatcher = CurrentDispatcher; if(dispatcher._hasShutdownFinished) // Dispatcher thread - no lock needed for read { throw new InvalidOperationException(SR.DispatcherHasShutdown); @@ -323,7 +324,7 @@ public static void PushFrame(DispatcherFrame frame) public static void ExitAllFrames() { - Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + Dispatcher dispatcher = CurrentDispatcher; if(dispatcher._frameDepth > 0) { dispatcher._exitAllFrames = true; @@ -580,22 +581,8 @@ public void Invoke(Action callback, DispatcherPriority priority, CancellationTok try { - DispatcherSynchronizationContext newSynchronizationContext; - if(BaseCompatibilityPreferences.GetReuseDispatcherSynchronizationContextInstance()) - { - newSynchronizationContext = _defaultDispatcherSynchronizationContext; - } - else - { - if(BaseCompatibilityPreferences.GetFlowDispatcherSynchronizationContextPriority()) - { - newSynchronizationContext = new DispatcherSynchronizationContext(this, priority); - } - else - { - newSynchronizationContext = new DispatcherSynchronizationContext(this, DispatcherPriority.Normal); - } - } + DispatcherSynchronizationContext newSynchronizationContext = DispatcherUtils.GetOrCreateContext(this, priority); + SynchronizationContext.SetSynchronizationContext(newSynchronizationContext); callback(); @@ -608,7 +595,7 @@ public void Invoke(Action callback, DispatcherPriority priority, CancellationTok } // Slow-Path: go through the queue. - DispatcherOperation operation = new DispatcherOperation(this, priority, callback); + DispatcherOperation operation = new DispatcherOperationAction(this, priority, callback); InvokeImpl(operation, cancellationToken, timeout); } @@ -702,12 +689,12 @@ public TResult Invoke(Func callback, DispatcherPriority priori /// /// The return value from the delegate being invoked. /// - public TResult Invoke(Func callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout) + public TResult Invoke(Func callback, DispatcherPriority priority, TimeSpan timeout, TArg1 arg1, TArg2 arg2) { ArgumentNullException.ThrowIfNull(callback); ValidatePriority(priority, "priority"); - if( timeout.TotalMilliseconds < 0 && + if (timeout.TotalMilliseconds < 0 && timeout != TimeSpan.FromMilliseconds(-1)) { throw new ArgumentOutOfRangeException(nameof(timeout)); @@ -716,28 +703,290 @@ public TResult Invoke(Func callback, DispatcherPriority priori // Fast-Path: if on the same thread, and invoking at Send priority, // and the cancellation token is not already canceled, then just // call the callback directly. - if(!cancellationToken.IsCancellationRequested && priority == DispatcherPriority.Send && CheckAccess()) + if (priority == DispatcherPriority.Send && CheckAccess()) + { + SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; + + try + { + DispatcherSynchronizationContext newSynchronizationContext = DispatcherUtils.GetOrCreateContext(this, priority); + + SynchronizationContext.SetSynchronizationContext(newSynchronizationContext); + + return callback(arg1, arg2); + } + finally + { + SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext); + } + } + + // Slow-Path: go through the queue. + DispatcherOperation operation = new(this, priority, callback, arg1, arg2); + return InvokeImpl(operation, CancellationToken.None, timeout); + } + + /// + /// Executes the specified Func synchronously on the + /// thread that the Dispatcher was created on. + /// + /// + /// A Func delegate to invoke through the dispatcher. + /// + /// + /// The priority that determines in what order the specified + /// callback is invoked relative to the other pending operations + /// in the Dispatcher. + /// + /// + /// A cancellation token that can be used to cancel the operation. + /// If the operation has not started, it will be aborted when the + /// cancellation token is canceled. If the operation has started, + /// the operation can cooperate with the cancellation request. + /// + /// + /// The minimum amount of time to wait for the operation to start. + /// Once the operation has started, it will complete before this method + /// returns. + /// + /// + /// The return value from the delegate being invoked. + /// + public TResult Invoke(Func callback, DispatcherPriority priority, TimeSpan timeout, TArg1 arg1, TArg2 arg2, TArg3 arg3) + { + ArgumentNullException.ThrowIfNull(callback); + ValidatePriority(priority, "priority"); + + if (timeout.TotalMilliseconds < 0 && + timeout != TimeSpan.FromMilliseconds(-1)) + { + throw new ArgumentOutOfRangeException(nameof(timeout)); + } + + // Fast-Path: if on the same thread, and invoking at Send priority, + // and the cancellation token is not already canceled, then just + // call the callback directly. + if (priority == DispatcherPriority.Send && CheckAccess()) + { + SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; + + try + { + DispatcherSynchronizationContext newSynchronizationContext = DispatcherUtils.GetOrCreateContext(this, priority); + + SynchronizationContext.SetSynchronizationContext(newSynchronizationContext); + + return callback(arg1, arg2, arg3); + } + finally + { + SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext); + } + } + + // Slow-Path: go through the queue. + DispatcherOperation operation = new(this, priority, callback, arg1, arg2, arg3); + return InvokeImpl(operation, CancellationToken.None, timeout); + } + + /// + /// Executes the specified Func synchronously on the + /// thread that the Dispatcher was created on. + /// + /// + /// A Func delegate to invoke through the dispatcher. + /// + /// + /// The priority that determines in what order the specified + /// callback is invoked relative to the other pending operations + /// in the Dispatcher. + /// + /// + /// A cancellation token that can be used to cancel the operation. + /// If the operation has not started, it will be aborted when the + /// cancellation token is canceled. If the operation has started, + /// the operation can cooperate with the cancellation request. + /// + /// + /// The minimum amount of time to wait for the operation to start. + /// Once the operation has started, it will complete before this method + /// returns. + /// + /// + /// The return value from the delegate being invoked. + /// + public TResult Invoke(Func callback, DispatcherPriority priority, TimeSpan timeout, TArg arg) + { + ArgumentNullException.ThrowIfNull(callback); + ValidatePriority(priority, "priority"); + + if (timeout.TotalMilliseconds < 0 && + timeout != TimeSpan.FromMilliseconds(-1)) + { + throw new ArgumentOutOfRangeException(nameof(timeout)); + } + + // Fast-Path: if on the same thread, and invoking at Send priority, + // and the cancellation token is not already canceled, then just + // call the callback directly. + if ( priority == DispatcherPriority.Send && CheckAccess()) { SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; try { - DispatcherSynchronizationContext newSynchronizationContext; - if(BaseCompatibilityPreferences.GetReuseDispatcherSynchronizationContextInstance()) + DispatcherSynchronizationContext newSynchronizationContext = DispatcherUtils.GetOrCreateContext(this, priority); + + SynchronizationContext.SetSynchronizationContext(newSynchronizationContext); + + return callback(arg); + } + finally + { + SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext); + } + } + + // Slow-Path: go through the queue. + DispatcherOperation operation = new(this, priority, callback, arg); + return InvokeImpl(operation, CancellationToken.None, timeout); + } + + private TResult InvokeImpl(DispatcherOperation operation, CancellationToken cancellationToken, TimeSpan timeout) + { + TResult result = default; + + Debug.Assert(timeout.TotalMilliseconds >= 0 || timeout == TimeSpan.FromMilliseconds(-1)); + Debug.Assert(operation.Priority != DispatcherPriority.Send || !CheckAccess()); // should be handled by caller + + if (!cancellationToken.IsCancellationRequested) + { + // This operation must be queued since it was invoked either to + // another thread, or at a priority other than Send. + InvokeAsyncImpl(operation, cancellationToken); + + CancellationToken ctTimeout = CancellationToken.None; + CancellationTokenRegistration ctTimeoutRegistration = new CancellationTokenRegistration(); + CancellationTokenSource ctsTimeout = null; + + if (timeout.TotalMilliseconds >= 0) + { + // Create a CancellationTokenSource that will abort the + // operation after the timeout. Note that this does not + // cancel the operation, just abort it if it is still pending. + ctsTimeout = new CancellationTokenSource(timeout); + ctTimeout = ctsTimeout.Token; + ctTimeoutRegistration = ctTimeout.Register(s => ((DispatcherOperation)s).Abort(), operation); + } + + + // We have already registered with the cancellation tokens + // (both provided by the user, and one for the timeout) to + // abort the operation when they are canceled. If the + // operation has already started when the timeout expires, + // we still wait for it to complete. This is different + // than simply waiting on the operation with a timeout + // because we are the ones queueing the dispatcher + // operation, not the caller. We can't leave the operation + // in a state that it might execute if we return that it did not + // invoke. + try + { + operation.Wait(); + + Debug.Assert(operation.Status == DispatcherOperationStatus.Completed || + operation.Status == DispatcherOperationStatus.Aborted); + + // Old async semantics return from Wait without + // throwing an exception if the operation was aborted. + // There is no need to test the timout condition, since + // the old async semantics would just return the result, + // which would be null. + + // This should not block because either the operation + // is using the old async sematics, or the operation + // completed successfully. + result = operation.Result; + } + catch (OperationCanceledException) + { + Debug.Assert(operation.Status == DispatcherOperationStatus.Aborted); + + // New async semantics will throw an exception if the + // operation was aborted. Here we convert that + // exception into a timeout exception if the timeout + // has expired (admittedly a weak relationship + // assuming causality). + if (ctTimeout.IsCancellationRequested) { - newSynchronizationContext = _defaultDispatcherSynchronizationContext; + // The operation was canceled because of the + // timeout, throw a TimeoutException instead. + throw new TimeoutException(); } else { - if(BaseCompatibilityPreferences.GetFlowDispatcherSynchronizationContextPriority()) - { - newSynchronizationContext = new DispatcherSynchronizationContext(this, priority); - } - else - { - newSynchronizationContext = new DispatcherSynchronizationContext(this, DispatcherPriority.Normal); - } + // The operation was canceled from some other reason. + throw; } + } + finally + { + ctTimeoutRegistration.Dispose(); + ctsTimeout?.Dispose(); + } + } + + return result; + } + + /// + /// Executes the specified Func synchronously on the + /// thread that the Dispatcher was created on. + /// + /// + /// A Func delegate to invoke through the dispatcher. + /// + /// + /// The priority that determines in what order the specified + /// callback is invoked relative to the other pending operations + /// in the Dispatcher. + /// + /// + /// A cancellation token that can be used to cancel the operation. + /// If the operation has not started, it will be aborted when the + /// cancellation token is canceled. If the operation has started, + /// the operation can cooperate with the cancellation request. + /// + /// + /// The minimum amount of time to wait for the operation to start. + /// Once the operation has started, it will complete before this method + /// returns. + /// + /// + /// The return value from the delegate being invoked. + /// + public TResult Invoke(Func callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout) + { + ArgumentNullException.ThrowIfNull(callback); + ValidatePriority(priority, "priority"); + + if( timeout.TotalMilliseconds < 0 && + timeout != TimeSpan.FromMilliseconds(-1)) + { + throw new ArgumentOutOfRangeException(nameof(timeout)); + } + + // Fast-Path: if on the same thread, and invoking at Send priority, + // and the cancellation token is not already canceled, then just + // call the callback directly. + if(!cancellationToken.IsCancellationRequested && priority == DispatcherPriority.Send && CheckAccess()) + { + SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; + + try + { + DispatcherSynchronizationContext newSynchronizationContext = DispatcherUtils.GetOrCreateContext(this, priority); + SynchronizationContext.SetSynchronizationContext(newSynchronizationContext); return callback(); @@ -750,7 +999,7 @@ public TResult Invoke(Func callback, DispatcherPriority priori // Slow-Path: go through the queue. DispatcherOperation operation = new DispatcherOperation(this, priority, callback); - return (TResult) InvokeImpl(operation, cancellationToken, timeout); + return InvokeImpl(operation, cancellationToken, timeout); } /// @@ -820,7 +1069,7 @@ public DispatcherOperation InvokeAsync(Action callback, DispatcherPriority prior ArgumentNullException.ThrowIfNull(callback); ValidatePriority(priority, "priority"); - DispatcherOperation operation = new DispatcherOperation(this, priority, callback); + DispatcherOperation operation = new DispatcherOperationAction(this, priority, callback); InvokeAsyncImpl(operation, cancellationToken); return operation; @@ -901,7 +1150,7 @@ private DispatcherOperation LegacyBeginInvokeImpl(DispatcherPriority priority, D ValidatePriority(priority, "priority"); ArgumentNullException.ThrowIfNull(method); - DispatcherOperation operation = new DispatcherOperation(this, method, priority, args, numArgs); + DispatcherOperation operation = new DispatcherOperationLegacy(this, method, priority, args, numArgs); InvokeAsyncImpl(operation, CancellationToken.None); return operation; @@ -1278,22 +1527,8 @@ internal object LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, try { - DispatcherSynchronizationContext newSynchronizationContext; - if(BaseCompatibilityPreferences.GetReuseDispatcherSynchronizationContextInstance()) - { - newSynchronizationContext = _defaultDispatcherSynchronizationContext; - } - else - { - if(BaseCompatibilityPreferences.GetFlowDispatcherSynchronizationContextPriority()) - { - newSynchronizationContext = new DispatcherSynchronizationContext(this, priority); - } - else - { - newSynchronizationContext = new DispatcherSynchronizationContext(this, DispatcherPriority.Normal); - } - } + DispatcherSynchronizationContext newSynchronizationContext = DispatcherUtils.GetOrCreateContext(this, priority); + SynchronizationContext.SetSynchronizationContext(newSynchronizationContext); return WrappedInvoke(method, args, numArgs, null); @@ -1305,7 +1540,7 @@ internal object LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, } // Slow-Path: go through the queue. - DispatcherOperation operation = new DispatcherOperation(this, method, priority, args, numArgs); + DispatcherOperation operation = new DispatcherOperationLegacy(this, method, priority, args, numArgs); return InvokeImpl(operation, CancellationToken.None, timeout); } @@ -1333,7 +1568,7 @@ private object InvokeImpl(DispatcherOperation operation, CancellationToken cance // cancel the operation, just abort it if it is still pending. ctsTimeout = new CancellationTokenSource(timeout); ctTimeout = ctsTimeout.Token; - ctTimeoutRegistration = ctTimeout.Register(s => ((DispatcherOperation)s).Abort(), operation); + ctTimeoutRegistration = ctTimeout.Register(static s => ((DispatcherOperation)s).Abort(), operation); } @@ -1420,103 +1655,6 @@ public DispatcherProcessingDisabled DisableProcessing() return dpd; } -/* - /// - /// Reports the range of priorities that are considered - /// as foreground priorities. - /// - /// - /// A foreground priority is processed before input. - /// - public static PriorityRange ForegroundPriorityRange - { - get - { - return _foregroundPriorityRange; - } - } - - /// - /// Reports the range of priorities that are considered - /// as background priorities. - /// - /// - /// A background priority is processed after input. - /// - public static PriorityRange BackgroundPriorityRange - { - get - { - return _backgroundPriorityRange; - } - } - - /// - /// Reports the range of priorities that are considered - /// as idle priorities. - /// - /// - /// An idle priority is processed periodically after background - /// priorities have been processed. - /// - public static PriorityRange IdlePriorityRange - { - get - { - return _idlePriorityRange; - } - } - - /// - /// Represents a convenient foreground priority. - /// - /// - /// A foreground priority is processed before input. In general - /// you should define your own foreground priority to allow for - /// more fine-grained ordering of queued items. - /// - public static Priority ForegroundPriority - { - get - { - return _foregroundPriority; - } - } - - /// - /// Represents a convenient background priority. - /// - /// - /// A background priority is processed after input. In general you - /// should define your own background priority to allow for more - /// fine-grained ordering of queued items. - /// - public static Priority BackgroundPriority - { - get - { - return _backgroundPriority; - } - } - - /// - /// Represents a convenient idle priority. - /// - /// - /// An idle priority is processed periodically after background - /// priorities have been processed. In general you should define - /// your own idle priority to allow for more fine-grained ordering - /// of queued items. - /// - public static Priority IdlePriority - { - get - { - return _idlePriority; - } - } -*/ - /// /// Validates that a priority is suitable for use by the dispatcher. /// @@ -1541,7 +1679,7 @@ public static void ValidatePriority(DispatcherPriority priority, string paramete DispatcherPriority.Inactive != priority) // NOTE: should be Priority.Min { // If we move to a Priority class, this exception will have to change too. - throw new System.ComponentModel.InvalidEnumArgumentException(parameterName, (int)priority, typeof(DispatcherPriority)); + throw new InvalidEnumArgumentException(parameterName, (int)priority, typeof(DispatcherPriority)); } } @@ -1557,7 +1695,7 @@ public static void ValidatePriority(DispatcherPriority priority, string paramete /// /// True if the calling thread has access to this object. /// - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Advanced)] + [EditorBrowsable(EditorBrowsableState.Advanced)] public DispatcherHooks Hooks { get @@ -2459,7 +2597,7 @@ private void OnRequestProcessingFailure(string methodName) if (_reservedPtsCache is Tuple> tuple) { List list = tuple.Item2; - list.Add(String.Format(System.Globalization.CultureInfo.InvariantCulture, + list.Add(String.Format(CultureInfo.InvariantCulture, "{0:O} {1} failed", DateTime.Now, methodName)); // keep the list from growing too large @@ -2849,7 +2987,7 @@ private object[] CombineParameters(object arg, object[] args) internal DispatcherSynchronizationContext _defaultDispatcherSynchronizationContext; - internal object _instanceLock = new object(); // Also used by DispatcherOperation + internal Lock _instanceLock = new Lock(); // Also used by DispatcherOperation private PriorityQueue _queue; private List _timers = new List(); private long _timersVersion; diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.1Arg.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.1Arg.cs new file mode 100644 index 00000000000..f29a78b1583 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.1Arg.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Windows.Threading; + +/// +/// DispatcherOperation represents a delegate that has been posted to the queue. +/// +internal sealed class DispatcherOperation : DispatcherOperation +{ + private readonly TArg _arg; + + internal DispatcherOperation(Dispatcher dispatcher, DispatcherPriority priority, + Func func, TArg arg) : base(dispatcher, priority, func) + { + _arg = arg; + } + + protected sealed override TResult InvokeDelegateCore() + { + Func func = (Func)_method; + return func(_arg); + } +} + diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.2Arg.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.2Arg.cs new file mode 100644 index 00000000000..c03c31a4a98 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.2Arg.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Windows.Threading; + +/// +/// DispatcherOperation represents a delegate that has been posted to the queue. +/// +internal sealed class DispatcherOperation : DispatcherOperation +{ + private readonly TArg1 _arg1; + private readonly TArg2 _arg2; + + internal DispatcherOperation(Dispatcher dispatcher, DispatcherPriority priority, Func func, + TArg1 arg1, TArg2 arg2) : base(dispatcher, priority, func) + { + _arg1 = arg1; + _arg2 = arg2; + } + + protected sealed override TResult InvokeDelegateCore() + { + Func func = (Func)_method; + return func(_arg1, _arg2); + } +} + diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.3Arg.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.3Arg.cs new file mode 100644 index 00000000000..7a2f751e204 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.3Arg.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Windows.Threading; + +/// +/// DispatcherOperation represents a delegate that has been posted to the queue. +/// +internal sealed class DispatcherOperation : DispatcherOperation +{ + private readonly TArg1 _arg1; + private readonly TArg2 _arg2; + private readonly TArg3 _arg3; + + internal DispatcherOperation(Dispatcher dispatcher, DispatcherPriority priority, Func func, + TArg1 arg1, TArg2 arg2, TArg3 arg3) : base(dispatcher, priority, func) + { + _arg1 = arg1; + _arg2 = arg2; + _arg3 = arg3; + } + + protected sealed override TResult InvokeDelegateCore() + { + Func func = (Func)_method; + return func(_arg1, _arg2, _arg3); + } +} + diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.cs new file mode 100644 index 00000000000..cb6294dc5cb --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.cs @@ -0,0 +1,104 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; +using MS.Internal; + +namespace System.Windows.Threading; + +/// +/// DispatcherOperation represents a delegate that has been posted to the queue. +/// +internal sealed class DispatcherOperationAction : DispatcherOperation +{ + internal DispatcherOperationAction(Dispatcher dispatcher, DispatcherPriority priority, Action func) : base( + dispatcher: dispatcher, + method: func, + priority: priority, + taskSource: new DispatcherOperationTaskSource(), + useAsyncSemantics: true) + { + } + + public override object Result + { + get + { + // New semantics require waiting for the operation to + // complete. + // + // Use DispatcherOperation.Wait instead of Task.Wait to handle + // waiting on the same thread. + Wait(); + + if (_status is DispatcherOperationStatus.Completed or DispatcherOperationStatus.Aborted) + { + // We know the operation has completed, and the + // _taskSource has been completed, so it safe to ask + // the task for the Awaiter, and the awaiter for the result. + // We don't actually care about the result, but this gives the + // Task the chance to throw any captured exceptions. + Task.GetAwaiter().GetResult(); + } + + return null; + } + } + + internal override void InvokeCompletions() + { + switch (_status) + { + case DispatcherOperationStatus.Aborted: + _taskSource.SetCanceled(); + break; + case DispatcherOperationStatus.Completed: + if (_exception != null) + { + _taskSource.SetException(_exception); + } + else + { + _taskSource.SetResult(null); + } + break; + + default: + Invariant.Assert(false, "Operation should be either Aborted or Completed!"); + break; + } + } + + protected override void InvokeImpl() + { + SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; + + try + { + // We are executing under the "foreign" execution context, but the SynchronizationContext + // must be for the correct dispatcher and the correct priority (since NETFX 4.5). + DispatcherSynchronizationContext newSynchronizationContext = DispatcherUtils.GetOrCreateContext(_dispatcher, _priority); + + SynchronizationContext.SetSynchronizationContext(newSynchronizationContext); + + // Win32 considers timers to be low priority. Avalon does not, since different timers are + // associated with different priorities. So we promote the timers before we invoke any work items. + _dispatcher.PromoteTimers(Environment.TickCount); + + try + { + Action action = (Action)_method; + action(); + } + catch (Exception e) + { + // Remember this for the later call to InvokeCompletions. + _exception = e; + } + } + finally + { + SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext); + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Event.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Event.cs new file mode 100644 index 00000000000..14189fb3233 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Event.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; + +namespace System.Windows.Threading; + +/// +/// DispatcherOperation represents a delegate that has been posted to the queue. +/// +public abstract partial class DispatcherOperation +{ + private protected sealed class DispatcherOperationEvent + { + private readonly DispatcherOperation _operation; + private readonly ManualResetEvent _event; + private readonly TimeSpan _timeout; + private bool _eventClosed; + + private Lock DispatcherLock + { + get => _operation.DispatcherLock; + } + + public DispatcherOperationEvent(DispatcherOperation op, TimeSpan timeout) + { + _operation = op; + _timeout = timeout; + _event = new ManualResetEvent(false); + _eventClosed = false; + + lock (DispatcherLock) + { + // We will set our event once the operation is completed or aborted. + _operation.Aborted += new EventHandler(OnCompletedOrAborted); + _operation.Completed += new EventHandler(OnCompletedOrAborted); + + // Since some other thread is dispatching this operation, it could + // have been dispatched while we were setting up the handlers. + // We check the state again and set the event ourselves if this + // happened. + if (_operation._status is not DispatcherOperationStatus.Pending and not DispatcherOperationStatus.Executing) + { + _event.Set(); + } + } + } + + private void OnCompletedOrAborted(object sender, EventArgs e) + { + lock (DispatcherLock) + { + if (!_eventClosed) + { + _event.Set(); + } + } + } + + public void WaitOne() + { + _event.WaitOne(_timeout, false); + + lock (DispatcherLock) + { + if (!_eventClosed) + { + // Cleanup the events. + _operation.Aborted -= new EventHandler(OnCompletedOrAborted); + _operation.Completed -= new EventHandler(OnCompletedOrAborted); + + // Close the event immediately instead of waiting for a GC + // because the Dispatcher is a a high-activity component and + // we could run out of events. + _event.Close(); + + _eventClosed = true; + } + } + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Frame.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Frame.cs new file mode 100644 index 00000000000..8cf0d053380 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Frame.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; + +namespace System.Windows.Threading; + +/// +/// DispatcherOperation represents a delegate that has been posted to the queue. +/// +public abstract partial class DispatcherOperation +{ + private protected sealed class DispatcherOperationFrame : DispatcherFrame + { + private readonly DispatcherOperation _operation; + private readonly Timer _waitTimer; + + // Note: we pass "exitWhenRequested=false" to the base + // DispatcherFrame constructor because we do not want to exit + // this frame if the dispatcher is shutting down. This is + // because we may need to invoke operations during the shutdown process. + public DispatcherOperationFrame(DispatcherOperation op, TimeSpan timeout) : base(false) + { + _operation = op; + + // We will exit this frame once the operation is completed or aborted. + _operation.Aborted += new EventHandler(OnCompletedOrAborted); + _operation.Completed += new EventHandler(OnCompletedOrAborted); + + // We will exit the frame if the operation is not completed within + // the requested timeout. + if (timeout.TotalMilliseconds > 0) + { + _waitTimer = new Timer(new TimerCallback(OnTimeout), + null, + timeout, + TimeSpan.FromMilliseconds(-1)); + } + + // Some other thread could have aborted the operation while we were + // setting up the handlers. We check the state again and mark the + // frame as "should not continue" if this happened. + if (_operation._status != DispatcherOperationStatus.Pending) + { + Exit(); + } + } + + private void OnCompletedOrAborted(object sender, EventArgs e) + { + Exit(); + } + + private void OnTimeout(object arg) + { + Exit(); + } + + private void Exit() + { + Continue = false; + + _waitTimer?.Dispose(); + + _operation.Aborted -= new EventHandler(OnCompletedOrAborted); + _operation.Completed -= new EventHandler(OnCompletedOrAborted); + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.cs new file mode 100644 index 00000000000..e90428a1218 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.cs @@ -0,0 +1,140 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using MS.Internal; + +namespace System.Windows.Threading; + +/// +/// DispatcherOperation represents a delegate that has been posted to the queue. +/// +public class DispatcherOperation : DispatcherOperation +{ + private TResult _result; + + internal DispatcherOperation(Dispatcher dispatcher, DispatcherPriority priority, Delegate func) : base( + dispatcher: dispatcher, + method: func, + priority: priority, + taskSource: new DispatcherOperationTaskSource(), + useAsyncSemantics: true) + { + } + + /// + /// Returns a Task representing the operation. + /// + public new Task Task + { + get + { + // Just upcast the base Task to what it really is. + return (Task)((DispatcherOperation)this).Task; + } + } + + /// + /// Returns an awaiter for awaiting the completion of the operation. + /// + /// + /// This method is intended to be used by compilers. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new TaskAwaiter GetAwaiter() + { + return Task.GetAwaiter(); + } + + /// + /// Returns the result of the operation if it has completed. + /// + public new TResult Result + { + get + { + // New semantics require waiting for the operation to complete. + // + // Use DispatcherOperation.Wait instead of Task.Wait to handle waiting on the same thread. + Wait(); + + if (_status is DispatcherOperationStatus.Completed or DispatcherOperationStatus.Aborted) + { + // We know the operation has completed, and the + // _taskSource has been completed, so it safe to ask + // the task for the Awaiter, and the awaiter for the result. + // We don't actually care about the result, but this gives the + // Task the chance to throw any captured exceptions. + Task.GetAwaiter().GetResult(); + } + + return _result; + } + } + + internal override void InvokeCompletions() + { + switch (_status) + { + case DispatcherOperationStatus.Aborted: + _taskSource.SetCanceled(); + break; + case DispatcherOperationStatus.Completed: + if (_exception != null) + { + _taskSource.SetException(_exception); + } + else + { + // Make sure we don't box at this stage + ((DispatcherOperationTaskSource)_taskSource).SetResult(_result); + } + break; + + default: + Invariant.Assert(false, "Operation should be either Aborted or Completed!"); + break; + } + } + + protected override void InvokeImpl() + { + SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; + + try + { + // We are executing under the "foreign" execution context, but the SynchronizationContext + // must be for the correct dispatcher and the correct priority (since NETFX 4.5). + DispatcherSynchronizationContext newSynchronizationContext = DispatcherUtils.GetOrCreateContext(_dispatcher, _priority); + + SynchronizationContext.SetSynchronizationContext(newSynchronizationContext); + + // Win32 considers timers to be low priority. Avalon does not, since different timers are + // associated with different priorities. So we promote the timers before we invoke any work items. + _dispatcher.PromoteTimers(Environment.TickCount); + + try + { + _result = InvokeDelegateCore(); + } + catch (Exception e) + { + // Remember this for the later call to InvokeCompletions. + _exception = e; + } + } + finally + { + SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext); + } + } + + protected virtual TResult InvokeDelegateCore() + { + Func func = (Func)_method; + return func(); + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs index 826b9b6aaa6..78feb20476f 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs @@ -1,11 +1,12 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Threading; using System.ComponentModel; -using System.Threading.Tasks; using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; using MS.Internal; +using MS.Internal.Interop; namespace System.Windows.Threading { @@ -13,91 +14,65 @@ namespace System.Windows.Threading /// DispatcherOperation represents a delegate that has been /// posted to the Dispatcher queue. /// - public class DispatcherOperation + public abstract partial class DispatcherOperation { - static DispatcherOperation() - { - _invokeInSecurityContext = new ContextCallback(InvokeInSecurityContext); - } + private CulturePreservingExecutionContext _executionContext; + + private protected readonly Dispatcher _dispatcher; + private protected readonly Delegate _method; - internal DispatcherOperation( - Dispatcher dispatcher, - Delegate method, - DispatcherPriority priority, - object args, - int numArgs, - DispatcherOperationTaskSource taskSource, - bool useAsyncSemantics) + private protected readonly bool _useAsyncSemantics; + + private protected DispatcherPriority _priority; + private protected Exception _exception; + + internal PriorityItem _item; // The Dispatcher sets this when it enqueues/deques the item. + internal DispatcherOperationStatus _status; // set from Dispatcher + + private EventHandler _aborted; + private EventHandler _completed; + + internal readonly DispatcherOperationTaskSource _taskSource; // also used from Dispatcher + + internal DispatcherOperation(Dispatcher dispatcher, Delegate method, DispatcherPriority priority, + DispatcherOperationTaskSource taskSource, bool useAsyncSemantics) { _dispatcher = dispatcher; _method = method; _priority = priority; - _numArgs = numArgs; - _args = args; _executionContext = CulturePreservingExecutionContext.Capture(); _taskSource = taskSource; _taskSource.Initialize(this); - + _useAsyncSemantics = useAsyncSemantics; } - internal DispatcherOperation( - Dispatcher dispatcher, - Delegate method, - DispatcherPriority priority, - object args, - int numArgs) : this( - dispatcher, - method, - priority, - args, - numArgs, - new DispatcherOperationTaskSource(), - false) - { - } + /// + /// Returns the Dispatcher that this operation was posted to. + /// + public Dispatcher Dispatcher => _dispatcher; - internal DispatcherOperation( - Dispatcher dispatcher, - DispatcherPriority priority, - Action action) : this( - dispatcher, - action, - priority, - null, - 0, - new DispatcherOperationTaskSource(), - true) - { - } - - internal DispatcherOperation( - Dispatcher dispatcher, - DispatcherPriority priority, - Delegate method, - object[] args) : this( - dispatcher, - method, - priority, - args, - -1, - new DispatcherOperationTaskSource(), - true) - { - } + private Lock DispatcherLock => _dispatcher._instanceLock; /// - /// Returns the Dispatcher that this operation was posted to. + /// The status of this operation. /// - public Dispatcher Dispatcher - { - get - { - return _dispatcher; - } - } + public DispatcherOperationStatus Status => _status; + + /// + /// Returns a Task representing the operation. + /// + public Task Task => _taskSource.GetTask(); + + /// + /// Name of this operation. + /// + /// + /// Returns a string representation of the operation to be invoked. + /// + internal string Name => _method.Method.DeclaringType + "." + _method.Method.Name; /// /// Gets or sets the priority of this operation within the @@ -105,44 +80,18 @@ public Dispatcher Dispatcher /// public DispatcherPriority Priority // NOTE: should be Priority { - get - { - return _priority; - } - + get => _priority; set { Dispatcher.ValidatePriority(value, "value"); - - if(value != _priority && _dispatcher.SetPriority(this, value)) + + if (value != _priority && _dispatcher.SetPriority(this, value)) { _priority = value; } } } - /// - /// The status of this operation. - /// - public DispatcherOperationStatus Status - { - get - { - return _status; - } - } - - /// - /// Returns a Task representing the operation. - /// - public Task Task - { - get - { - return _taskSource.GetTask(); - } - } - /// /// Returns an awaiter for awaiting the completion of the operation. /// @@ -179,12 +128,12 @@ public DispatcherOperationStatus Wait() /// public DispatcherOperationStatus Wait(TimeSpan timeout) { - if((_status == DispatcherOperationStatus.Pending || _status == DispatcherOperationStatus.Executing) && + if ((_status == DispatcherOperationStatus.Pending || _status == DispatcherOperationStatus.Executing) && timeout.TotalMilliseconds != 0) { - if(_dispatcher.Thread == Thread.CurrentThread) + if (_dispatcher.Thread == Thread.CurrentThread) { - if(_status == DispatcherOperationStatus.Executing) + if (_status == DispatcherOperationStatus.Executing) { // We are the dispatching thread, and the current operation state is // executing, which means that the operation is in the middle of @@ -193,7 +142,7 @@ public DispatcherOperationStatus Wait(TimeSpan timeout) // we throw an exception instead. throw new InvalidOperationException(SR.ThreadMayNotWaitOnOperationsAlreadyExecutingOnTheSameThread); } - + // We are the dispatching thread for this operation, so // we can't block. We will push a frame instead. DispatcherOperationFrame frame = new DispatcherOperationFrame(this, timeout); @@ -215,10 +164,9 @@ public DispatcherOperationStatus Wait(TimeSpan timeout) } } - if(_useAsyncSemantics) + if (_useAsyncSemantics) { - if(_status == DispatcherOperationStatus.Completed || - _status == DispatcherOperationStatus.Aborted) + if (_status is DispatcherOperationStatus.Completed or DispatcherOperationStatus.Aborted) { // We know the operation has completed, so it safe to ask // the task for the Awaiter, and the awaiter for the result. @@ -227,10 +175,10 @@ public DispatcherOperationStatus Wait(TimeSpan timeout) Task.GetAwaiter().GetResult(); } } - + return _status; } - + /// /// Aborts this operation. /// @@ -257,31 +205,13 @@ public bool Abort() _taskSource.SetCanceled(); // Raise the aborted event. - EventHandler aborted = _aborted; - if (aborted != null) - { - aborted(this, EventArgs.Empty); - } + _aborted?.Invoke(this, EventArgs.Empty); } } return removed; } - /// - /// Name of this operation. - /// - /// - /// Returns a string representation of the operation to be invoked. - /// - internal String Name - { - get - { - return _method.Method.DeclaringType + "." + _method.Method.Name; - } - } - /// /// ID of this operation. /// @@ -299,46 +229,19 @@ internal long Id unsafe { // we need a non-readonly field of a pointer-compatible type (using _priority) - fixed (DispatcherPriority* pb = &this._priority) + fixed (DispatcherPriority* pb = &_priority) { - addr = (long) pb; + addr = (long)pb; } } return addr; } } - + /// /// Returns the result of the operation if it has completed. /// - public object Result - { - get - { - if(_useAsyncSemantics) - { - // New semantics require waiting for the operation to - // complete. - // - // Use DispatcherOperation.Wait instead of Task.Wait to handle - // waiting on the same thread. - Wait(); - - if(_status == DispatcherOperationStatus.Completed || - _status == DispatcherOperationStatus.Aborted) - { - // We know the operation has completed, and the - // _taskSource has been completed, so it safe to ask - // the task for the Awaiter, and the awaiter for the result. - // We don't actually care about the result, but this gives the - // Task the chance to throw any captured exceptions. - Task.GetAwaiter().GetResult(); - } - } - - return _result; - } - } + public virtual object Result { get; } /// /// An event that is raised when the operation is aborted or canceled. @@ -349,15 +252,14 @@ public event EventHandler Aborted { lock (DispatcherLock) { - _aborted = (EventHandler) Delegate.Combine(_aborted, value); + _aborted = (EventHandler)Delegate.Combine(_aborted, value); } } - remove { - lock(DispatcherLock) + lock (DispatcherLock) { - _aborted = (EventHandler) Delegate.Remove(_aborted, value); + _aborted = (EventHandler)Delegate.Remove(_aborted, value); } } } @@ -376,21 +278,26 @@ public event EventHandler Completed { lock (DispatcherLock) { - _completed = (EventHandler) Delegate.Combine(_completed, value); + _completed = (EventHandler)Delegate.Combine(_completed, value); } } - remove { - lock(DispatcherLock) + lock (DispatcherLock) { - _completed = (EventHandler) Delegate.Remove(_completed, value); + _completed = (EventHandler)Delegate.Remove(_completed, value); } } } - + + // Note: this is called by the Dispatcher to actually invoke the completions for the operation. + internal abstract void InvokeCompletions(); + + // Invoke --> InvokeImpl + protected abstract void InvokeImpl(); + // Note: this is called by the Dispatcher to actually invoke the operation. - // Invoke --> InvokeInSecurityContext --> InvokeImpl + // Invoke --> InvokeImpl internal void Invoke() { // Mark this operation as executing. @@ -398,9 +305,9 @@ internal void Invoke() // Invoke the operation under the execution context that was // current when the operation was created. - if(_executionContext != null) + if (_executionContext != null) { - CulturePreservingExecutionContext.Run(_executionContext, _invokeInSecurityContext, this); + CulturePreservingExecutionContext.Run(_executionContext, static (state) => ((DispatcherOperation)state).InvokeImpl(), this); // Release any resources held by the execution context. _executionContext.Dispose(); @@ -409,17 +316,15 @@ internal void Invoke() else { // _executionContext can be null if someone called - // ExecutionContext.SupressFlow before calling BeginInvoke/Invoke. - // In this case we'll just call the invokation directly. - // SupressFlow is a privileged operation, so this is not a - // security hole. - _invokeInSecurityContext(this); + // ExecutionContext.SuppressFlow before calling BeginInvoke/Invoke. + // In this case we'll just call the invocation directly. + InvokeImpl(); } EventHandler handler; // either completed or aborted - lock(DispatcherLock) + lock (DispatcherLock) { - if(_exception is OperationCanceledException) + if (_exception is OperationCanceledException) { // A new way to abort/cancel an operation is to raise an // OperationCanceledException exception. This only works @@ -435,338 +340,14 @@ internal void Invoke() else { // The operation either completed, or a new version threw an - // exception and we caught it. There is no seperate event + // exception and we caught it. There is no separate event // for this, so we raise the same Completed event for both. handler = _completed; _status = DispatcherOperationStatus.Completed; } } - - if(handler != null) - { - handler(this, EventArgs.Empty); - } - } - - // Note: this is called by the Dispatcher to actually invoke the completions for the operation. - internal void InvokeCompletions() - { - switch(_status) - { - case DispatcherOperationStatus.Aborted: - _taskSource.SetCanceled(); - break; - - case DispatcherOperationStatus.Completed: - if(_exception != null) - { - _taskSource.SetException(_exception); - } - else - { - _taskSource.SetResult(_result); - } - break; - - default: - Invariant.Assert(false, "Operation should be either Aborted or Completed!"); - break; - } - } - - - // Invoke --> InvokeInSecurityContext --> InvokeImpl - private static void InvokeInSecurityContext(Object state) - { - DispatcherOperation operation = (DispatcherOperation) state; - operation.InvokeImpl(); - } - - // Invoke --> InvokeInSecurityContext --> InvokeImpl - private void InvokeImpl() - { - SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; - - try - { - // We are executing under the "foreign" execution context, but the - // SynchronizationContext must be for the correct dispatcher and - // priority. - DispatcherSynchronizationContext newSynchronizationContext; - if(BaseCompatibilityPreferences.GetReuseDispatcherSynchronizationContextInstance()) - { - newSynchronizationContext = Dispatcher._defaultDispatcherSynchronizationContext; - } - else - { - if(BaseCompatibilityPreferences.GetFlowDispatcherSynchronizationContextPriority()) - { - newSynchronizationContext = new DispatcherSynchronizationContext(_dispatcher, _priority); - } - else - { - newSynchronizationContext = new DispatcherSynchronizationContext(_dispatcher, DispatcherPriority.Normal); - } - } - SynchronizationContext.SetSynchronizationContext(newSynchronizationContext); - - - // Win32 considers timers to be low priority. Avalon does not, since different timers - // are associated with different priorities. So we promote the timers before we - // invoke any work items. - _dispatcher.PromoteTimers(Environment.TickCount); - - if(_useAsyncSemantics) - { - try - { - _result = InvokeDelegateCore(); - } - catch(Exception e) - { - // Remember this for the later call to InvokeCompletions. - _exception = e; - } - } - else - { - // Invoke the delegate and route exceptions through the dispatcher events. - _result = _dispatcher.WrappedInvoke(_method, _args, _numArgs, null); - - // Note: we do not catch exceptions, they flow out the the Dispatcher.UnhandledException handling. - } - } - finally - { - SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext); - } - } - - protected virtual object InvokeDelegateCore() - { - Action action = (Action) _method; - action(); - return null; - } - - private class DispatcherOperationFrame : DispatcherFrame - { - // Note: we pass "exitWhenRequested=false" to the base - // DispatcherFrame construsctor because we do not want to exit - // this frame if the dispatcher is shutting down. This is - // because we may need to invoke operations during the shutdown process. - public DispatcherOperationFrame(DispatcherOperation op, TimeSpan timeout) : base(false) - { - _operation = op; - - // We will exit this frame once the operation is completed or aborted. - _operation.Aborted += new EventHandler(OnCompletedOrAborted); - _operation.Completed += new EventHandler(OnCompletedOrAborted); - - // We will exit the frame if the operation is not completed within - // the requested timeout. - if(timeout.TotalMilliseconds > 0) - { - _waitTimer = new Timer(new TimerCallback(OnTimeout), - null, - timeout, - TimeSpan.FromMilliseconds(-1)); - } - - // Some other thread could have aborted the operation while we were - // setting up the handlers. We check the state again and mark the - // frame as "should not continue" if this happened. - if(_operation._status != DispatcherOperationStatus.Pending) - { - Exit(); - } -} - - private void OnCompletedOrAborted(object sender, EventArgs e) - { - Exit(); - } - - private void OnTimeout(object arg) - { - Exit(); - } - - private void Exit() - { - Continue = false; - - _waitTimer?.Dispose(); - - _operation.Aborted -= new EventHandler(OnCompletedOrAborted); - _operation.Completed -= new EventHandler(OnCompletedOrAborted); - } - private DispatcherOperation _operation; - private Timer _waitTimer; + handler?.Invoke(this, EventArgs.Empty); } - - private class DispatcherOperationEvent - { - public DispatcherOperationEvent(DispatcherOperation op, TimeSpan timeout) - { - _operation = op; - _timeout = timeout; - _event = new ManualResetEvent(false); - _eventClosed = false; - - lock(DispatcherLock) - { - // We will set our event once the operation is completed or aborted. - _operation.Aborted += new EventHandler(OnCompletedOrAborted); - _operation.Completed += new EventHandler(OnCompletedOrAborted); - - // Since some other thread is dispatching this operation, it could - // have been dispatched while we were setting up the handlers. - // We check the state again and set the event ourselves if this - // happened. - if(_operation._status != DispatcherOperationStatus.Pending && _operation._status != DispatcherOperationStatus.Executing) - { - _event.Set(); - } - } - } - - private void OnCompletedOrAborted(object sender, EventArgs e) - { - lock(DispatcherLock) - { - if(!_eventClosed) - { - _event.Set(); - } - } - } - - public void WaitOne() - { - _event.WaitOne(_timeout, false); - - lock(DispatcherLock) - { - if(!_eventClosed) - { - // Cleanup the events. - _operation.Aborted -= new EventHandler(OnCompletedOrAborted); - _operation.Completed -= new EventHandler(OnCompletedOrAborted); - - // Close the event immediately instead of waiting for a GC - // because the Dispatcher is a a high-activity component and - // we could run out of events. - _event.Close(); - - _eventClosed = true; - } - } - } - - private object DispatcherLock - { - get { return _operation.DispatcherLock; } - } - - private DispatcherOperation _operation; - private TimeSpan _timeout; - private ManualResetEvent _event; - private bool _eventClosed; - } - - private object DispatcherLock - { - get { return _dispatcher._instanceLock; } - } - - private CulturePreservingExecutionContext _executionContext; - private static readonly ContextCallback _invokeInSecurityContext; - - private readonly Dispatcher _dispatcher; - private DispatcherPriority _priority; - internal readonly Delegate _method; - private readonly object _args; - private readonly int _numArgs; - - internal DispatcherOperationStatus _status; // set from Dispatcher - private object _result; - private Exception _exception; - - internal PriorityItem _item; // The Dispatcher sets this when it enques/deques the item. - - private EventHandler _aborted; - private EventHandler _completed; - - internal readonly DispatcherOperationTaskSource _taskSource; // also used from Dispatcher - private readonly bool _useAsyncSemantics; } - - /// - /// DispatcherOperation represents a delegate that has been - /// posted to the Dispatcher queue. - /// - public class DispatcherOperation : DispatcherOperation - { - internal DispatcherOperation( - Dispatcher dispatcher, - DispatcherPriority priority, - Func func) : base( - dispatcher, - func, - priority, - null, - 0, - new DispatcherOperationTaskSource(), - true) - { - } - - /// - /// Returns a Task representing the operation. - /// - public new Task Task - { - get - { - // Just upcast the base Task to what it really is. - return (Task)((DispatcherOperation)this).Task; - } - } - - /// - /// Returns an awaiter for awaiting the completion of the operation. - /// - /// - /// This method is intended to be used by compilers. - /// - [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] - public new TaskAwaiter GetAwaiter() - { - return Task.GetAwaiter(); - } - - /// - /// Returns the result of the operation if it has completed. - /// - public new TResult Result - { - get - { - return (TResult) ((DispatcherOperation)this).Result; - } - } - - protected override object InvokeDelegateCore() - { - Func func = (Func) _method; - return func(); - } -} - - /// - /// A convenient delegate to use for dispatcher operations. - /// - public delegate object DispatcherOperationCallback(object arg); } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationCallback.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationCallback.cs new file mode 100644 index 00000000000..0e510947dd4 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationCallback.cs @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Windows.Threading; + +/// +/// A convenient delegate to use for dispatcher operations. +/// +public delegate object DispatcherOperationCallback(object arg); diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationLegacy.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationLegacy.cs new file mode 100644 index 00000000000..50a8d1cabd1 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationLegacy.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; +using MS.Internal; + +namespace System.Windows.Threading; + +/// +/// DispatcherOperation represents a delegate that has been posted to the queue. +/// +internal sealed class DispatcherOperationLegacy : DispatcherOperation +{ + private readonly object _args; + private readonly int _numArgs; + private object _result; + + private DispatcherOperationLegacy(Dispatcher dispatcher, Delegate method, DispatcherPriority priority, object args, int numArgs, + DispatcherOperationTaskSource taskSource, bool useAsyncSemantics) : base(dispatcher, method, priority, taskSource, useAsyncSemantics) + { + _numArgs = numArgs; + _args = args; + } + + internal DispatcherOperationLegacy(Dispatcher dispatcher, Delegate method, DispatcherPriority priority, object args, int numArgs) : this( + dispatcher, method, priority, args, numArgs, new DispatcherOperationTaskSource(), useAsyncSemantics: false) + { + } + + public override object Result + { + get + { + return _result; + } + } + + internal override void InvokeCompletions() + { + switch (_status) + { + case DispatcherOperationStatus.Aborted: + _taskSource.SetCanceled(); + break; + case DispatcherOperationStatus.Completed: + if (_exception != null) + { + _taskSource.SetException(_exception); + } + else + { + _taskSource.SetResult(_result); + } + break; + + default: + Invariant.Assert(false, "Operation should be either Aborted or Completed!"); + break; + } + } + + protected override void InvokeImpl() + { + SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; + + try + { + // We are executing under the "foreign" execution context, but the SynchronizationContext + // must be for the correct dispatcher and the correct priority (since NETFX 4.5). + DispatcherSynchronizationContext newSynchronizationContext = DispatcherUtils.GetOrCreateContext(_dispatcher, _priority); + + SynchronizationContext.SetSynchronizationContext(newSynchronizationContext); + + // Win32 considers timers to be low priority. Avalon does not, since different timers are + // associated with different priorities. So we promote the timers before we invoke any work items. + _dispatcher.PromoteTimers(Environment.TickCount); + + // Invoke the delegate and route exceptions through the dispatcher events. + _result = _dispatcher.WrappedInvoke(_method, _args, _numArgs, null); + + // Note: we do not catch exceptions, they flow out the the Dispatcher.UnhandledException handling. + } + finally + { + SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext); + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationTaskSource.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationTaskSource.cs index c6f3d616597..d023e7cd50b 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationTaskSource.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationTaskSource.cs @@ -1,75 +1,69 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Threading.Tasks; -namespace System.Windows.Threading +namespace System.Windows.Threading; + +/// +/// DispatcherOperation uses this class to access a TaskCompletionSource without being a generic itself. +/// +internal abstract class DispatcherOperationTaskSource +{ + public abstract void Initialize(DispatcherOperation operation); + public abstract Task GetTask(); + public abstract void SetCanceled(); + public abstract void SetResult(object result); + public abstract void SetException(Exception exception); +} + +internal sealed class DispatcherOperationTaskSource : DispatcherOperationTaskSource { - // DispatcherOperation uses this class to access a TaskCompletionSource - // without being a generic iteself. - internal abstract class DispatcherOperationTaskSource + private TaskCompletionSource _taskCompletionSource; + + /// + /// Create the underlying TaskCompletionSource and set the DispatcherOperation as the Task's AsyncState. + /// + /// + public override void Initialize(DispatcherOperation operation) + { + Debug.Assert(_taskCompletionSource is null); + + _taskCompletionSource = new TaskCompletionSource(new DispatcherOperationTaskMapping(operation)); + } + + public override Task GetTask() { - public abstract void Initialize(DispatcherOperation operation); - public abstract Task GetTask(); - public abstract void SetCanceled(); - public abstract void SetResult(object result); - public abstract void SetException(Exception exception); + Debug.Assert(_taskCompletionSource is not null); + + return _taskCompletionSource.Task; } - internal class DispatcherOperationTaskSource : DispatcherOperationTaskSource + public override void SetCanceled() { - // Create the underlying TaskCompletionSource and set the - // DispatcherOperation as the Task's AsyncState. - public override void Initialize(DispatcherOperation operation) - { - if(_taskCompletionSource != null) - { - throw new InvalidOperationException(); - } - - _taskCompletionSource = new TaskCompletionSource(new DispatcherOperationTaskMapping(operation)); - } - - public override Task GetTask() - { - if(_taskCompletionSource == null) - { - throw new InvalidOperationException(); - } - - return _taskCompletionSource.Task; - } - - public override void SetCanceled() - { - if(_taskCompletionSource == null) - { - throw new InvalidOperationException(); - } - - _taskCompletionSource.SetCanceled(); - } - - public override void SetResult(object result) - { - if(_taskCompletionSource == null) - { - throw new InvalidOperationException(); - } - - _taskCompletionSource.SetResult((TResult)result); - } - - public override void SetException(Exception exception) - { - if(_taskCompletionSource == null) - { - throw new InvalidOperationException(); - } - - _taskCompletionSource.SetException(exception); - } - - private TaskCompletionSource _taskCompletionSource; + Debug.Assert(_taskCompletionSource is not null); + + _taskCompletionSource.SetCanceled(); + } + + public override void SetResult(object result) + { + Debug.Assert(_taskCompletionSource is not null); + + _taskCompletionSource.SetResult((TResult)result); + } + + public void SetResult(TResult result) + { + Debug.Assert(_taskCompletionSource is not null); + + _taskCompletionSource.SetResult(result); + } + + public override void SetException(Exception exception) + { + Debug.Assert(_taskCompletionSource is not null); + + _taskCompletionSource.SetException(exception); } } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherUtils.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherUtils.cs new file mode 100644 index 00000000000..6f2905bcac0 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherUtils.cs @@ -0,0 +1,28 @@ +// 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; + +namespace System.Windows.Threading; + +internal static class DispatcherUtils +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static DispatcherSynchronizationContext GetOrCreateContext(Dispatcher dispatcher, DispatcherPriority priority) + { + // Not used since NETFX 4.5 + if (BaseCompatibilityPreferences.GetReuseDispatcherSynchronizationContextInstance()) + { + return dispatcher._defaultDispatcherSynchronizationContext; + } + + // This is our general path without any special app context switches + if (BaseCompatibilityPreferences.GetFlowDispatcherSynchronizationContextPriority()) + { + return new DispatcherSynchronizationContext(dispatcher, priority); + } + + // Compatibility path + return new DispatcherSynchronizationContext(dispatcher, DispatcherPriority.Normal); + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj b/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj index 38aa9a81d81..3d8d0a3ddc6 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj @@ -283,10 +283,19 @@ - + + + + + + + + + + diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs index 2d6c992000a..173c3b7c071 100644 --- a/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs @@ -134,6 +134,66 @@ public void BeginInvoke_InvokeDelegateObjectArray_Success() Assert.NotNull(operation.Task); } + [WpfFact] + public void Invoke_SameThread_TReturn_Success() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + + static int action() => 5; + // Send + current thread forces direct callback invocation + int result = dispatcher.Invoke(action); + + Assert.Equal(5, result); + } + + [WpfFact] + public void Invoke_SameThread_TReturn_TArg_Success() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + + static int action(int parameter) => parameter + 5; + // Send + current thread forces direct callback invocation + int result = dispatcher.Invoke(action, DispatcherPriority.Send, TimeSpan.FromSeconds(3), 4); + + Assert.Equal(9, result); + } + + [WpfFact] + public void Invoke_SameThread_DispatcherOperation_TReturn_TArg_Success() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + + static int action(int parameter) => parameter + 5; + // Anything different than Send + current thread is DispatcherOperation allocation and queue pass + int result = dispatcher.Invoke(action, DispatcherPriority.Background, TimeSpan.FromSeconds(3), 4); + + Assert.Equal(9, result); + } + + [WpfFact] + public void Invoke_SameThread_TReturn_TArg1_TArg2_Success() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + + static double action(int parameter, double doubleParameter) => parameter + doubleParameter + 5.5; + // Send + current thread forces direct callback invocation + double result = dispatcher.Invoke(action, DispatcherPriority.Send, TimeSpan.FromSeconds(3), 4, 14.5); + + Assert.Equal(24.0, result); + } + + [WpfFact] + public void Invoke_SameThread_DispatcherOperation_TReturn_TArg1_TArg2_Success() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + + static double action(int parameter, double doubleParameter) => parameter + doubleParameter + 5.5; + // Anything different than Send + current thread is DispatcherOperation allocation and queue pass + double result = dispatcher.Invoke(action, DispatcherPriority.Background, TimeSpan.FromSeconds(3), 4, 14.5); + + Assert.Equal(24.0, result); + } + [WpfTheory] [InlineData(DispatcherPriority.Invalid)] [InlineData(DispatcherPriority.Invalid - 1)] From b5e9f88f7d86a3eb1c87294897a29e1d2cbd0ea3 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Tue, 20 May 2025 22:03:23 +0200 Subject: [PATCH 02/54] some fixes --- .../System/Windows/Threading/Dispatcher.cs | 61 +++++++++++++++++++ ...ion.cs => DispatcherOperation.Action.0.cs} | 21 ++++--- .../Threading/DispatcherOperation.Action.1.cs | 23 +++++++ ...lt.cs => DispatcherOperation.TResult.0.cs} | 4 +- ...rg.cs => DispatcherOperation.TResult.1.cs} | 0 ...rg.cs => DispatcherOperation.TResult.2.cs} | 0 ...rg.cs => DispatcherOperation.TResult.3.cs} | 0 .../src/WindowsBase/WindowsBase.csproj | 11 ++-- 8 files changed, 105 insertions(+), 15 deletions(-) rename src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/{DispatcherOperation.Action.cs => DispatcherOperation.Action.0.cs} (88%) create mode 100644 src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.1.cs rename src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/{DispatcherOperation.TResult.cs => DispatcherOperation.TResult.0.cs} (97%) rename src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/{DispatcherOperation.1Arg.cs => DispatcherOperation.TResult.1.cs} (100%) rename src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/{DispatcherOperation.2Arg.cs => DispatcherOperation.TResult.2.cs} (100%) rename src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/{DispatcherOperation.3Arg.cs => DispatcherOperation.TResult.3.cs} (100%) diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs index 530c2c3c06e..4caeb9a818a 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs @@ -538,6 +538,67 @@ public void Invoke(Action callback, DispatcherPriority priority, CancellationTok Invoke(callback, priority, cancellationToken, TimeSpan.FromMilliseconds(-1)); } + /// + /// Executes the specified Action synchronously on the thread that + /// the Dispatcher was created on. + /// + /// + /// An Action delegate to invoke through the dispatcher. + /// + /// + /// The priority that determines in what order the specified + /// callback is invoked relative to the other pending operations + /// in the Dispatcher. + /// + /// + /// A cancellation token that can be used to cancel the operation. + /// If the operation has not started, it will be aborted when the + /// cancellation token is canceled. If the operation has started, + /// the operation can cooperate with the cancellation request. + /// + /// + /// The minimum amount of time to wait for the operation to start. + /// Once the operation has started, it will complete before this method + /// returns. + /// + public void Invoke(Action callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout, TArg arg) + { + ArgumentNullException.ThrowIfNull(callback); + ValidatePriority(priority, "priority"); + + if (timeout.TotalMilliseconds < 0 && + timeout != TimeSpan.FromMilliseconds(-1)) + { + throw new ArgumentOutOfRangeException(nameof(timeout)); + } + + // Fast-Path: if on the same thread, and invoking at Send priority, + // and the cancellation token is not already canceled, then just + // call the callback directly. + if (!cancellationToken.IsCancellationRequested && priority == DispatcherPriority.Send && CheckAccess()) + { + SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; + + try + { + DispatcherSynchronizationContext newSynchronizationContext = DispatcherUtils.GetOrCreateContext(this, priority); + + SynchronizationContext.SetSynchronizationContext(newSynchronizationContext); + + callback(arg); + return; + } + finally + { + SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext); + } + } + + // Slow-Path: go through the queue. + DispatcherOperation operation = new DispatcherOperationAction(this, priority, callback, arg); + InvokeImpl(operation, cancellationToken, timeout); + } + /// /// Executes the specified Action synchronously on the thread that /// the Dispatcher was created on. diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.0.cs similarity index 88% rename from src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.cs rename to src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.0.cs index cb6294dc5cb..5274675ef83 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.0.cs @@ -9,18 +9,18 @@ namespace System.Windows.Threading; /// /// DispatcherOperation represents a delegate that has been posted to the queue. /// -internal sealed class DispatcherOperationAction : DispatcherOperation +internal class DispatcherOperationAction : DispatcherOperation { - internal DispatcherOperationAction(Dispatcher dispatcher, DispatcherPriority priority, Action func) : base( + internal DispatcherOperationAction(Dispatcher dispatcher, DispatcherPriority priority, Delegate method) : base( dispatcher: dispatcher, - method: func, + method: method, priority: priority, taskSource: new DispatcherOperationTaskSource(), useAsyncSemantics: true) { } - public override object Result + public sealed override object Result { get { @@ -45,7 +45,7 @@ public override object Result } } - internal override void InvokeCompletions() + internal sealed override void InvokeCompletions() { switch (_status) { @@ -69,7 +69,7 @@ internal override void InvokeCompletions() } } - protected override void InvokeImpl() + protected sealed override void InvokeImpl() { SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; @@ -87,8 +87,7 @@ protected override void InvokeImpl() try { - Action action = (Action)_method; - action(); + InvokeDelegateCore(); } catch (Exception e) { @@ -101,4 +100,10 @@ protected override void InvokeImpl() SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext); } } + + protected virtual void InvokeDelegateCore() + { + Action action = (Action)_method; + action(); + } } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.1.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.1.cs new file mode 100644 index 00000000000..139cae06c77 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.1.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Windows.Threading; + +internal sealed class DispatcherOperationAction : DispatcherOperationAction +{ + private readonly TArg _arg; + + internal DispatcherOperationAction(Dispatcher dispatcher, DispatcherPriority priority, Action method, TArg arg) : base( + dispatcher: dispatcher, + method: method, + priority: priority) + { + _arg = arg; + } + + protected sealed override void InvokeDelegateCore() + { + Action action = (Action)_method; + action(_arg); + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.0.cs similarity index 97% rename from src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.cs rename to src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.0.cs index e90428a1218..0281c98c91f 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.0.cs @@ -75,7 +75,7 @@ internal DispatcherOperation(Dispatcher dispatcher, DispatcherPriority priority, } } - internal override void InvokeCompletions() + internal sealed override void InvokeCompletions() { switch (_status) { @@ -100,7 +100,7 @@ internal override void InvokeCompletions() } } - protected override void InvokeImpl() + protected sealed override void InvokeImpl() { SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.1Arg.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.1.cs similarity index 100% rename from src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.1Arg.cs rename to src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.1.cs diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.2Arg.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.2.cs similarity index 100% rename from src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.2Arg.cs rename to src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.2.cs diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.3Arg.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.3.cs similarity index 100% rename from src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.3Arg.cs rename to src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.3.cs diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj b/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj index 3d8d0a3ddc6..f2253a3a3ec 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj @@ -245,6 +245,7 @@ + @@ -286,13 +287,13 @@ - - - - + + + + - + From 51e0e86d787d39dd13da924c3d1fbe033fbc4c58 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Wed, 21 May 2025 22:42:11 +0200 Subject: [PATCH 03/54] keep boxing prop value due to public interface --- .../MS/internal/Automation/ElementProxy.cs | 4 ++-- .../WindowsBase/System/Windows/Threading/Dispatcher.cs | 8 ++++---- .../src/WindowsBase/ref/WindowsBase.cs | 2 -- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs index 85c256c333d..a667fd95bb3 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs @@ -392,11 +392,11 @@ private ProviderOptions InContextGetProviderOptions() } // Return value for specified property; or null if not supported - private int InContextGetPropertyValue(int property) + private object InContextGetPropertyValue(int property) { AutomationPeer peer = Peer ?? throw new ElementNotAvailableException(); - return (int)peer.GetPropertyValue(property); + return peer.GetPropertyValue(property); } /// Returns whether this is the Root of the WCP tree or not diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs index 4caeb9a818a..d425bac1bfd 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs @@ -561,7 +561,7 @@ public void Invoke(Action callback, DispatcherPriority priority, CancellationTok /// Once the operation has started, it will complete before this method /// returns. /// - public void Invoke(Action callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout, TArg arg) + internal void Invoke(Action callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout, TArg arg) { ArgumentNullException.ThrowIfNull(callback); ValidatePriority(priority, "priority"); @@ -750,7 +750,7 @@ public TResult Invoke(Func callback, DispatcherPriority priori /// /// The return value from the delegate being invoked. /// - public TResult Invoke(Func callback, DispatcherPriority priority, TimeSpan timeout, TArg1 arg1, TArg2 arg2) + internal TResult Invoke(Func callback, DispatcherPriority priority, TimeSpan timeout, TArg1 arg1, TArg2 arg2) { ArgumentNullException.ThrowIfNull(callback); ValidatePriority(priority, "priority"); @@ -813,7 +813,7 @@ public TResult Invoke(Func callbac /// /// The return value from the delegate being invoked. /// - public TResult Invoke(Func callback, DispatcherPriority priority, TimeSpan timeout, TArg1 arg1, TArg2 arg2, TArg3 arg3) + internal TResult Invoke(Func callback, DispatcherPriority priority, TimeSpan timeout, TArg1 arg1, TArg2 arg2, TArg3 arg3) { ArgumentNullException.ThrowIfNull(callback); ValidatePriority(priority, "priority"); @@ -876,7 +876,7 @@ public TResult Invoke(Func /// The return value from the delegate being invoked. /// - public TResult Invoke(Func callback, DispatcherPriority priority, TimeSpan timeout, TArg arg) + internal TResult Invoke(Func callback, DispatcherPriority priority, TimeSpan timeout, TArg arg) { ArgumentNullException.ThrowIfNull(callback); ValidatePriority(priority, "priority"); diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/ref/WindowsBase.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/ref/WindowsBase.cs index 6673a43b0d6..fa8fd5edbeb 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/ref/WindowsBase.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/ref/WindowsBase.cs @@ -1823,7 +1823,6 @@ public event System.EventHandler Completed { add { } remove { } } [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public System.Runtime.CompilerServices.TaskAwaiter GetAwaiter() { throw null; } - protected virtual object InvokeDelegateCore() { throw null; } public System.Windows.Threading.DispatcherOperationStatus Wait() { throw null; } public System.Windows.Threading.DispatcherOperationStatus Wait(System.TimeSpan timeout) { throw null; } } @@ -1843,7 +1842,6 @@ internal DispatcherOperation() { } [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public new System.Runtime.CompilerServices.TaskAwaiter GetAwaiter() { throw null; } - protected override object InvokeDelegateCore() { throw null; } } public enum DispatcherPriority { From 9e8daccd557860af7ae8469af77598d383c9057a Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 22 May 2025 23:43:23 +0200 Subject: [PATCH 04/54] fix --- .../PresentationCore/MS/internal/Automation/ElementProxy.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs index a667fd95bb3..12efa359512 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs @@ -314,11 +314,11 @@ private IRawElementProviderFragment InContextGetFocus() } // redirect to AutomationPeer - private int InContextGetPatternProvider(int arg) + private object InContextGetPatternProvider(int arg) { AutomationPeer peer = Peer ?? throw new ElementNotAvailableException(); - return (int)peer.GetWrappedPattern(arg); + return peer.GetWrappedPattern(arg); } // Return proxy representing element in specified direction (parent/next/firstchild/etc.) From b6f4102c327878764b3907796184c72e7c3035a0 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 29 May 2025 19:29:16 +0200 Subject: [PATCH 05/54] Polish it a bit, fix exception text --- .../MS/internal/Automation/ElementProxy.cs | 45 ++++-------- .../Automation/ElementUtil.ReturnInfo.cs | 69 +++++++++++++++++++ .../MS/internal/Automation/ElementUtil.cs | 69 +++++++++---------- .../PresentationCore/PresentationCore.csproj | 1 + 4 files changed, 116 insertions(+), 68 deletions(-) create mode 100644 src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ReturnInfo.cs diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs index 12efa359512..bcce505a271 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs @@ -10,10 +10,9 @@ using System.Windows; using System.Windows.Automation; -using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; +using System.Windows.Automation.Provider; using System.Windows.Input; -using System.Windows.Threading; namespace MS.Internal.Automation { @@ -25,7 +24,7 @@ namespace MS.Internal.Automation // // Currently exposes just BoundingRectangle, ClassName, IsEnabled // and IsKeyboardFocused properties. - internal class ElementProxy: IRawElementProviderFragmentRoot, IRawElementProviderAdviseEvents + internal class ElementProxy : IRawElementProviderFragmentRoot, IRawElementProviderAdviseEvents { //------------------------------------------------------ // @@ -88,44 +87,31 @@ public object GetPropertyValue(int property) public ProviderOptions ProviderOptions { - get + get { AutomationPeer peer = Peer; - if (peer == null) - { - return ProviderOptions.ServerSideProvider; - } - return ElementUtil.Invoke(peer, static (state) => state.InContextGetProviderOptions(), this); + + return peer is null ? ProviderOptions.ServerSideProvider : ElementUtil.Invoke(peer, static (state) => state.InContextGetProviderOptions(), this); } - } + } public IRawElementProviderSimple HostRawElementProvider { get { - IRawElementProviderSimple host = null; - HostedWindowWrapper hwndWrapper = null; AutomationPeer peer = Peer; - if (peer == null) + + if (peer is null) { return null; } - hwndWrapper = ElementUtil.Invoke(peer, static (state) => state.InContextGetHostRawElementProvider(), this); - if (hwndWrapper != null) - host = GetHostHelper(hwndWrapper); - - return host; - } - } + HostedWindowWrapper hwndWrapper = ElementUtil.Invoke(peer, static (state) => state.InContextGetHostRawElementProvider(), this); - private static IRawElementProviderSimple GetHostHelper(HostedWindowWrapper hwndWrapper) - { - return AutomationInteropProvider.HostProviderFromHandle(hwndWrapper.Handle); + return hwndWrapper is not null ? AutomationInteropProvider.HostProviderFromHandle(hwndWrapper.Handle) : null; + } } - // IRawElementProviderFragment methods... - public IRawElementProviderFragment Navigate(NavigateDirection direction) { AutomationPeer peer = Peer; @@ -149,12 +135,12 @@ public Rect BoundingRectangle return ElementUtil.Invoke(peer, static (state) => state.InContextBoundingRectangle(), this); } } - + public IRawElementProviderSimple[] GetEmbeddedFragmentRoots() { return null; } - + public void SetFocus() { AutomationPeer peer = Peer ?? throw new ElementNotAvailableException(); @@ -172,7 +158,6 @@ public IRawElementProviderFragmentRoot FragmentRoot } } - // IRawElementProviderFragmentRoot methods.. public IRawElementProviderFragment ElementProviderFromPoint(double x, double y) { AutomationPeer peer = Peer; @@ -259,9 +244,9 @@ internal AutomationPeer Peer { get { - if (_peer is WeakReference) + if (_peer is WeakReference weakRef) { - AutomationPeer peer = (AutomationPeer)((WeakReference)_peer).Target; + AutomationPeer peer = (AutomationPeer)weakRef.Target; return peer; } else diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ReturnInfo.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ReturnInfo.cs new file mode 100644 index 00000000000..d36b55f80df --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ReturnInfo.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace MS.Internal.Automation; + +/// +/// Utility class for working with . +/// +internal static partial class ElementUtil +{ + /// + /// Wraps the return value and exception of an asynchronous operation. + /// + /// + [StructLayout(LayoutKind.Auto)] + private readonly struct ReturnInfo + { + /// + /// The exception that was thrown during the operation, if any. + /// + public Exception? StoredException { get; init; } + + /// + /// Gets a value indicating whether the operation has been completed or timed out. + /// + public bool Completed { get; init; } + + /// + /// The return value of the operation, if it completed successfully. + /// + public TReturn Value { get; init; } + + /// + /// Creates a new instance of with the specified exception. + /// + /// The exception that was thrown during the operation. + /// Returns a object. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReturnInfo FromException(Exception exception) + { + return new ReturnInfo + { + StoredException = exception, + Completed = true, + Value = default! + }; + } + + /// + /// Creates a new instance of with the specified value. + /// + /// The return value of the operation. + /// Returns a object. + public static ReturnInfo FromResult(TReturn value) + { + return new ReturnInfo + { + StoredException = null, + Completed = true, + Value = value + }; + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs index 8fbfea4f57e..d40189b2ea7 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs @@ -14,16 +14,8 @@ namespace MS.Internal.Automation; /// /// Utility class for working with . /// -internal static class ElementUtil +internal static partial class ElementUtil { - private readonly struct ReturnInfo - { - public Exception? StoredException { get; init; } - public bool Completed { get; init; } - - public TReturn Value { get; init; } - } - ///// ///// Provides a helper to invoke work on the UI thread, re-throwing all exceptions on the thread that invoked this execution. ///// @@ -63,11 +55,11 @@ static ReturnInfo ExceptionWrapper(Func func, TArg arg) { try { - return new ReturnInfo() { Value = func(arg), Completed = true }; + return ReturnInfo.FromResult(func(arg)); } catch (Exception e) { - return new ReturnInfo() { StoredException = e, Completed = true }; + return ReturnInfo.FromException(e); } } @@ -88,11 +80,11 @@ static ReturnInfo ExceptionWrapper(Func func, TA { try { - return new ReturnInfo() { Value = func(arg1, arg2), Completed = true }; + return ReturnInfo.FromResult(func(arg1, arg2)); } catch (Exception e) { - return new ReturnInfo() { StoredException = e, Completed = true }; + return ReturnInfo.FromException(e); } } @@ -120,14 +112,23 @@ private static TReturn HandleReturnValue(Dispatcher dispatcher, ref rea return retVal.Value; } + /// + /// Throws an indicating that the associated dispatcher has been shut down. + /// [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowInvalidOperationException() => throw new InvalidOperationException(SR.AutomationDispatcherShutdown); + /// + /// Throws a indicating that the automation operation has timed out. + /// [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] - private static void ThrowTimeoutException() => throw new TimeoutException(SR.AutomationDispatcherShutdown); + private static void ThrowTimeoutException() => throw new TimeoutException(SR.AutomationTimeout); + /// + /// Unwraps the exception and throws it on the current thread. + /// [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] private static void UnwrapException(Exception exception) => throw exception; @@ -144,31 +145,23 @@ internal static object Invoke(AutomationPeer peer, DispatcherOperationCallback w Exception? remoteException = null; bool completed = false; - object retVal = dispatcher.Invoke( - DispatcherPriority.Send, - TimeSpan.FromMinutes(3), - (DispatcherOperationCallback)delegate (object workArg) + object retVal = dispatcher.Invoke(DispatcherPriority.Send, TimeSpan.FromMinutes(3), (DispatcherOperationCallback)delegate (object workArg) + { + try { - try - { - return work(workArg); - } - catch (Exception e) - { - remoteException = e; - return null; - } - catch //for non-CLS Compliant exceptions - { - remoteException = null; - return null; - } - finally - { - completed = true; - } - }, - arg); + return work(workArg); + } + catch (Exception e) + { + remoteException = e; + return null; + } + finally + { + completed = true; + } + }, + arg); if (completed) { diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/PresentationCore.csproj b/src/Microsoft.DotNet.Wpf/src/PresentationCore/PresentationCore.csproj index da1d32be5fa..a01827dcf03 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/PresentationCore.csproj +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/PresentationCore.csproj @@ -137,6 +137,7 @@ + From 2f3391d9395e5c9bded0e69b821a7f037b63b12c Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 29 May 2025 20:07:19 +0200 Subject: [PATCH 06/54] Convert ExceptionWrapper as static class --- .../MS/internal/Automation/ElementUtil.cs | 2 + .../MS/Internal/Threading/ExceptionWrapper.cs | 66 ++++++++----------- .../System/Windows/Threading/Dispatcher.cs | 8 +-- 3 files changed, 32 insertions(+), 44 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs index d40189b2ea7..2aa906c7737 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs @@ -63,6 +63,7 @@ static ReturnInfo ExceptionWrapper(Func func, TArg arg) } } + // Either returns the result or throws an exception if the operation did not complete successfully return HandleReturnValue(dispatcher, in retVal); } @@ -88,6 +89,7 @@ static ReturnInfo ExceptionWrapper(Func func, TA } } + // Either returns the result or throws an exception if the operation did not complete successfully return HandleReturnValue(dispatcher, in retVal); } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionWrapper.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionWrapper.cs index a1d6d84a2dd..1fd5f8936f8 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Threading; @@ -8,14 +8,27 @@ namespace System.Windows.Threading /// /// Class for Filtering and Catching Exceptions /// - internal class ExceptionWrapper + internal static class ExceptionWrapper { - internal ExceptionWrapper() - { - } + internal static event CatchHandler Catch; + internal static event FilterHandler Filter; + + /// + /// Exception Catch Handler Delegate + /// Returns true if the exception is "handled" + /// Returns false if the caller should rethow the exception. + /// + internal delegate bool CatchHandler(object source, Exception e); + + /// + /// Exception Catch Handler + /// Returns true if the exception is "handled" + /// Returns false if the caller should rethow the exception. + /// + internal delegate bool FilterHandler(object source, Exception e); // Helper for exception filtering: - public object TryCatchWhen(object source, Delegate callback, object args, int numArgs, Delegate catchHandler) + internal static object TryCatchWhen(object source, Delegate callback, object args, int numArgs, Delegate catchHandler) { object result = null; @@ -34,7 +47,7 @@ public object TryCatchWhen(object source, Delegate callback, object args, int nu return result; } - private object InternalRealCall(Delegate callback, object args, int numArgs) + private static object InternalRealCall(Delegate callback, object args, int numArgs) { object result = null; @@ -121,27 +134,22 @@ private object InternalRealCall(Delegate callback, object args, int numArgs) return result; } - private bool FilterException(object source, Exception e) + private static bool FilterException(object source, Exception e) { // If we have a Catch handler we should catch the exception // unless the Filter handler says we shouldn't. - bool shouldCatch = (null != Catch); - if(null != Filter) - { - shouldCatch = Filter(source, e); - } - return shouldCatch; + return Filter?.Invoke(source, e) ?? Catch is not null; } // This returns false when caller should rethrow the exception. // true means Exception is "handled" and things just continue on. - private bool CatchException(object source, Exception e, Delegate catchHandler) + private static bool CatchException(object source, Exception e, Delegate catchHandler) { - if (catchHandler != null) + if (catchHandler is not null) { - if(catchHandler is DispatcherOperationCallback) + if (catchHandler is DispatcherOperationCallback catchCallback) { - ((DispatcherOperationCallback)catchHandler)(null); + catchCallback(null); } else { @@ -149,28 +157,8 @@ private bool CatchException(object source, Exception e, Delegate catchHandler) } } - if(null != Catch) - return Catch(source, e); - - return false; + return Catch?.Invoke(source, e) ?? false; } - - /// - /// Exception Catch Handler Delegate - /// Returns true if the exception is "handled" - /// Returns false if the caller should rethow the exception. - /// - public delegate bool CatchHandler(object source, Exception e); - - /// - /// Exception Catch Handler - /// Returns true if the exception is "handled" - /// Returns false if the caller should rethow the exception. - /// - public event CatchHandler Catch; - - public delegate bool FilterHandler(object source, Exception e); - public event FilterHandler Filter; } } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs index d425bac1bfd..9e0ab6bf2ab 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs @@ -26,9 +26,8 @@ static Dispatcher() _globalLock = new object(); _dispatchers = new List(); _possibleDispatcher = new WeakReference(null); - _exceptionWrapper = new ExceptionWrapper(); - _exceptionWrapper.Catch += new ExceptionWrapper.CatchHandler(CatchExceptionStatic); - _exceptionWrapper.Filter += new ExceptionWrapper.FilterHandler(ExceptionFilterStatic); + ExceptionWrapper.Catch += CatchExceptionStatic; + ExceptionWrapper.Filter += ExceptionFilterStatic; } /// @@ -2971,7 +2970,7 @@ private bool HasUnhandledExceptionHandler internal object WrappedInvoke(Delegate callback, object args, int numArgs, Delegate catchHandler) { - return _exceptionWrapper.TryCatchWhen(this, callback, args, numArgs, catchHandler); + return ExceptionWrapper.TryCatchWhen(this, callback, args, numArgs, catchHandler); } private object[] CombineParameters(object arg, object[] args) @@ -3026,7 +3025,6 @@ private object[] CombineParameters(object arg, object[] args) private int _postedProcessingType; private static WindowMessage _msgProcessQueue; - private static ExceptionWrapper _exceptionWrapper; private static readonly object ExceptionDataKey = new object(); // Preallocated arguments for exception handling. From ef2247e1c7557910afd2f116878dc0550249ab41 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 29 May 2025 20:11:13 +0200 Subject: [PATCH 07/54] Remove ExceptionFilterHelper dead code --- .../Threading/ExceptionFilterHelper.cs | 55 ------------------- .../src/WindowsBase/WindowsBase.csproj | 1 - 2 files changed, 56 deletions(-) delete mode 100644 src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionFilterHelper.cs diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionFilterHelper.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionFilterHelper.cs deleted file mode 100644 index 31c3cb75850..00000000000 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionFilterHelper.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -// Description: Internal class replicating the functionality of the -// former VB class of the same name. It's no longer used internally, but -// exists solely for compat - in case anyone used private reflection. - -namespace MS.Internal.Threading -{ - internal delegate object InternalRealCallDelegate(Delegate method, object args, int numArgs); - internal delegate bool FilterExceptionDelegate(object source, Exception e); - internal delegate bool CatchExceptionDelegate(object source, Exception e, Delegate catchHandler); - - /// - /// Class for Filtering and Catching Exceptions - /// - internal sealed class ExceptionFilterHelper - { - internal ExceptionFilterHelper( InternalRealCallDelegate internalRealCall, - FilterExceptionDelegate filterException, - CatchExceptionDelegate catchException) - { - _internalRealCall = internalRealCall; - _filterException = filterException; - _catchException = catchException; - } - - internal object TryCatchWhen( object source, - Delegate method, - object args, - int numArgs, - Delegate catchHandler) - { - object result = null; - - try - { - result = _internalRealCall.Invoke(method, args, numArgs); - } - catch (Exception e) when (_filterException.Invoke(source, e)) - { - if (!_catchException.Invoke(source, e, catchHandler)) - { - throw; - } - } - - return result; - } - - private InternalRealCallDelegate _internalRealCall; - private FilterExceptionDelegate _filterException; - private CatchExceptionDelegate _catchException; - } -} diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj b/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj index f2253a3a3ec..64db6ada9d7 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj @@ -148,7 +148,6 @@ - From 6b13583315bfe00c40f10b3f889d21934da6dca6 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 29 May 2025 20:37:34 +0200 Subject: [PATCH 08/54] Provide specific implementation for ExceptionWrapper, remove casts --- .../src/Shared/RefAssemblyAttrs.cs | 1 + .../src/WindowsBase/LibraryAssemblyInfo.cs | 3 ++- .../MS/Internal/Threading/ExceptionWrapper.cs | 18 +++++++++--------- .../System/Windows/Threading/Dispatcher.cs | 8 +++----- .../Windows/Markup/InternalTypeHelperTests.cs | 10 +++++----- .../Windows/Threading/DispatcherTests.cs | 2 +- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/RefAssemblyAttrs.cs b/src/Microsoft.DotNet.Wpf/src/Shared/RefAssemblyAttrs.cs index 0e2505fbb3e..243113550b3 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/RefAssemblyAttrs.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/RefAssemblyAttrs.cs @@ -57,6 +57,7 @@ internal static class BuildInfo internal const string SystemPrinting = $"System.Printing, PublicKey={WCP_PUBLIC_KEY_STRING}"; internal const string SystemXaml = $"System.Xaml, PublicKey={DEVDIV_PUBLIC_KEY_STRING}"; internal const string WindowsFormsIntegration = $"WindowsFormsIntegration, PublicKey={WCP_PUBLIC_KEY_STRING}"; + internal const string WindowsBaseTests = $"WindowsBase.Tests, PublicKey={WCP_PUBLIC_KEY_STRING}"; // Make internal visible to the 3.5 dll, System.Windows.Presentation.dll. // we hard code the key here because the 3.5 dll is built in the devdiv depot using the CLR key. diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/LibraryAssemblyInfo.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/LibraryAssemblyInfo.cs index 043e9b01263..b4e5fdad7fd 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/LibraryAssemblyInfo.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/LibraryAssemblyInfo.cs @@ -2,5 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Runtime.CompilerServices; +using MS.Internal.WindowsBase; -[assembly: InternalsVisibleTo("WindowsBase.Tests, PublicKey=00000000000000000400000000000000")] +[assembly: InternalsVisibleTo(BuildInfo.WindowsBaseTests)] diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionWrapper.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionWrapper.cs index 1fd5f8936f8..fb975a6298d 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionWrapper.cs @@ -18,17 +18,17 @@ internal static class ExceptionWrapper /// Returns true if the exception is "handled" /// Returns false if the caller should rethow the exception. /// - internal delegate bool CatchHandler(object source, Exception e); + internal delegate bool CatchHandler(Dispatcher dispatcher, Exception e); /// /// Exception Catch Handler /// Returns true if the exception is "handled" /// Returns false if the caller should rethow the exception. /// - internal delegate bool FilterHandler(object source, Exception e); + internal delegate bool FilterHandler(Dispatcher dispatcher, Exception e); // Helper for exception filtering: - internal static object TryCatchWhen(object source, Delegate callback, object args, int numArgs, Delegate catchHandler) + internal static object TryCatchWhen(Dispatcher dispatcher, Delegate callback, object args, int numArgs, Delegate catchHandler) { object result = null; @@ -36,9 +36,9 @@ internal static object TryCatchWhen(object source, Delegate callback, object arg { result = InternalRealCall(callback, args, numArgs); } - catch (Exception e) when (FilterException(source, e)) + catch (Exception e) when (FilterException(dispatcher, e)) { - if (!CatchException(source, e, catchHandler)) + if (!CatchException(dispatcher, e, catchHandler)) { throw; } @@ -134,16 +134,16 @@ private static object InternalRealCall(Delegate callback, object args, int numAr return result; } - private static bool FilterException(object source, Exception e) + private static bool FilterException(Dispatcher dispatcher, Exception e) { // If we have a Catch handler we should catch the exception // unless the Filter handler says we shouldn't. - return Filter?.Invoke(source, e) ?? Catch is not null; + return Filter?.Invoke(dispatcher, e) ?? Catch is not null; } // This returns false when caller should rethrow the exception. // true means Exception is "handled" and things just continue on. - private static bool CatchException(object source, Exception e, Delegate catchHandler) + private static bool CatchException(Dispatcher dispatcher, Exception e, Delegate catchHandler) { if (catchHandler is not null) { @@ -157,7 +157,7 @@ private static bool CatchException(object source, Exception e, Delegate catchHan } } - return Catch?.Invoke(source, e) ?? false; + return Catch?.Invoke(dispatcher, e) ?? false; } } } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs index 9e0ab6bf2ab..099e761e47d 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs @@ -2872,10 +2872,9 @@ private void KillWin32Timer() } // Exception filter returns true if exception should be caught. - private static bool ExceptionFilterStatic(object source, Exception e) + private static bool ExceptionFilterStatic(Dispatcher dispatcher, Exception e) { - Dispatcher d = (Dispatcher)source; - return d.ExceptionFilter(e); + return dispatcher.ExceptionFilter(e); } private bool ExceptionFilter(Exception e) @@ -2930,9 +2929,8 @@ private bool ExceptionFilter(Exception e) // This returns false when caller should rethrow the exception. // true means Exception is "handled" and things just continue on. - private static bool CatchExceptionStatic(object source, Exception e) + private static bool CatchExceptionStatic(Dispatcher dispatcher, Exception e) { - Dispatcher dispatcher = (Dispatcher)source; return dispatcher.CatchException(e); } diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Markup/InternalTypeHelperTests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Markup/InternalTypeHelperTests.cs index d8cbae67266..d99f3a60507 100644 --- a/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Markup/InternalTypeHelperTests.cs +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Markup/InternalTypeHelperTests.cs @@ -17,19 +17,19 @@ public void Ctor_Default() private class SubInternalTypeHelper : InternalTypeHelper { - protected override void AddEventHandler(EventInfo eventInfo, object target, Delegate handler) + protected internal override void AddEventHandler(EventInfo eventInfo, object target, Delegate handler) => throw new NotImplementedException(); - protected override Delegate CreateDelegate(Type delegateType, object target, string handler) + protected internal override Delegate CreateDelegate(Type delegateType, object target, string handler) => throw new NotImplementedException(); - protected override object CreateInstance(Type type, CultureInfo culture) + protected internal override object CreateInstance(Type type, CultureInfo culture) => throw new NotImplementedException(); - protected override object GetPropertyValue(PropertyInfo propertyInfo, object target, CultureInfo culture) + protected internal override object GetPropertyValue(PropertyInfo propertyInfo, object target, CultureInfo culture) => throw new NotImplementedException(); - protected override void SetPropertyValue(PropertyInfo propertyInfo, object target, object value, CultureInfo culture) + protected internal override void SetPropertyValue(PropertyInfo propertyInfo, object target, object value, CultureInfo culture) => throw new NotImplementedException(); } } diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs index 173c3b7c071..54453576796 100644 --- a/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs @@ -260,4 +260,4 @@ public void VerifyAccess_InvokeOnDifferentThread_ThrowsInvalidOperationException thread.Join(); Assert.True(threwInvalidOperationException); } -} \ No newline at end of file +} From 85bdcbd7c7ad6e9594197397f39006639a60c463 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 29 May 2025 20:45:54 +0200 Subject: [PATCH 09/54] Wrap ExceptionWrapper inside Dispatcher --- .../MS/Internal/Threading/ExceptionWrapper.cs | 45 +++++++------------ .../System/Windows/Threading/Dispatcher.cs | 17 +------ 2 files changed, 16 insertions(+), 46 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionWrapper.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionWrapper.cs index fb975a6298d..0c5b5e2754f 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionWrapper.cs @@ -3,30 +3,15 @@ using System.Threading; -namespace System.Windows.Threading +namespace System.Windows.Threading; + +public sealed partial class Dispatcher { /// /// Class for Filtering and Catching Exceptions /// internal static class ExceptionWrapper { - internal static event CatchHandler Catch; - internal static event FilterHandler Filter; - - /// - /// Exception Catch Handler Delegate - /// Returns true if the exception is "handled" - /// Returns false if the caller should rethow the exception. - /// - internal delegate bool CatchHandler(Dispatcher dispatcher, Exception e); - - /// - /// Exception Catch Handler - /// Returns true if the exception is "handled" - /// Returns false if the caller should rethow the exception. - /// - internal delegate bool FilterHandler(Dispatcher dispatcher, Exception e); - // Helper for exception filtering: internal static object TryCatchWhen(Dispatcher dispatcher, Delegate callback, object args, int numArgs, Delegate catchHandler) { @@ -59,14 +44,14 @@ private static object InternalRealCall(Delegate callback, object args, int numAr // of an arbitrary "params object[]" is passed. int numArgsEx = numArgs; object singleArg = args; - if(numArgs == -1) + if (numArgs == -1) { object[] argsArr = (object[])args; if (argsArr == null || argsArr.Length == 0) { numArgsEx = 0; } - else if(argsArr.Length == 1) + else if (argsArr.Length == 1) { numArgsEx = 1; singleArg = argsArr[0]; @@ -75,7 +60,7 @@ private static object InternalRealCall(Delegate callback, object args, int numAr // Special-case delegates that we know about to avoid the // expensive DynamicInvoke call. - if(numArgsEx == 0) + if (numArgsEx == 0) { if (callback is Action action) { @@ -94,7 +79,7 @@ private static object InternalRealCall(Delegate callback, object args, int numAr } } } - else if(numArgsEx == 1) + else if (numArgsEx == 1) { if (callback is DispatcherOperationCallback dispatcherOperationCallback) { @@ -134,15 +119,15 @@ private static object InternalRealCall(Delegate callback, object args, int numAr return result; } + /// + /// Exception filter returns if exception should be caught. + /// private static bool FilterException(Dispatcher dispatcher, Exception e) { - // If we have a Catch handler we should catch the exception - // unless the Filter handler says we shouldn't. - return Filter?.Invoke(dispatcher, e) ?? Catch is not null; + // This will raise Dispatcher.UnhandledException event if registered. + return dispatcher.ExceptionFilter(e); } - // This returns false when caller should rethrow the exception. - // true means Exception is "handled" and things just continue on. private static bool CatchException(Dispatcher dispatcher, Exception e, Delegate catchHandler) { if (catchHandler is not null) @@ -157,9 +142,9 @@ private static bool CatchException(Dispatcher dispatcher, Exception e, Delegate } } - return Catch?.Invoke(dispatcher, e) ?? false; + // This returns false when caller should rethrow the exception. + // true means Exception is "handled" and things just continue on. + return dispatcher.CatchException(e); } } } - - diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs index 099e761e47d..0f5cfb347ff 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs @@ -18,7 +18,7 @@ namespace System.Windows.Threading /// /// Provides UI services for a thread. /// - public sealed class Dispatcher + public sealed partial class Dispatcher { static Dispatcher() { @@ -26,8 +26,6 @@ static Dispatcher() _globalLock = new object(); _dispatchers = new List(); _possibleDispatcher = new WeakReference(null); - ExceptionWrapper.Catch += CatchExceptionStatic; - ExceptionWrapper.Filter += ExceptionFilterStatic; } /// @@ -2871,12 +2869,6 @@ private void KillWin32Timer() } } - // Exception filter returns true if exception should be caught. - private static bool ExceptionFilterStatic(Dispatcher dispatcher, Exception e) - { - return dispatcher.ExceptionFilter(e); - } - private bool ExceptionFilter(Exception e) { // see whether this dispatcher has already seen the exception. @@ -2927,13 +2919,6 @@ private bool ExceptionFilter(Exception e) return requestCatch; } - // This returns false when caller should rethrow the exception. - // true means Exception is "handled" and things just continue on. - private static bool CatchExceptionStatic(Dispatcher dispatcher, Exception e) - { - return dispatcher.CatchException(e); - } - // The exception filter called for catching an unhandled exception. private bool CatchException(Exception e) { From d9e637344267471180517a2b05cc723f6345e923 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 29 May 2025 20:53:24 +0200 Subject: [PATCH 10/54] Move ExceptionWrapper into System.Windows.Threading --- .../Threading/Dispatcher.ExceptionWrapper.cs} | 17 ++++++++++++----- .../System/Windows/Threading/Dispatcher.cs | 14 +++++++++++--- .../src/WindowsBase/WindowsBase.csproj | 2 +- 3 files changed, 24 insertions(+), 9 deletions(-) rename src/Microsoft.DotNet.Wpf/src/WindowsBase/{MS/Internal/Threading/ExceptionWrapper.cs => System/Windows/Threading/Dispatcher.ExceptionWrapper.cs} (86%) diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionWrapper.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.ExceptionWrapper.cs similarity index 86% rename from src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionWrapper.cs rename to src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.ExceptionWrapper.cs index 0c5b5e2754f..66af1cf2dfc 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Threading/ExceptionWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.ExceptionWrapper.cs @@ -8,12 +8,18 @@ namespace System.Windows.Threading; public sealed partial class Dispatcher { /// - /// Class for Filtering and Catching Exceptions + /// Helper class for Filtering and Catching Exceptions. /// + /// + /// This is mostly legacy interface as newer (e.g. async) methods don't catch/filter the exceptions. + /// internal static class ExceptionWrapper { - // Helper for exception filtering: - internal static object TryCatchWhen(Dispatcher dispatcher, Delegate callback, object args, int numArgs, Delegate catchHandler) + /// + /// Calls the delegate and catches exceptions that are filtered by the dispatcher, raising the UnhandledException event if necessary. + /// + /// Result of the delegate method call in case it has succeeded. + internal static object TryCatchWhen(Dispatcher dispatcher, Delegate callback, object args, int numArgs, Delegate catchCallback) { object result = null; @@ -23,7 +29,8 @@ internal static object TryCatchWhen(Dispatcher dispatcher, Delegate callback, ob } catch (Exception e) when (FilterException(dispatcher, e)) { - if (!CatchException(dispatcher, e, catchHandler)) + // Determine whether we should catch or throw the exception, executing the catchCallback beforehand + if (!CatchException(dispatcher, e, catchCallback)) { throw; } @@ -68,7 +75,7 @@ private static object InternalRealCall(Delegate callback, object args, int numAr } else { - if (callback is Dispatcher.ShutdownCallback shutdownCallback) + if (callback is ShutdownCallback shutdownCallback) { shutdownCallback(); } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs index 0f5cfb347ff..7acb951ebda 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs @@ -2951,12 +2951,20 @@ private bool HasUnhandledExceptionHandler get { return (UnhandledException != null); } } - internal object WrappedInvoke(Delegate callback, object args, int numArgs, Delegate catchHandler) + /// + /// Calls the delegate and catches exceptions that are filtered by the dispatcher, raising the UnhandledException event if necessary. + /// + /// Result of the delegate method call in case it has succeeded. + internal object WrappedInvoke(Delegate callback, object args, int numArgs, Delegate catchCallback) { - return ExceptionWrapper.TryCatchWhen(this, callback, args, numArgs, catchHandler); + return ExceptionWrapper.TryCatchWhen(this, callback, args, numArgs, catchCallback); } - private object[] CombineParameters(object arg, object[] args) + /// + /// Combines the first argument with the rest of the arguments into an array. + /// + /// NOTE: This is a legacy invoke arguments handling. + private static object[] CombineParameters(object arg, object[] args) { object[] parameters = new object[1 + (args == null ? 1 : args.Length)]; parameters[0] = arg; diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj b/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj index 64db6ada9d7..5a8cc5202fc 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj @@ -278,7 +278,6 @@ - @@ -286,6 +285,7 @@ + From 454db37509157a7cc88e271e85177645daa9ced0 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Fri, 30 May 2025 18:39:51 +0200 Subject: [PATCH 11/54] Move stuff around --- .../System/Windows/Threading/Dispatcher.cs | 209 +++++++----------- .../Windows/Threading/DispatcherTests.cs | 12 + 2 files changed, 90 insertions(+), 131 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs index 7acb951ebda..3b785f9cdf9 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs @@ -535,67 +535,6 @@ public void Invoke(Action callback, DispatcherPriority priority, CancellationTok Invoke(callback, priority, cancellationToken, TimeSpan.FromMilliseconds(-1)); } - /// - /// Executes the specified Action synchronously on the thread that - /// the Dispatcher was created on. - /// - /// - /// An Action delegate to invoke through the dispatcher. - /// - /// - /// The priority that determines in what order the specified - /// callback is invoked relative to the other pending operations - /// in the Dispatcher. - /// - /// - /// A cancellation token that can be used to cancel the operation. - /// If the operation has not started, it will be aborted when the - /// cancellation token is canceled. If the operation has started, - /// the operation can cooperate with the cancellation request. - /// - /// - /// The minimum amount of time to wait for the operation to start. - /// Once the operation has started, it will complete before this method - /// returns. - /// - internal void Invoke(Action callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout, TArg arg) - { - ArgumentNullException.ThrowIfNull(callback); - ValidatePriority(priority, "priority"); - - if (timeout.TotalMilliseconds < 0 && - timeout != TimeSpan.FromMilliseconds(-1)) - { - throw new ArgumentOutOfRangeException(nameof(timeout)); - } - - // Fast-Path: if on the same thread, and invoking at Send priority, - // and the cancellation token is not already canceled, then just - // call the callback directly. - if (!cancellationToken.IsCancellationRequested && priority == DispatcherPriority.Send && CheckAccess()) - { - SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; - - try - { - DispatcherSynchronizationContext newSynchronizationContext = DispatcherUtils.GetOrCreateContext(this, priority); - - SynchronizationContext.SetSynchronizationContext(newSynchronizationContext); - - callback(arg); - return; - } - finally - { - SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext); - } - } - - // Slow-Path: go through the queue. - DispatcherOperation operation = new DispatcherOperationAction(this, priority, callback, arg); - InvokeImpl(operation, cancellationToken, timeout); - } - /// /// Executes the specified Action synchronously on the thread that /// the Dispatcher was created on. @@ -722,22 +661,64 @@ public TResult Invoke(Func callback, DispatcherPriority priori } /// - /// Executes the specified Func synchronously on the + /// Executes the specified synchronously on the /// thread that the Dispatcher was created on. /// /// - /// A Func delegate to invoke through the dispatcher. + /// An delegate to invoke through the dispatcher. /// /// /// The priority that determines in what order the specified /// callback is invoked relative to the other pending operations /// in the Dispatcher. /// - /// - /// A cancellation token that can be used to cancel the operation. - /// If the operation has not started, it will be aborted when the - /// cancellation token is canceled. If the operation has started, - /// the operation can cooperate with the cancellation request. + /// + /// The minimum amount of time to wait for the operation to start. + /// Once the operation has started, it will complete before this method + /// returns. + /// + internal void Invoke(Action callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout, TArg arg) + { + Debug.Assert(callback is not null, "Callback cannot be null"); + Debug.Assert(timeout.TotalMilliseconds >= 0 || timeout == TimeSpan.FromMilliseconds(-1)); + + // Fast-Path: if on the same thread, and invoking at Send priority, and the cancellation + // token is not already canceled, then just call the callback directly. + if (!cancellationToken.IsCancellationRequested && priority is DispatcherPriority.Send && CheckAccess()) + { + SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; + + try + { + DispatcherSynchronizationContext newSynchronizationContext = DispatcherUtils.GetOrCreateContext(this, priority); + + SynchronizationContext.SetSynchronizationContext(newSynchronizationContext); + + callback(arg); + return; + } + finally + { + SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext); + } + } + + // Slow-Path: go through the queue. + DispatcherOperation operation = new DispatcherOperationAction(this, priority, callback, arg); + InvokeImpl(operation, cancellationToken, timeout); + } + + /// + /// Executes the specified synchronously on the + /// thread that the Dispatcher was created on. + /// + /// + /// A delegate to invoke through the dispatcher. + /// + /// + /// The priority that determines in what order the specified + /// callback is invoked relative to the other pending operations + /// in the Dispatcher. /// /// /// The minimum amount of time to wait for the operation to start. @@ -749,19 +730,11 @@ public TResult Invoke(Func callback, DispatcherPriority priori /// internal TResult Invoke(Func callback, DispatcherPriority priority, TimeSpan timeout, TArg1 arg1, TArg2 arg2) { - ArgumentNullException.ThrowIfNull(callback); - ValidatePriority(priority, "priority"); - - if (timeout.TotalMilliseconds < 0 && - timeout != TimeSpan.FromMilliseconds(-1)) - { - throw new ArgumentOutOfRangeException(nameof(timeout)); - } + Debug.Assert(callback is not null, "Callback cannot be null"); + Debug.Assert(timeout.TotalMilliseconds >= 0 || timeout == TimeSpan.FromMilliseconds(-1)); - // Fast-Path: if on the same thread, and invoking at Send priority, - // and the cancellation token is not already canceled, then just - // call the callback directly. - if (priority == DispatcherPriority.Send && CheckAccess()) + // Fast-Path: if on the same thread, and invoking at Send priority, then just call the callback directly. + if (priority is DispatcherPriority.Send && CheckAccess()) { SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; @@ -785,23 +758,17 @@ internal TResult Invoke(Func callb } /// - /// Executes the specified Func synchronously on the + /// Executes the specified synchronously on the /// thread that the Dispatcher was created on. /// /// - /// A Func delegate to invoke through the dispatcher. + /// A delegate to invoke through the dispatcher. /// /// /// The priority that determines in what order the specified /// callback is invoked relative to the other pending operations /// in the Dispatcher. /// - /// - /// A cancellation token that can be used to cancel the operation. - /// If the operation has not started, it will be aborted when the - /// cancellation token is canceled. If the operation has started, - /// the operation can cooperate with the cancellation request. - /// /// /// The minimum amount of time to wait for the operation to start. /// Once the operation has started, it will complete before this method @@ -812,19 +779,11 @@ internal TResult Invoke(Func callb /// internal TResult Invoke(Func callback, DispatcherPriority priority, TimeSpan timeout, TArg1 arg1, TArg2 arg2, TArg3 arg3) { - ArgumentNullException.ThrowIfNull(callback); - ValidatePriority(priority, "priority"); - - if (timeout.TotalMilliseconds < 0 && - timeout != TimeSpan.FromMilliseconds(-1)) - { - throw new ArgumentOutOfRangeException(nameof(timeout)); - } + Debug.Assert(callback is not null, "Callback cannot be null"); + Debug.Assert(timeout.TotalMilliseconds >= 0 || timeout == TimeSpan.FromMilliseconds(-1)); - // Fast-Path: if on the same thread, and invoking at Send priority, - // and the cancellation token is not already canceled, then just - // call the callback directly. - if (priority == DispatcherPriority.Send && CheckAccess()) + // Fast-Path: if on the same thread, and invoking at Send priority, then just call the callback directly. + if (priority is DispatcherPriority.Send && CheckAccess()) { SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; @@ -848,23 +807,17 @@ internal TResult Invoke(Func - /// Executes the specified Func synchronously on the + /// Executes the specified synchronously on the /// thread that the Dispatcher was created on. /// /// - /// A Func delegate to invoke through the dispatcher. + /// A delegate to invoke through the dispatcher. /// /// /// The priority that determines in what order the specified /// callback is invoked relative to the other pending operations /// in the Dispatcher. /// - /// - /// A cancellation token that can be used to cancel the operation. - /// If the operation has not started, it will be aborted when the - /// cancellation token is canceled. If the operation has started, - /// the operation can cooperate with the cancellation request. - /// /// /// The minimum amount of time to wait for the operation to start. /// Once the operation has started, it will complete before this method @@ -875,19 +828,11 @@ internal TResult Invoke(Func internal TResult Invoke(Func callback, DispatcherPriority priority, TimeSpan timeout, TArg arg) { - ArgumentNullException.ThrowIfNull(callback); - ValidatePriority(priority, "priority"); - - if (timeout.TotalMilliseconds < 0 && - timeout != TimeSpan.FromMilliseconds(-1)) - { - throw new ArgumentOutOfRangeException(nameof(timeout)); - } + Debug.Assert(callback is not null, "Callback cannot be null"); + Debug.Assert(timeout.TotalMilliseconds >= 0 || timeout == TimeSpan.FromMilliseconds(-1)); - // Fast-Path: if on the same thread, and invoking at Send priority, - // and the cancellation token is not already canceled, then just - // call the callback directly. - if ( priority == DispatcherPriority.Send && CheckAccess()) + // Fast-Path: if on the same thread, and invoking at Send priority, then just call the callback directly. + if (priority is DispatcherPriority.Send && CheckAccess()) { SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; @@ -1724,20 +1669,22 @@ public DispatcherProcessingDisabled DisableProcessing() /// that is raised if the priority is not suitable for use by /// the dispatcher. /// - public static void ValidatePriority(DispatcherPriority priority, string parameterName) // NOTE: should be Priority + public static void ValidatePriority(DispatcherPriority priority, string parameterName) { - // First make sure the Priority is valid. - // Priority.ValidatePriority(priority, paramName); + // Make sure the priority is in a range recognized by the dispatcher. + if (!_foregroundPriorityRange.Contains(priority) && + !_backgroundPriorityRange.Contains(priority) && + !_idlePriorityRange.Contains(priority) && + priority is not DispatcherPriority.Inactive) + { + ThrowInvalidEnumArgumentException(parameterName, (int)priority); + } - // Second, make sure the priority is in a range recognized by - // the dispatcher. - if(!_foregroundPriorityRange.Contains(priority) && - !_backgroundPriorityRange.Contains(priority) && - !_idlePriorityRange.Contains(priority) && - DispatcherPriority.Inactive != priority) // NOTE: should be Priority.Min + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] + static void ThrowInvalidEnumArgumentException(string argumentName, int value) { - // If we move to a Priority class, this exception will have to change too. - throw new InvalidEnumArgumentException(parameterName, (int)priority, typeof(DispatcherPriority)); + throw new InvalidEnumArgumentException(argumentName, value, typeof(DispatcherPriority)); } } diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs index 54453576796..a0ad83dee46 100644 --- a/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs @@ -146,6 +146,18 @@ public void Invoke_SameThread_TReturn_Success() Assert.Equal(5, result); } + [WpfFact] + public void Invoke_SameThread_TArg_Success() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + + int result = 5; + // Send + current thread forces direct callback invocation + dispatcher.Invoke((param) => result += param, DispatcherPriority.Send, CancellationToken.None, TimeSpan.FromSeconds(3), 4); + + Assert.Equal(9, result); + } + [WpfFact] public void Invoke_SameThread_TReturn_TArg_Success() { From e25f0d2d2b665d28cb284f3681a6157224284d4f Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Fri, 30 May 2025 18:56:16 +0200 Subject: [PATCH 12/54] Remove unused wrapper --- .../MS/internal/Automation/ElementUtil.cs | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs index 2aa906c7737..756753854fa 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs @@ -16,31 +16,6 @@ namespace MS.Internal.Automation; /// internal static partial class ElementUtil { - ///// - ///// Provides a helper to invoke work on the UI thread, re-throwing all exceptions on the thread that invoked this execution. - ///// - //internal static TReturn Invoke(AutomationPeer peer, Func work) - //{ - // // Null dispatcher likely means the visual is in bad shape - // Dispatcher dispatcher = peer.Dispatcher ?? throw new ElementNotAvailableException(); - - // static ReturnInfo ExceptionWrapper(Func func) - // { - // try - // { - // return new ReturnInfo() { Value = func(), Completed = true }; - // } - // catch (Exception e) - // { - // return new ReturnInfo() { StoredException = e, Completed = true }; - // } - // } - - // ReturnInfo retVal = dispatcher.Invoke(ExceptionWrapper, DispatcherPriority.Send, TimeSpan.FromMinutes(3), work); - - // return HandleReturnValue(dispatcher, in retVal); - //} - /// /// Provides a helper to invoke work on the UI thread, re-throwing all exceptions on the thread that invoked this execution. /// From f078b643957af891979ab00feb2f8e0ad6f86590 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Fri, 30 May 2025 18:58:31 +0200 Subject: [PATCH 13/54] Remove using --- .../MS/internal/Automation/GridProviderWrapper.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs index 7f001157606..7c122a012be 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs @@ -1,14 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Grid pattern provider wrapper for WCP -// -// - -using System.Windows.Threading; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; From 0db154dfc478780b3f0c692e5e09ec345bbbd43e Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Fri, 30 May 2025 19:00:09 +0200 Subject: [PATCH 14/54] since I've done half the job there --- .../src/WindowsBase/System/Windows/Threading/Dispatcher.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs index 3b785f9cdf9..a07f1787282 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs @@ -187,7 +187,7 @@ public Thread Thread /// /// True if the calling thread has access to this object. /// - [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Never)] + [EditorBrowsable(EditorBrowsableState.Never)] public bool CheckAccess() { return Thread == Thread.CurrentThread; @@ -202,7 +202,7 @@ public bool CheckAccess() /// This method is public so that derived classes can probe to /// see if the calling thread has access to itself. /// - [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Never)] + [EditorBrowsable(EditorBrowsableState.Never)] public void VerifyAccess() { if(!CheckAccess()) From 84aedcb32c1cd8ea1c6db867efa0b33b30f95e9e Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Fri, 30 May 2025 19:07:27 +0200 Subject: [PATCH 15/54] Avoid casts where we know the type --- .../Windows/Threading/DispatcherOperation.Action.1.cs | 10 ++++++---- .../Windows/Threading/DispatcherOperation.TResult.1.cs | 4 +++- .../Windows/Threading/DispatcherOperation.TResult.2.cs | 4 +++- .../Windows/Threading/DispatcherOperation.TResult.3.cs | 4 +++- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.1.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.1.cs index 139cae06c77..1c1338218ef 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.1.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.1.cs @@ -1,6 +1,8 @@ // 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; + namespace System.Windows.Threading; internal sealed class DispatcherOperationAction : DispatcherOperationAction @@ -8,16 +10,16 @@ internal sealed class DispatcherOperationAction : DispatcherOperationActio private readonly TArg _arg; internal DispatcherOperationAction(Dispatcher dispatcher, DispatcherPriority priority, Action method, TArg arg) : base( - dispatcher: dispatcher, - method: method, - priority: priority) + dispatcher: dispatcher, + method: method, + priority: priority) { _arg = arg; } protected sealed override void InvokeDelegateCore() { - Action action = (Action)_method; + Action action = Unsafe.As>(_method); action(_arg); } } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.1.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.1.cs index f29a78b1583..28c5c155891 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.1.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.1.cs @@ -1,6 +1,8 @@ // 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; + namespace System.Windows.Threading; /// @@ -18,7 +20,7 @@ internal DispatcherOperation(Dispatcher dispatcher, DispatcherPriority priority, protected sealed override TResult InvokeDelegateCore() { - Func func = (Func)_method; + Func func = Unsafe.As>(_method); return func(_arg); } } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.2.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.2.cs index c03c31a4a98..41609f1915b 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.2.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.2.cs @@ -1,6 +1,8 @@ // 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; + namespace System.Windows.Threading; /// @@ -20,7 +22,7 @@ internal DispatcherOperation(Dispatcher dispatcher, DispatcherPriority priority, protected sealed override TResult InvokeDelegateCore() { - Func func = (Func)_method; + Func func = Unsafe.As>(_method); return func(_arg1, _arg2); } } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.3.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.3.cs index 7a2f751e204..eba500e01c4 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.3.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.3.cs @@ -1,6 +1,8 @@ // 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; + namespace System.Windows.Threading; /// @@ -22,7 +24,7 @@ internal DispatcherOperation(Dispatcher dispatcher, DispatcherPriority priority, protected sealed override TResult InvokeDelegateCore() { - Func func = (Func)_method; + Func func = Unsafe.As>(_method); return func(_arg1, _arg2, _arg3); } } From f090abcfd2648cc0ba87f5bb98beb4fbf8ab916d Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Fri, 30 May 2025 20:12:59 +0200 Subject: [PATCH 16/54] Avoid apicompat suppressions by limiting "visibility" --- .../ApiCompat/Baselines/WindowsBase-ref-Net48.baseline.txt | 4 +++- .../System/Windows/Threading/DispatcherOperation.Action.0.cs | 4 ++-- .../System/Windows/Threading/DispatcherOperation.Action.1.cs | 2 +- .../System/Windows/Threading/DispatcherOperation.TResult.0.cs | 4 ++-- .../System/Windows/Threading/DispatcherOperation.TResult.1.cs | 2 +- .../System/Windows/Threading/DispatcherOperation.TResult.2.cs | 2 +- .../System/Windows/Threading/DispatcherOperation.TResult.3.cs | 2 +- .../System/Windows/Threading/DispatcherOperation.cs | 3 +-- .../System/Windows/Threading/DispatcherOperationLegacy.cs | 2 +- 9 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/WindowsBase-ref-Net48.baseline.txt b/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/WindowsBase-ref-Net48.baseline.txt index abc3fcc2a71..d49e8532aa1 100644 --- a/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/WindowsBase-ref-Net48.baseline.txt +++ b/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/WindowsBase-ref-Net48.baseline.txt @@ -32,4 +32,6 @@ CannotRemoveBaseTypeOrInterface : Type 'System.Windows.Markup.XmlnsCompatibleWit CannotRemoveBaseTypeOrInterface : Type 'System.Windows.Markup.XmlnsDefinitionAttribute' does not implement interface 'System.Runtime.InteropServices._Attribute' in the implementation but it does in the contract. CannotRemoveBaseTypeOrInterface : Type 'System.Windows.Markup.XmlnsPrefixAttribute' does not implement interface 'System.Runtime.InteropServices._Attribute' in the implementation but it does in the contract. CannotRemoveBaseTypeOrInterface : Type 'System.Windows.Media.DisableDpiAwarenessAttribute' does not implement interface 'System.Runtime.InteropServices._Attribute' in the implementation but it does in the contract. -Total Issues: 33 +MembersMustExist : Member 'protected System.Object System.Windows.Threading.DispatcherOperation.InvokeDelegateCore()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'protected System.Object System.Windows.Threading.DispatcherOperation.InvokeDelegateCore()' does not exist in the implementation but it does exist in the contract. +Total Issues: 35 diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.0.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.0.cs index 5274675ef83..eba3482bc46 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.0.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.0.cs @@ -69,7 +69,7 @@ internal sealed override void InvokeCompletions() } } - protected sealed override void InvokeImpl() + private protected sealed override void InvokeImpl() { SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; @@ -101,7 +101,7 @@ protected sealed override void InvokeImpl() } } - protected virtual void InvokeDelegateCore() + private protected virtual void InvokeDelegateCore() { Action action = (Action)_method; action(); diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.1.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.1.cs index 1c1338218ef..a570d92c224 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.1.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.1.cs @@ -17,7 +17,7 @@ internal DispatcherOperationAction(Dispatcher dispatcher, DispatcherPriority pri _arg = arg; } - protected sealed override void InvokeDelegateCore() + private protected sealed override void InvokeDelegateCore() { Action action = Unsafe.As>(_method); action(_arg); diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.0.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.0.cs index 0281c98c91f..2b29e6ade20 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.0.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.0.cs @@ -100,7 +100,7 @@ internal sealed override void InvokeCompletions() } } - protected sealed override void InvokeImpl() + private protected sealed override void InvokeImpl() { SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; @@ -132,7 +132,7 @@ protected sealed override void InvokeImpl() } } - protected virtual TResult InvokeDelegateCore() + private protected virtual TResult InvokeDelegateCore() { Func func = (Func)_method; return func(); diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.1.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.1.cs index 28c5c155891..4fa2cc90690 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.1.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.1.cs @@ -18,7 +18,7 @@ internal DispatcherOperation(Dispatcher dispatcher, DispatcherPriority priority, _arg = arg; } - protected sealed override TResult InvokeDelegateCore() + private protected sealed override TResult InvokeDelegateCore() { Func func = Unsafe.As>(_method); return func(_arg); diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.2.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.2.cs index 41609f1915b..4411722d453 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.2.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.2.cs @@ -20,7 +20,7 @@ internal DispatcherOperation(Dispatcher dispatcher, DispatcherPriority priority, _arg2 = arg2; } - protected sealed override TResult InvokeDelegateCore() + private protected sealed override TResult InvokeDelegateCore() { Func func = Unsafe.As>(_method); return func(_arg1, _arg2); diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.3.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.3.cs index eba500e01c4..2ef7e351094 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.3.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.3.cs @@ -22,7 +22,7 @@ internal DispatcherOperation(Dispatcher dispatcher, DispatcherPriority priority, _arg3 = arg3; } - protected sealed override TResult InvokeDelegateCore() + private protected sealed override TResult InvokeDelegateCore() { Func func = Unsafe.As>(_method); return func(_arg1, _arg2, _arg3); diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs index 78feb20476f..a56de0f4648 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using MS.Internal; -using MS.Internal.Interop; namespace System.Windows.Threading { @@ -294,7 +293,7 @@ public event EventHandler Completed internal abstract void InvokeCompletions(); // Invoke --> InvokeImpl - protected abstract void InvokeImpl(); + private protected abstract void InvokeImpl(); // Note: this is called by the Dispatcher to actually invoke the operation. // Invoke --> InvokeImpl diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationLegacy.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationLegacy.cs index 50a8d1cabd1..38c18a53ac3 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationLegacy.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationLegacy.cs @@ -59,7 +59,7 @@ internal override void InvokeCompletions() } } - protected override void InvokeImpl() + private protected sealed override void InvokeImpl() { SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current; From 7a029c43f8bb450bbe21c8cc343811e653d001ae Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Fri, 30 May 2025 21:56:52 +0200 Subject: [PATCH 17/54] Change access modifiers --- .../Threading/DispatcherOperation.Action.0.cs | 2 +- .../DispatcherOperation.TResult.0.cs | 2 +- .../Threading/DispatcherOperationLegacy.cs | 2 +- .../DispatcherOperationTaskSource.cs | 23 ++++++++++--------- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.0.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.0.cs index eba3482bc46..e9e25a3a84b 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.0.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.0.cs @@ -53,7 +53,7 @@ internal sealed override void InvokeCompletions() _taskSource.SetCanceled(); break; case DispatcherOperationStatus.Completed: - if (_exception != null) + if (_exception is not null) { _taskSource.SetException(_exception); } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.0.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.0.cs index 2b29e6ade20..b33940df111 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.0.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.0.cs @@ -83,7 +83,7 @@ internal sealed override void InvokeCompletions() _taskSource.SetCanceled(); break; case DispatcherOperationStatus.Completed: - if (_exception != null) + if (_exception is not null) { _taskSource.SetException(_exception); } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationLegacy.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationLegacy.cs index 38c18a53ac3..6483d1a2b52 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationLegacy.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationLegacy.cs @@ -43,7 +43,7 @@ internal override void InvokeCompletions() _taskSource.SetCanceled(); break; case DispatcherOperationStatus.Completed: - if (_exception != null) + if (_exception is not null) { _taskSource.SetException(_exception); } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationTaskSource.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationTaskSource.cs index d023e7cd50b..cfef9b4cbd0 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationTaskSource.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationTaskSource.cs @@ -10,11 +10,11 @@ namespace System.Windows.Threading; /// internal abstract class DispatcherOperationTaskSource { - public abstract void Initialize(DispatcherOperation operation); - public abstract Task GetTask(); - public abstract void SetCanceled(); - public abstract void SetResult(object result); - public abstract void SetException(Exception exception); + internal abstract void Initialize(DispatcherOperation operation); + internal abstract Task GetTask(); + internal abstract void SetCanceled(); + internal abstract void SetResult(object result); + internal abstract void SetException(Exception exception); } internal sealed class DispatcherOperationTaskSource : DispatcherOperationTaskSource @@ -25,44 +25,45 @@ internal sealed class DispatcherOperationTaskSource : DispatcherOperati /// Create the underlying TaskCompletionSource and set the DispatcherOperation as the Task's AsyncState. /// /// - public override void Initialize(DispatcherOperation operation) + internal override void Initialize(DispatcherOperation operation) { Debug.Assert(_taskCompletionSource is null); _taskCompletionSource = new TaskCompletionSource(new DispatcherOperationTaskMapping(operation)); } - public override Task GetTask() + internal override Task GetTask() { Debug.Assert(_taskCompletionSource is not null); return _taskCompletionSource.Task; } - public override void SetCanceled() + internal override void SetCanceled() { Debug.Assert(_taskCompletionSource is not null); _taskCompletionSource.SetCanceled(); } - public override void SetResult(object result) + internal override void SetResult(object result) { Debug.Assert(_taskCompletionSource is not null); _taskCompletionSource.SetResult((TResult)result); } - public void SetResult(TResult result) + internal void SetResult(TResult result) { Debug.Assert(_taskCompletionSource is not null); _taskCompletionSource.SetResult(result); } - public override void SetException(Exception exception) + internal override void SetException(Exception exception) { Debug.Assert(_taskCompletionSource is not null); + Debug.Assert(exception is not null); _taskCompletionSource.SetException(exception); } From b66c4146df5d268d58fa9f2060f31b824dd99ad8 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Fri, 30 May 2025 21:58:25 +0200 Subject: [PATCH 18/54] Interpolation --- .../WindowsBase/System/Windows/Threading/DispatcherOperation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs index a56de0f4648..07f8c2af709 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs @@ -71,7 +71,7 @@ internal DispatcherOperation(Dispatcher dispatcher, Delegate method, DispatcherP /// /// Returns a string representation of the operation to be invoked. /// - internal string Name => _method.Method.DeclaringType + "." + _method.Method.Name; + internal string Name => $"{_method.Method.DeclaringType}.{_method.Method.Name}"; /// /// Gets or sets the priority of this operation within the From 41f9af102711be1d2160026603c7ddac9703d8f9 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Fri, 30 May 2025 22:25:05 +0200 Subject: [PATCH 19/54] Fix DWriteForwarder/System.Printing dependencies in the build process --- Microsoft.Dotnet.Wpf.sln | 10 ++++++++++ .../Windows/Threading/DispatcherTests.cs | 20 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/Microsoft.Dotnet.Wpf.sln b/Microsoft.Dotnet.Wpf.sln index 09509d6f269..1b156137dbf 100644 --- a/Microsoft.Dotnet.Wpf.sln +++ b/Microsoft.Dotnet.Wpf.sln @@ -34,6 +34,9 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsBase", "src\Microsoft.DotNet.Wpf\src\WindowsBase\WindowsBase.csproj", "{FA69991B-9696-42D0-A5C7-F5E73F0DEE9E}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DirectWriteForwarder", "src\Microsoft.DotNet.Wpf\src\DirectWriteForwarder\DirectWriteForwarder.vcxproj", "{50A5318F-3B9A-48B9-9615-D5FA9D6D9C3E}" + ProjectSection(ProjectDependencies) = postProject + {FA69991B-9696-42D0-A5C7-F5E73F0DEE9E} = {FA69991B-9696-42D0-A5C7-F5E73F0DEE9E} + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PresentationCore", "src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj", "{74C63A45-EAE5-407A-9F89-E0C6BC604841}" EndProject @@ -157,6 +160,13 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PresentationFramework.Royale-ref", "src\Microsoft.DotNet.Wpf\src\Themes\PresentationFramework.Royale\ref\PresentationFramework.Royale-ref.csproj", "{598B6188-937C-448B-8E6B-925F000BC076}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "System.Printing", "src\Microsoft.DotNet.Wpf\src\System.Printing\System.Printing.vcxproj", "{765E6BBC-772B-4808-BB72-E85615E8F237}" + ProjectSection(ProjectDependencies) = postProject + {17AA6CC2-CAE3-429C-B065-B76B8E14C632} = {17AA6CC2-CAE3-429C-B065-B76B8E14C632} + {74C63A45-EAE5-407A-9F89-E0C6BC604841} = {74C63A45-EAE5-407A-9F89-E0C6BC604841} + {9AC36357-34B7-40A1-95CA-FE9F46D089A7} = {9AC36357-34B7-40A1-95CA-FE9F46D089A7} + {B0C1157E-8664-44C4-AD8E-35CD6A78C769} = {B0C1157E-8664-44C4-AD8E-35CD6A78C769} + {FA69991B-9696-42D0-A5C7-F5E73F0DEE9E} = {FA69991B-9696-42D0-A5C7-F5E73F0DEE9E} + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PresentationUI", "src\Microsoft.DotNet.Wpf\src\PresentationUI\PresentationUI.csproj", "{B8E5B99C-D162-4DCA-9C4F-90CF2CDE942E}" EndProject diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs index a0ad83dee46..9616e58b9e2 100644 --- a/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs @@ -206,6 +206,26 @@ public void Invoke_SameThread_DispatcherOperation_TReturn_TArg1_TArg2_Success() Assert.Equal(24.0, result); } + [WpfFact] + public void Invoke_SameThread_TReturn_TArg1_TArg2_PropagatesException() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + + static double action(int parameter, double doubleParameter) => throw new InvalidOperationException("Exception for testing."); + // Send + current thread forces direct callback invocation + Assert.Throws(() => dispatcher.Invoke(action, DispatcherPriority.Send, TimeSpan.FromSeconds(3), 4, 14.5)); + } + + [WpfFact] + public void Invoke_SameThread_DispatcherOperation_TReturn_TArg1_TArg2_PropagatesException() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + + static double action(int parameter, double doubleParameter) => throw new InvalidOperationException("Exception for testing."); + // Anything different than Send + current thread is DispatcherOperation allocation and queue pass + Assert.Throws(() => dispatcher.Invoke(action, DispatcherPriority.Background, TimeSpan.FromSeconds(3), 4, 14.5)); + } + [WpfTheory] [InlineData(DispatcherPriority.Invalid)] [InlineData(DispatcherPriority.Invalid - 1)] From 9d09b65d2fbd698ef99d69f1d5e46ef86541f941 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 01:11:20 +0200 Subject: [PATCH 20/54] Add a few more tests --- .../Windows/Threading/DispatcherTests.cs | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs index 9616e58b9e2..8c7ac9adc69 100644 --- a/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs @@ -211,7 +211,7 @@ public void Invoke_SameThread_TReturn_TArg1_TArg2_PropagatesException() { Dispatcher dispatcher = Dispatcher.CurrentDispatcher; - static double action(int parameter, double doubleParameter) => throw new InvalidOperationException("Exception for testing."); + static double action(int parameter, double doubleParameter) => throw new InvalidOperationException("Throw this"); // Send + current thread forces direct callback invocation Assert.Throws(() => dispatcher.Invoke(action, DispatcherPriority.Send, TimeSpan.FromSeconds(3), 4, 14.5)); } @@ -221,11 +221,31 @@ public void Invoke_SameThread_DispatcherOperation_TReturn_TArg1_TArg2_Propagates { Dispatcher dispatcher = Dispatcher.CurrentDispatcher; - static double action(int parameter, double doubleParameter) => throw new InvalidOperationException("Exception for testing."); + static double action(int parameter, double doubleParameter) => throw new InvalidOperationException("Throw this"); // Anything different than Send + current thread is DispatcherOperation allocation and queue pass Assert.Throws(() => dispatcher.Invoke(action, DispatcherPriority.Background, TimeSpan.FromSeconds(3), 4, 14.5)); } + [WpfFact] + public void Invoke_SameThread_Legacy_PropagatesException() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + + static object action(object exceptionText) => throw new InvalidOperationException((string)exceptionText); + // Send + current thread forces direct callback invocation + Assert.Throws(() => dispatcher.Invoke(DispatcherPriority.Send, TimeSpan.FromSeconds(3), (DispatcherOperationCallback)action, "Throw this")); + } + + [WpfFact] + public void Invoke_SameThread_DispatcherOperation_Legacy_PropagatesException() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + + static object action(object exceptionText) => throw new InvalidOperationException((string)exceptionText); + // Anything different than Send + current thread is DispatcherOperation allocation and queue pass + Assert.Throws(() => dispatcher.Invoke(DispatcherPriority.Background, TimeSpan.FromSeconds(3), (DispatcherOperationCallback)action, "Throw this")); + } + [WpfTheory] [InlineData(DispatcherPriority.Invalid)] [InlineData(DispatcherPriority.Invalid - 1)] From c52cf1a708b3e571b4f6fd70a00a9f77809a9ca4 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 01:21:50 +0200 Subject: [PATCH 21/54] Fix wrappers --- .../MS/internal/Automation/DockProviderWrapper.cs | 12 ++---------- .../MS/internal/Automation/GridProviderWrapper.cs | 6 +++--- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs index b20ae8c78cd..d31dfaea77c 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -62,10 +62,7 @@ public void SetDockPosition(DockPosition dockPosition) public DockPosition DockPosition { - get - { - return (DockPosition)ElementUtil.Invoke(_peer, new DispatcherOperationCallback( GetDockPosition ), null); - } + get => ElementUtil.Invoke(_peer, static (state) => state.DockPosition, _iface); } #endregion Interface IDockProvider @@ -100,11 +97,6 @@ private object SetDockPosition( object arg ) return null; } - private object GetDockPosition( object unused ) - { - return _iface.DockPosition; - } - #endregion Private Methods diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs index 7c122a012be..3fdc956a73e 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs @@ -48,17 +48,17 @@ private GridProviderWrapper( AutomationPeer peer, IGridProvider iface ) public IRawElementProviderSimple GetItem(int row, int column) { - return ElementUtil.Invoke(_peer, static (state, rowColumn) => state.GetItem(rowColumn[0], rowColumn[1]), this, new int[] { row, column }); + return ElementUtil.Invoke(_peer, static (state, rowColumn) => state.GetItem(rowColumn[0], rowColumn[1]), _iface, new int[] { row, column }); } public int RowCount { - get => ElementUtil.Invoke(_peer, static (state) => state.RowCount, this); + get => ElementUtil.Invoke(_peer, static (state) => state.RowCount, _iface); } public int ColumnCount { - get => ElementUtil.Invoke(_peer, static (state) => state.ColumnCount, this); + get => ElementUtil.Invoke(_peer, static (state) => state.ColumnCount, _iface); } #endregion Interface IGridProvider From 74a202569cdcc6f25be6c7f3299fb28830af290b Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 01:28:02 +0200 Subject: [PATCH 22/54] ScrollProviderWrapper / ExpandCollapseProviderWrapper --- .../ExpandCollapseProviderWrapper.cs | 12 +--- .../Automation/ScrollProviderWrapper.cs | 68 +++---------------- 2 files changed, 12 insertions(+), 68 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs index a543c985d4c..2a104f441d2 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -67,10 +67,7 @@ public void Collapse() public ExpandCollapseState ExpandCollapseState { - get - { - return (ExpandCollapseState) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetExpandCollapseState ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.ExpandCollapseState, _iface); } #endregion Interface IExpandCollapseProvider @@ -111,11 +108,6 @@ private object Collapse( object unused ) return null; } - private object GetExpandCollapseState( object unused ) - { - return _iface.ExpandCollapseState; - } - #endregion Private Methods diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs index 0c86bde70c4..e2e411faf98 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -68,50 +68,32 @@ public void SetScrollPercent( double horizontalPercent, double verticalPercent ) public double HorizontalScrollPercent { - get - { - return (double) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetHorizontalScrollPercent ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.HorizontalScrollPercent, _iface); } public double VerticalScrollPercent { - get - { - return (double) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetVerticalScrollPercent ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.VerticalScrollPercent, _iface); } public double HorizontalViewSize { - get - { - return (double) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetHorizontalViewSize ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.HorizontalViewSize, _iface); } public double VerticalViewSize { - get - { - return (double) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetVerticalViewSize ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.VerticalViewSize, _iface); } - + public bool HorizontallyScrollable { - get - { - return (bool) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetHorizontallyScrollable ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.HorizontallyScrollable, _iface); } - + public bool VerticallyScrollable { - get - { - return (bool) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetVerticallyScrollable ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.VerticallyScrollable, _iface); } #endregion Interface IScrollProvider @@ -122,7 +104,7 @@ public bool VerticallyScrollable // Internal Methods // //------------------------------------------------------ - + #region Internal Methods internal static object Wrap( AutomationPeer peer, object iface ) @@ -153,36 +135,6 @@ private object SetScrollPercent( object arg ) _iface.SetScrollPercent( args[ 0 ], args[ 1 ] ); return null; } - - private object GetHorizontalScrollPercent( object unused ) - { - return _iface.HorizontalScrollPercent; - } - - private object GetVerticalScrollPercent( object unused ) - { - return _iface.VerticalScrollPercent; - } - - private object GetHorizontalViewSize( object unused ) - { - return _iface.HorizontalViewSize; - } - - private object GetVerticalViewSize( object unused ) - { - return _iface.VerticalViewSize; - } - - private object GetHorizontallyScrollable( object unused ) - { - return _iface.HorizontallyScrollable; - } - - private object GetVerticallyScrollable( object unused ) - { - return _iface.VerticallyScrollable; - } #endregion Private Methods From 3abb086e8184a27ab82e940d5a017024e2019e2e Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 01:29:57 +0200 Subject: [PATCH 23/54] RangeValueProviderWrapper --- .../Automation/RangeValueProviderWrapper.cs | 64 +++---------------- 1 file changed, 8 insertions(+), 56 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs index 82905d1e0bb..8e08454062b 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -61,50 +61,32 @@ public void SetValue( double val ) public double Value { - get - { - return (double) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetValue ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.Value, _iface); } public bool IsReadOnly { - get - { - return (bool) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetIsReadOnly ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.IsReadOnly, _iface); } public double Maximum { - get - { - return (double) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetMaximum ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.Maximum, _iface); } public double Minimum { - get - { - return (double) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetMinimum ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.Minimum, _iface); } public double LargeChange { - get - { - return (double) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetLargeChange ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.LargeChange, _iface); } public double SmallChange { - get - { - return (double) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetSmallChange ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.SmallChange, _iface); } #endregion Interface IRangeValueProvider @@ -115,7 +97,7 @@ public double SmallChange // Internal Methods // //------------------------------------------------------ - + #region Internal Methods internal static object Wrap( AutomationPeer peer, object iface ) @@ -139,36 +121,6 @@ private object SetValueInternal( object arg ) return null; } - private object GetValue( object unused ) - { - return _iface.Value; - } - - private object GetIsReadOnly( object unused ) - { - return _iface.IsReadOnly; - } - - private object GetMaximum( object unused ) - { - return _iface.Maximum; - } - - private object GetMinimum( object unused ) - { - return _iface.Minimum; - } - - private object GetLargeChange( object unused ) - { - return _iface.LargeChange; - } - - private object GetSmallChange( object unused ) - { - return _iface.SmallChange; - } - #endregion Private Methods From 473af5931bc99f72cf3ea0d32710a62fc5e89c6f Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 01:34:12 +0200 Subject: [PATCH 24/54] GridItemProviderWrapper --- .../Automation/GridItemProviderWrapper.cs | 65 ++----------------- 1 file changed, 7 insertions(+), 58 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs index e7a20f62d47..071ddfd0fbc 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -8,7 +8,6 @@ // // -using System.Windows.Threading; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -56,42 +55,27 @@ private GridItemProviderWrapper( AutomationPeer peer, IGridItemProvider iface ) public int Row { - get - { - return (int) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetRow ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.Row, _iface); } public int Column { - get - { - return (int) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetColumn ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.Column, _iface); } public int RowSpan { - get - { - return (int) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetRowSpan ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.RowSpan, _iface); } public int ColumnSpan { - get - { - return (int) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetColumnSpan ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.ColumnSpan, _iface); } public IRawElementProviderSimple ContainingGrid { - get - { - return (IRawElementProviderSimple) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetContainingGrid ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.ContainingGrid, _iface); } #endregion Interface IGridItemProvider @@ -102,7 +86,7 @@ public IRawElementProviderSimple ContainingGrid // Internal Methods // //------------------------------------------------------ - + #region Internal Methods internal static object Wrap( AutomationPeer peer, object iface ) @@ -112,41 +96,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object GetRow( object unused ) - { - return _iface.Row; - } - - private object GetColumn( object unused ) - { - return _iface.Column; - } - - private object GetRowSpan( object unused ) - { - return _iface.RowSpan; - } - - private object GetColumnSpan( object unused ) - { - return _iface.ColumnSpan; - } - - private object GetContainingGrid( object unused ) - { - return _iface.ContainingGrid; - } - - #endregion Private Methods - //------------------------------------------------------ // From da104f36100a0cb78eae65af57018c0f3f8fc74c Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 03:25:31 +0200 Subject: [PATCH 25/54] Few more tests --- .../Windows/Threading/DispatcherTests.cs | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs index 8c7ac9adc69..564217a6513 100644 --- a/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs @@ -146,6 +146,16 @@ public void Invoke_SameThread_TReturn_Success() Assert.Equal(5, result); } + [WpfFact] + public void Invoke_SameThread_TReturn_PropagatesException() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + + static double action() => throw new InvalidOperationException("Throw this"); + // Send + current thread forces direct callback invocation + Assert.Throws(() => dispatcher.Invoke(action, DispatcherPriority.Send, CancellationToken.None, TimeSpan.FromSeconds(3))); + } + [WpfFact] public void Invoke_SameThread_TArg_Success() { @@ -158,6 +168,16 @@ public void Invoke_SameThread_TArg_Success() Assert.Equal(9, result); } + [WpfFact] + public void Invoke_SameThread_TArg_PropagatesException() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + + static void action(double action) => throw new InvalidOperationException("Throw this"); + // Send + current thread forces direct callback invocation + Assert.Throws(() => dispatcher.Invoke(action, DispatcherPriority.Send, CancellationToken.None, TimeSpan.FromSeconds(3), 7.0)); + } + [WpfFact] public void Invoke_SameThread_TReturn_TArg_Success() { @@ -236,6 +256,41 @@ public void Invoke_SameThread_Legacy_PropagatesException() Assert.Throws(() => dispatcher.Invoke(DispatcherPriority.Send, TimeSpan.FromSeconds(3), (DispatcherOperationCallback)action, "Throw this")); } + [WpfFact] + public void Invoke_SameThread_Legacy_UnhandledException_Handled_DoesNotThrow() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + dispatcher.UnhandledException += unhandledException; + + // Legacy Invoke with unhandled exception handler will not propagate the exception + static void unhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) => e.Handled = true; + static object action(object exceptionText) => throw new InvalidOperationException((string)exceptionText); + + // Send + current thread forces direct callback invocation + dispatcher.Invoke(DispatcherPriority.Send, TimeSpan.FromSeconds(3), (DispatcherOperationCallback)action, "Throw this"); + + dispatcher.UnhandledException -= unhandledException; + } + + [WpfFact] + public void Invoke_SameThread_Legacy_UnhandledExceptionFilter_RequestCatch_PropagatesException() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + dispatcher.UnhandledException += unhandledException; + dispatcher.UnhandledExceptionFilter += unhandledExceptionFilter; + + // Legacy Invoke with unhandled exception handler will not propagate the exception unless the filter requests it + static void unhandledExceptionFilter(object sender, DispatcherUnhandledExceptionFilterEventArgs e) => e.RequestCatch = false; + static void unhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) => e.Handled = true; + static object action(object exceptionText) => throw new InvalidOperationException((string)exceptionText); + + // Send + current thread forces direct callback invocation + Assert.Throws(() => dispatcher.Invoke(DispatcherPriority.Send, TimeSpan.FromSeconds(3), (DispatcherOperationCallback)action, "Throw this")); + + dispatcher.UnhandledException -= unhandledException; + dispatcher.UnhandledExceptionFilter -= unhandledExceptionFilter; + } + [WpfFact] public void Invoke_SameThread_DispatcherOperation_Legacy_PropagatesException() { @@ -246,6 +301,41 @@ public void Invoke_SameThread_DispatcherOperation_Legacy_PropagatesException() Assert.Throws(() => dispatcher.Invoke(DispatcherPriority.Background, TimeSpan.FromSeconds(3), (DispatcherOperationCallback)action, "Throw this")); } + [WpfFact] + public void Invoke_SameThread_DispatcherOperation_Legacy_UnhandledException_Handled_DoesNotThrow() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + dispatcher.UnhandledException += unhandledException; + + // Legacy Invoke with unhandled exception handler will not propagate the exception + static void unhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) => e.Handled = true; + static object action(object exceptionText) => throw new InvalidOperationException((string)exceptionText); + + // Anything different than Send + current thread is DispatcherOperation allocation and queue pass + dispatcher.Invoke(DispatcherPriority.Background, TimeSpan.FromSeconds(3), (DispatcherOperationCallback)action, "Throw this"); + + dispatcher.UnhandledException -= unhandledException; + } + + [WpfFact] + public void Invoke_SameThread_DispatcherOperation_Legacy_UnhandledExceptionFilter_RequestCatch_PropagatesException() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + dispatcher.UnhandledException += unhandledException; + dispatcher.UnhandledExceptionFilter += unhandledExceptionFilter; + + // Legacy Invoke with unhandled exception handler will not propagate the exception unless the filter requests it + static void unhandledExceptionFilter(object sender, DispatcherUnhandledExceptionFilterEventArgs e) => e.RequestCatch = false; + static void unhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) => e.Handled = true; + static object action(object exceptionText) => throw new InvalidOperationException((string)exceptionText); + + // Anything different than Send + current thread is DispatcherOperation allocation and queue pass + Assert.Throws(() => dispatcher.Invoke(DispatcherPriority.Background, TimeSpan.FromSeconds(3), (DispatcherOperationCallback)action, "Throw this")); + + dispatcher.UnhandledException -= unhandledException; + dispatcher.UnhandledExceptionFilter -= unhandledExceptionFilter; + } + [WpfTheory] [InlineData(DispatcherPriority.Invalid)] [InlineData(DispatcherPriority.Invalid - 1)] From b1d70468cecd0950598fbeb68af03d12c8eff1be Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 03:39:36 +0200 Subject: [PATCH 26/54] TextRangeProviderWrapper --- .../Automation/TextRangeProviderWrapper.cs | 60 ++++--------------- 1 file changed, 11 insertions(+), 49 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs index 2af9bd62450..bcf0443d04f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs @@ -45,12 +45,11 @@ public ITextRangeProvider Clone() public bool Compare(ITextRangeProvider range) { - if (!(range is TextRangeProviderWrapper)) - { - throw new ArgumentException(SR.Format(SR.TextRangeProvider_InvalidRangeProvider, "range")); - } + if (range is not TextRangeProviderWrapper) + throw new ArgumentException(SR.Format(SR.TextRangeProvider_InvalidRangeProvider, nameof(range))); - return (bool)ElementUtil.Invoke(_peer, new DispatcherOperationCallback(Compare), range); + // Note: We always need to unwrap the range argument here. + return ElementUtil.Invoke(_peer, static (state, range) => state.Compare(UnwrapArgument(range)), _iface, range); } public int CompareEndpoints(TextPatternRangeEndpoint endpoint, ITextRangeProvider targetRange, TextPatternRangeEndpoint targetEndpoint) @@ -84,24 +83,23 @@ public ITextRangeProvider FindText(string text, bool backward, bool ignoreCase) public object GetAttributeValue(int attribute) { - object[] args = new object[] { attribute }; - return ElementUtil.Invoke(_peer, new DispatcherOperationCallback(GetAttributeValue), args); + // Note: If an attribute value is ever a range then we'll need to wrap/unwrap it appropriately here. + return ElementUtil.Invoke(_peer, static (state, attribute) => state.GetAttributeValue(attribute), _iface, attribute); } - public double [] GetBoundingRectangles() + public double[] GetBoundingRectangles() { - return (double [])ElementUtil.Invoke(_peer, new DispatcherOperationCallback(GetBoundingRectangles), null); + return ElementUtil.Invoke(_peer, static (state) => state.GetBoundingRectangles(), _iface); } public IRawElementProviderSimple GetEnclosingElement() { - return (IRawElementProviderSimple)ElementUtil.Invoke(_peer, new DispatcherOperationCallback(GetEnclosingElement), null); + return ElementUtil.Invoke(_peer, static (state) => state.GetEnclosingElement(), _iface); } public string GetText(int maxLength) { - object[] args = new object[] {maxLength}; - return (string)ElementUtil.Invoke(_peer, new DispatcherOperationCallback(GetText), args); + return ElementUtil.Invoke(_peer, static (state, maxLength) => state.GetText(maxLength), _iface, maxLength); } public int Move(TextUnit unit, int count) @@ -149,7 +147,7 @@ public void ScrollIntoView(bool alignToTop) public IRawElementProviderSimple[] GetChildren() { - return (IRawElementProviderSimple[])ElementUtil.Invoke(_peer, new DispatcherOperationCallback(GetChildren), null); + return ElementUtil.Invoke(_peer, static (state) => state.GetChildren(), _iface); } @@ -218,12 +216,6 @@ private object Clone(object unused) return TextRangeProviderWrapper.WrapArgument( _iface.Clone(), _peer ); } - private object Compare(object arg) - { - ITextRangeProvider range = (ITextRangeProvider)arg; - return _iface.Compare( TextRangeProviderWrapper.UnwrapArgument( range ) ); - } - private object CompareEndpoints(object arg) { object[] args = (object[])arg; @@ -259,31 +251,6 @@ private object FindText(object arg) return TextRangeProviderWrapper.WrapArgument( _iface.FindText(text, backward, ignoreCase), _peer ); } - private object GetAttributeValue(object arg) - { - object[] args = (object[])arg; - int attribute = (int)args[0]; - return _iface.GetAttributeValue(attribute); - // note: if an attribute value is ever a range then we'll need to wrap/unwrap it appropriately here. - } - - private object GetBoundingRectangles(object unused) - { - return _iface.GetBoundingRectangles(); - } - - private object GetEnclosingElement(object unused) - { - return _iface.GetEnclosingElement(); - } - - private object GetText(object arg) - { - object[] args = (object[])arg; - int maxLength = (int)args[0]; - return _iface.GetText(maxLength); - } - private object Move(object arg) { object[] args = (object[])arg; @@ -336,11 +303,6 @@ private object ScrollIntoView(object arg) return null; } - private object GetChildren(object unused) - { - return _iface.GetChildren(); - } - #endregion Private Methods From 7ce8720e6490489706df08f5a03b919fb7917cd9 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 03:42:57 +0200 Subject: [PATCH 27/54] TableItemProviderWrapper --- .../Automation/TableItemProviderWrapper.cs | 81 +++---------------- 1 file changed, 10 insertions(+), 71 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs index 6f894d8857d..595597672ea 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -56,52 +56,37 @@ private TableItemProviderWrapper( AutomationPeer peer, ITableItemProvider iface public int Row { - get - { - return (int) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetRow ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.Row, _iface); } public int Column { - get - { - return (int) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetColumn ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.Column, _iface); } public int RowSpan { - get - { - return (int) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetRowSpan ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.RowSpan, _iface); } public int ColumnSpan { - get - { - return (int) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetColumnSpan ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.ColumnSpan, _iface); } public IRawElementProviderSimple ContainingGrid { - get - { - return (IRawElementProviderSimple) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetContainingGrid ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.ContainingGrid, _iface); } - public IRawElementProviderSimple [] GetRowHeaderItems() + public IRawElementProviderSimple[] GetRowHeaderItems() { - return (IRawElementProviderSimple []) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetRowHeaderItems ), null ); + return ElementUtil.Invoke(_peer, static (state) => state.GetRowHeaderItems(), _iface); } - public IRawElementProviderSimple [] GetColumnHeaderItems() + public IRawElementProviderSimple[] GetColumnHeaderItems() { - return (IRawElementProviderSimple []) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetColumnHeaderItems ), null ); + return ElementUtil.Invoke(_peer, static (state) => state.GetColumnHeaderItems(), _iface); } #endregion Interface ITableItemProvider @@ -122,52 +107,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object GetRow( object unused ) - { - return _iface.Row; - } - - private object GetColumn( object unused ) - { - return _iface.Column; - } - - private object GetRowSpan( object unused ) - { - return _iface.RowSpan; - } - - private object GetColumnSpan( object unused ) - { - return _iface.ColumnSpan; - } - - private object GetContainingGrid( object unused ) - { - return _iface.ContainingGrid; - } - - private object GetRowHeaderItems( object unused ) - { - return _iface.GetRowHeaderItems(); - } - - private object GetColumnHeaderItems( object unused ) - { - return _iface.GetColumnHeaderItems(); - } - - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields From 8bfc0a1d87db85d44645d1e3c1b24f86b2d45d32 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 03:44:43 +0200 Subject: [PATCH 28/54] SelectionProviderWrapper --- .../Automation/SelectionProviderWrapper.cs | 46 +++---------------- 1 file changed, 7 insertions(+), 39 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs index 9e19f682bac..abc82f1cc7f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -54,25 +54,19 @@ private SelectionProviderWrapper( AutomationPeer peer, ISelectionProvider iface #region Interface ISelectionProvider - public IRawElementProviderSimple [] GetSelection() + public IRawElementProviderSimple[] GetSelection() { - return (IRawElementProviderSimple []) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetSelection ), null ); + return ElementUtil.Invoke(_peer, static (state) => state.GetSelection(), _iface); } public bool CanSelectMultiple { - get - { - return (bool) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetCanSelectMultiple ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.CanSelectMultiple, _iface); } - public bool IsSelectionRequired + public bool IsSelectionRequired { - get - { - return (bool) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetIsSelectionRequired ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.IsSelectionRequired, _iface); } #endregion Interface ISelectionProvider @@ -83,7 +77,7 @@ public bool IsSelectionRequired // Internal Methods // //------------------------------------------------------ - + #region Internal Methods internal static object Wrap( AutomationPeer peer, object iface ) @@ -93,32 +87,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object GetSelection( object unused ) - { - return _iface.GetSelection(); - } - - private object GetCanSelectMultiple( object unused ) - { - return _iface.CanSelectMultiple; - } - - private object GetIsSelectionRequired( object unused ) - { - return _iface.IsSelectionRequired; - } - - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields From c668e4cd6462c8dd62d9989f34bac6f6c55d605d Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 03:48:00 +0200 Subject: [PATCH 29/54] WindowProviderWrapper --- .../Automation/WindowProviderWrapper.cs | 85 ++++--------------- 1 file changed, 16 insertions(+), 69 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs index cb92daf6d23..7163cbdfcfd 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -65,57 +65,39 @@ public void Close() ElementUtil.Invoke( _peer, new DispatcherOperationCallback( Close ), null ); } - public bool WaitForInputIdle( int milliseconds ) + public bool WaitForInputIdle(int milliseconds) { - return (bool)ElementUtil.Invoke( _peer, new DispatcherOperationCallback( WaitForInputIdle ), milliseconds ); + return ElementUtil.Invoke(_peer, static (state, milliseconds) => state.WaitForInputIdle(milliseconds), _iface, milliseconds); } - public bool Maximizable + public bool Maximizable { - get - { - return (bool) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetMaximizable ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.Maximizable, _iface); } - + public bool Minimizable { - get - { - return (bool) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetMinimizable ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.Minimizable, _iface); } - + public bool IsModal { - get - { - return (bool) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetIsModal ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.IsModal, _iface); } - + public WindowVisualState VisualState { - get - { - return (WindowVisualState) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetVisualState ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.VisualState, _iface); } - + public WindowInteractionState InteractionState { - get - { - return (WindowInteractionState) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetInteractionState ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.InteractionState, _iface); } - + public bool IsTopmost { - get - { - return (bool) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetIsTopmost ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.IsTopmost, _iface); } #endregion Interface IWindowProvider @@ -126,7 +108,7 @@ public bool IsTopmost // Internal Methods // //------------------------------------------------------ - + #region Internal Methods internal static object Wrap( AutomationPeer peer, object iface) @@ -150,47 +132,12 @@ private object SetVisualState( object arg ) return null; } - private object WaitForInputIdle( object arg ) - { - return _iface.WaitForInputIdle( (int) arg ); - } - private object Close( object unused ) { _iface.Close(); return null; } - private object GetMaximizable( object unused ) - { - return _iface.Maximizable; - } - - private object GetMinimizable( object unused ) - { - return _iface.Minimizable; - } - - private object GetIsModal( object unused ) - { - return _iface.IsModal; - } - - private object GetVisualState( object unused ) - { - return _iface.VisualState; - } - - private object GetInteractionState( object unused ) - { - return _iface.InteractionState; - } - - private object GetIsTopmost( object unused ) - { - return _iface.IsTopmost; - } - #endregion Private Methods From 118ec86053fdc717e0eba6e8ca2b9831c7f67318 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 03:49:20 +0200 Subject: [PATCH 30/54] ValueProviderWrapper --- .../Automation/ValueProviderWrapper.cs | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs index c6807172d67..1a3152e048b 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -61,18 +61,12 @@ public void SetValue( string val ) public string Value { - get - { - return (string) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetValue ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.Value, _iface); } public bool IsReadOnly { - get - { - return (bool) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetIsReadOnly ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.IsReadOnly, _iface); } #endregion Interface IValueProvider @@ -83,7 +77,7 @@ public bool IsReadOnly // Internal Methods // //------------------------------------------------------ - + #region Internal Methods internal static object Wrap( AutomationPeer peer, object iface ) @@ -107,16 +101,6 @@ private object SetValueInternal( object arg ) return null; } - private object GetValue( object unused ) - { - return _iface.Value; - } - - private object GetIsReadOnly( object unused ) - { - return _iface.IsReadOnly; - } - #endregion Private Methods From c720586da24e95c799bbc62aa00e8b84e3203ff1 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 03:53:03 +0200 Subject: [PATCH 31/54] TableProviderWrapper --- .../Automation/TableProviderWrapper.cs | 74 +++---------------- 1 file changed, 12 insertions(+), 62 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs index dae898b1883..dba5b147100 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -57,41 +57,32 @@ private TableProviderWrapper( AutomationPeer peer, ITableProvider iface ) public IRawElementProviderSimple GetItem(int row, int column) { - return (IRawElementProviderSimple) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetItem ), new int [ ] { row, column } ); - } + return ElementUtil.Invoke(_peer, static (state, rowColumn) => state.GetItem(rowColumn[0], rowColumn[1]), _iface, new int[] { row, column }); + } public int RowCount { - get - { - return (int) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetRowCount ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.RowCount, _iface); } public int ColumnCount { - get - { - return (int) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetColumnCount ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.ColumnCount, _iface); } - - public IRawElementProviderSimple [] GetRowHeaders() + + public IRawElementProviderSimple[] GetRowHeaders() { - return (IRawElementProviderSimple []) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetRowHeaders ), null ); + return ElementUtil.Invoke(_peer, static (state) => state.GetRowHeaders(), _iface); } - public IRawElementProviderSimple [] GetColumnHeaders() + public IRawElementProviderSimple[] GetColumnHeaders() { - return (IRawElementProviderSimple []) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetColumnHeaders ), null ); + return ElementUtil.Invoke(_peer, static (state) => state.GetColumnHeaders(), _iface); } public RowOrColumnMajor RowOrColumnMajor { - get - { - return (RowOrColumnMajor) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetRowOrColumnMajor ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.RowOrColumnMajor, _iface); } #endregion Interface ITableProvider @@ -102,7 +93,7 @@ public RowOrColumnMajor RowOrColumnMajor // Internal Methods // //------------------------------------------------------ - + #region Internal Methods internal static object Wrap( AutomationPeer peer, object iface ) @@ -112,47 +103,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object GetItem( object arg ) - { - int [ ] coords = (int [ ]) arg; - return _iface.GetItem( coords[ 0 ], coords[ 1 ] ); - } - - private object GetRowCount( object unused ) - { - return _iface.RowCount; - } - - private object GetColumnCount( object unused ) - { - return _iface.ColumnCount; - } - - private object GetRowHeaders( object unused ) - { - return _iface.GetRowHeaders(); - } - - private object GetColumnHeaders( object unused ) - { - return _iface.GetColumnHeaders(); - } - - private object GetRowOrColumnMajor( object unused ) - { - return _iface.RowOrColumnMajor; - } - - #endregion Private Methods - //------------------------------------------------------ // From 057b3df9c73cfc825bf5c8578f05c44b477cd896 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 03:54:29 +0200 Subject: [PATCH 32/54] SelectionItemProviderWrapper --- .../SelectionItemProviderWrapper.cs | 26 ++++--------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs index 8c2305e6f22..8ce83b916e9 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -71,19 +71,13 @@ public void RemoveFromSelection() } public bool IsSelected - { - get - { - return (bool) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetIsSelected ), null ); - } + { + get => ElementUtil.Invoke(_peer, static (state) => state.IsSelected, _iface); } public IRawElementProviderSimple SelectionContainer { - get - { - return (IRawElementProviderSimple) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetSelectionContainer ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.SelectionContainer, _iface); } #endregion Interface ISelectionItemProvider @@ -94,7 +88,7 @@ public IRawElementProviderSimple SelectionContainer // Internal Methods // //------------------------------------------------------ - + #region Internal Methods internal static object Wrap( AutomationPeer peer, object iface ) @@ -130,16 +124,6 @@ private object RemoveFromSelection( object unused ) return null; } - private object GetIsSelected( object unused ) - { - return _iface.IsSelected; - } - - private object GetSelectionContainer( object unused ) - { - return _iface.SelectionContainer; - } - #endregion Private Methods From f1719ad76ff6913ca20e4f2985a11ac333001f08 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 04:09:58 +0200 Subject: [PATCH 33/54] TransformProviderWrapper --- .../Automation/TransformProviderWrapper.cs | 40 ++++--------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs index 2ab80ad29fa..927d875103a 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -72,28 +72,19 @@ public void Rotate( double degrees ) public bool CanMove { - get - { - return (bool) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetCanMove ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.CanMove, _iface); } - + public bool CanResize { - get - { - return (bool) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetCanResize ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.CanResize, _iface); } - + public bool CanRotate { - get - { - return (bool) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetCanRotate ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.CanRotate, _iface); } - + #endregion Interface ITransformProvider @@ -102,7 +93,7 @@ public bool CanRotate // Internal Methods // //------------------------------------------------------ - + #region Internal Methods internal static object Wrap( AutomationPeer peer, object iface ) @@ -139,21 +130,6 @@ private object Rotate( object arg ) _iface.Rotate( (double)arg ); return null; } - - private object GetCanMove( object unused ) - { - return _iface.CanMove; - } - - private object GetCanResize( object unused ) - { - return _iface.CanResize; - } - - private object GetCanRotate( object unused ) - { - return _iface.CanRotate; - } #endregion Private Methods From 9b634d99fbd8d39f5da9372ceb640f345ae643e5 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 04:11:19 +0200 Subject: [PATCH 34/54] Finish rest of the wrapper-returns --- .../Automation/MultipleViewProviderWrapper.cs | 30 +++---------- .../Automation/TextProviderWrapper.cs | 42 ++++--------------- .../Automation/TextRangeProviderWrapper.cs | 7 +--- .../Automation/ToggleProviderWrapper.cs | 14 ++----- 4 files changed, 18 insertions(+), 75 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs index 3507cbba7ff..6ed3674fcbe 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -54,9 +54,9 @@ private MultipleViewProviderWrapper( AutomationPeer peer, IMultipleViewProvider #region Interface IMultipleViewProvider - public string GetViewName( int viewID ) + public string GetViewName(int viewID) { - return (string) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetViewName ), viewID ); + return ElementUtil.Invoke(_peer, static (state, viewID) => state.GetViewName(viewID), _iface, viewID); } public void SetCurrentView( int viewID ) @@ -66,15 +66,12 @@ public void SetCurrentView( int viewID ) public int CurrentView { - get - { - return (int) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetCurrentView ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.CurrentView, _iface); } - public int [] GetSupportedViews() + public int[] GetSupportedViews() { - return (int []) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetSupportedViews ), null ); + return ElementUtil.Invoke(_peer, static (state) => state.GetSupportedViews(), _iface); } #endregion Interface IMultipleViewProvider @@ -103,25 +100,10 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Methods - private object GetViewName( object arg ) - { - return _iface.GetViewName( (int) arg ); - } - private object SetCurrentView( object arg ) { _iface.SetCurrentView( (int) arg ); return null; - } - - private object GetCurrentView( object unused ) - { - return _iface.CurrentView; - } - - private object GetSupportedViews( object unused ) - { - return _iface.GetSupportedViews(); } #endregion Private Methods diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs index f8c60784e16..4c0e3ad4472 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // Description: Text pattern provider wrapper for WCP @@ -39,14 +39,14 @@ private TextProviderWrapper( AutomationPeer peer, ITextProvider iface ) #region Interface ITextProvider - public ITextRangeProvider [] GetSelection() + public ITextRangeProvider[] GetSelection() { - return (ITextRangeProvider [])ElementUtil.Invoke(_peer, new DispatcherOperationCallback(GetSelection), null); + return ElementUtil.Invoke(_peer, static (state, peer) => TextRangeProviderWrapper.WrapArgument(state.GetSelection(), peer), _iface, _peer); } - public ITextRangeProvider [] GetVisibleRanges() + public ITextRangeProvider[] GetVisibleRanges() { - return (ITextRangeProvider[])ElementUtil.Invoke(_peer, new DispatcherOperationCallback(GetVisibleRanges), null); + return ElementUtil.Invoke(_peer, static (state, peer) => TextRangeProviderWrapper.WrapArgument(state.GetVisibleRanges(), peer), _iface, _peer); } public ITextRangeProvider RangeFromChild(IRawElementProviderSimple childElement) @@ -64,20 +64,14 @@ public ITextRangeProvider RangeFromPoint(Point screenLocation) return (ITextRangeProvider)ElementUtil.Invoke(_peer, new DispatcherOperationCallback(RangeFromPoint), screenLocation); } - public ITextRangeProvider DocumentRange + public ITextRangeProvider DocumentRange { - get - { - return (ITextRangeProvider)ElementUtil.Invoke(_peer, new DispatcherOperationCallback(GetDocumentRange), null); - } + get => ElementUtil.Invoke(_peer, static (state, peer) => TextRangeProviderWrapper.WrapArgument(state.DocumentRange, peer), _iface, _peer); } public SupportedTextSelection SupportedTextSelection { - get - { - return (SupportedTextSelection)ElementUtil.Invoke(_peer, new DispatcherOperationCallback(GetSupportedTextSelection), null); - } + get => ElementUtil.Invoke(_peer, static (state) => state.SupportedTextSelection, _iface); } #endregion Interface ITextProvider @@ -106,16 +100,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Methods - private object GetSelection(object unused) - { - return TextRangeProviderWrapper.WrapArgument( _iface.GetSelection(), _peer ); - } - - private object GetVisibleRanges(object unused) - { - return TextRangeProviderWrapper.WrapArgument( _iface.GetVisibleRanges(), _peer ); - } - private object RangeFromChild(object arg) { IRawElementProviderSimple childElement = (IRawElementProviderSimple)arg; @@ -128,16 +112,6 @@ private object RangeFromPoint(object arg) return TextRangeProviderWrapper.WrapArgument( _iface.RangeFromPoint(screenLocation), _peer ); } - private object GetDocumentRange(object unused) - { - return TextRangeProviderWrapper.WrapArgument( _iface.DocumentRange, _peer ); - } - - private object GetSupportedTextSelection(object unused) - { - return _iface.SupportedTextSelection; - } - #endregion Private Methods //------------------------------------------------------ diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs index bcf0443d04f..c2ce8aaa2f3 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs @@ -40,7 +40,7 @@ internal TextRangeProviderWrapper( AutomationPeer peer, ITextRangeProvider iface public ITextRangeProvider Clone() { - return (ITextRangeProvider)ElementUtil.Invoke(_peer, new DispatcherOperationCallback(Clone), null); + return ElementUtil.Invoke(_peer, static (state, peer) => WrapArgument(state.Clone(), peer), _iface, _peer); } public bool Compare(ITextRangeProvider range) @@ -211,11 +211,6 @@ internal static ITextRangeProvider UnwrapArgument(ITextRangeProvider argument) #region Private Methods - private object Clone(object unused) - { - return TextRangeProviderWrapper.WrapArgument( _iface.Clone(), _peer ); - } - private object CompareEndpoints(object arg) { object[] args = (object[])arg; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs index 60162ee3a05..bba863b5889 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -62,10 +62,7 @@ public void Toggle( ) public ToggleState ToggleState { - get - { - return (ToggleState) ElementUtil.Invoke( _peer, new DispatcherOperationCallback( GetToggleState ), null ); - } + get => ElementUtil.Invoke(_peer, static (state) => state.ToggleState, _iface); } #endregion Interface IToggleProvider @@ -76,7 +73,7 @@ public ToggleState ToggleState // Internal Methods // //------------------------------------------------------ - + #region Internal Methods internal static object Wrap( AutomationPeer peer, object iface ) @@ -100,11 +97,6 @@ private object ToggleInternal( object unused ) return null; } - private object GetToggleState( object unused ) - { - return _iface.ToggleState; - } - #endregion Private Methods From 1c27ef6f8db31da562231656df9feaafeee10958 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 14:20:01 +0200 Subject: [PATCH 35/54] Remove legacy Invoke interface in favour of new methods, add void invocations --- .../Automation/DockProviderWrapper.cs | 19 +- .../MS/internal/Automation/ElementProxy.cs | 5 +- .../Automation/ElementUtil.ActionInfo.cs | 59 +++++ .../Automation/ElementUtil.ReturnInfo.cs | 6 +- .../MS/internal/Automation/ElementUtil.cs | 128 ++++++----- .../ExpandCollapseProviderWrapper.cs | 26 +-- .../Automation/InvokeProviderWrapper.cs | 21 +- .../ItemContainerProviderWrapper.cs | 38 ++-- .../Automation/MultipleViewProviderWrapper.cs | 6 +- .../Automation/RangeValueProviderWrapper.cs | 21 +- .../Automation/ScrollItemProviderWrapper.cs | 21 +- .../Automation/ScrollProviderWrapper.cs | 35 +-- .../SelectionItemProviderWrapper.cs | 37 +--- .../SynchronizedInputProviderWrapper.cs | 32 +-- .../Automation/TextProviderWrapper.cs | 40 ++-- .../Automation/TextRangeProviderWrapper.cs | 202 +++++++----------- .../Automation/ToggleProviderWrapper.cs | 21 +- .../Automation/TransformProviderWrapper.cs | 43 +--- .../Automation/ValueProviderWrapper.cs | 21 +- .../VirtualizedItemProviderWrapper.cs | 22 +- .../Automation/WindowProviderWrapper.cs | 29 +-- .../PresentationCore/PresentationCore.csproj | 1 + 22 files changed, 291 insertions(+), 542 deletions(-) create mode 100644 src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ActionInfo.cs diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs index d31dfaea77c..f175254ec6f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs @@ -57,7 +57,7 @@ private DockProviderWrapper( AutomationPeer peer, IDockProvider iface ) public void SetDockPosition(DockPosition dockPosition) { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( SetDockPosition ), dockPosition ); + ElementUtil.Invoke(_peer, static (state, dockPosition) => state.SetDockPosition(dockPosition), _iface, dockPosition); } public DockPosition DockPosition @@ -83,23 +83,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object SetDockPosition( object arg ) - { - _iface.SetDockPosition( (DockPosition) arg ); - return null; - } - - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs index bcce505a271..63a99efae45 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs @@ -145,7 +145,7 @@ public void SetFocus() { AutomationPeer peer = Peer ?? throw new ElementNotAvailableException(); - ElementUtil.Invoke(peer, state => ((ElementProxy)state).InContextSetFocus(), this); + ElementUtil.Invoke(peer, state => state.InContextSetFocus(), this); } public IRawElementProviderFragmentRoot FragmentRoot @@ -409,12 +409,11 @@ private Rect InContextBoundingRectangle() } // Set focus to this element... - private object InContextSetFocus() + private void InContextSetFocus() { AutomationPeer peer = Peer ?? throw new ElementNotAvailableException(); peer.SetFocus(); - return null; } // Return proxy representing the root of this WCP tree... diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ActionInfo.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ActionInfo.cs new file mode 100644 index 00000000000..dcbd004cb36 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ActionInfo.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace MS.Internal.Automation; + +/// +/// Utility class for working with . +/// +internal static partial class ElementUtil +{ + /// + /// Wraps the return value and exception of an asynchronous operation. + /// + /// + [StructLayout(LayoutKind.Auto)] + private readonly struct ActionInfo + { + /// + /// The exception that was thrown during the operation, if any. + /// + public Exception? StoredException { get; init; } + + /// + /// Gets a value indicating whether the operation has been completed or timed out. + /// + public bool HasCompleted { get; init; } + + /// + /// Creates a new instance of with the specified exception. + /// + /// The exception that was thrown during the operation. + /// Returns a object. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ActionInfo FromException(Exception exception) + { + return new ActionInfo + { + StoredException = exception, + HasCompleted = true + }; + } + + /// + /// Creates a new instance of with the specified value. + /// + /// The return value of the operation. + /// Returns a object. + public static ActionInfo Completed { get; } = new ActionInfo + { + StoredException = null, + HasCompleted = true + }; + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ReturnInfo.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ReturnInfo.cs index d36b55f80df..2446154095b 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ReturnInfo.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ReturnInfo.cs @@ -28,7 +28,7 @@ private readonly struct ReturnInfo /// /// Gets a value indicating whether the operation has been completed or timed out. /// - public bool Completed { get; init; } + public bool HasCompleted { get; init; } /// /// The return value of the operation, if it completed successfully. @@ -46,7 +46,7 @@ public static ReturnInfo FromException(Exception exception) return new ReturnInfo { StoredException = exception, - Completed = true, + HasCompleted = true, Value = default! }; } @@ -61,7 +61,7 @@ public static ReturnInfo FromResult(TReturn value) return new ReturnInfo { StoredException = null, - Completed = true, + HasCompleted = true, Value = value }; } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs index 756753854fa..d3111f958b2 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs @@ -16,6 +16,79 @@ namespace MS.Internal.Automation; /// internal static partial class ElementUtil { + /// + /// Provides a helper to invoke work on the UI thread, re-throwing all exceptions on the thread that invoked this execution. + /// + internal static void Invoke(AutomationPeer peer, Action work, TArg arg) + { + // Null dispatcher likely means the visual is in bad shape + Dispatcher dispatcher = peer.Dispatcher ?? throw new ElementNotAvailableException(); + + ActionInfo retVal = dispatcher.Invoke(ExceptionWrapper, DispatcherPriority.Send, TimeSpan.FromMinutes(3), work, arg); + + static ActionInfo ExceptionWrapper(Action func, TArg arg) + { + try + { + func(arg); + return ActionInfo.Completed; + } + catch (Exception e) + { + return ActionInfo.FromException(e); + } + } + + // Either throws an exception if the operation did not complete successfully, or does nothing. + HandleActionInfo(dispatcher, retVal); + } + + /// + /// Provides a helper to invoke work on the UI thread, re-throwing all exceptions on the thread that invoked this execution. + /// + internal static void Invoke(AutomationPeer peer, Action work, TArg1 arg1, TArg2 arg2) + { + // Null dispatcher likely means the visual is in bad shape + Dispatcher dispatcher = peer.Dispatcher ?? throw new ElementNotAvailableException(); + + ActionInfo retVal = dispatcher.Invoke(ExceptionWrapper, DispatcherPriority.Send, TimeSpan.FromMinutes(3), work, arg1, arg2); + + static ActionInfo ExceptionWrapper(Action func, TArg1 arg1, TArg2 arg2) + { + try + { + func(arg1, arg2); + return ActionInfo.Completed; + } + catch (Exception e) + { + return ActionInfo.FromException(e); + } + } + + // Either throws an exception if the operation did not complete successfully, or does nothing. + HandleActionInfo(dispatcher, retVal); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void HandleActionInfo(Dispatcher dispatcher, ActionInfo retVal) + { + if (!retVal.HasCompleted) + { + if (dispatcher.HasShutdownStarted) + { + ThrowInvalidOperationException(); + } + else + { + ThrowTimeoutException(); + } + } + + if (retVal.StoredException is not null) + UnwrapException(retVal.StoredException); + } + /// /// Provides a helper to invoke work on the UI thread, re-throwing all exceptions on the thread that invoked this execution. /// @@ -71,7 +144,7 @@ static ReturnInfo ExceptionWrapper(Func func, TA [MethodImpl(MethodImplOptions.AggressiveInlining)] private static TReturn HandleReturnValue(Dispatcher dispatcher, ref readonly ReturnInfo retVal) { - if (!retVal.Completed) + if (!retVal.HasCompleted) { if (dispatcher.HasShutdownStarted) { @@ -110,57 +183,4 @@ private static TReturn HandleReturnValue(Dispatcher dispatcher, ref rea [MethodImpl(MethodImplOptions.NoInlining)] private static void UnwrapException(Exception exception) => throw exception; - /// - /// Provides a helper to invoke work on the UI thread, re-throwing all exceptions on the thread that invoked this execution. - /// - [OverloadResolutionPriority(1)] // We raise the priority here to avoid generic explosion when it's not needed - internal static object Invoke(AutomationPeer peer, DispatcherOperationCallback work, object arg) - { - // Null dispatcher likely means the visual is in bad shape - Dispatcher dispatcher = peer.Dispatcher ?? throw new ElementNotAvailableException(); - - Exception? remoteException = null; - bool completed = false; - - object retVal = dispatcher.Invoke(DispatcherPriority.Send, TimeSpan.FromMinutes(3), (DispatcherOperationCallback)delegate (object workArg) - { - try - { - return work(workArg); - } - catch (Exception e) - { - remoteException = e; - return null; - } - finally - { - completed = true; - } - }, - arg); - - if (completed) - { - if (remoteException is not null) - { - throw remoteException; - } - } - else - { - bool dispatcherInShutdown = dispatcher.HasShutdownStarted; - - if (dispatcherInShutdown) - { - throw new InvalidOperationException(SR.AutomationDispatcherShutdown); - } - else - { - throw new TimeoutException(SR.AutomationTimeout); - } - } - - return retVal; - } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs index 2a104f441d2..52631201204 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs @@ -57,12 +57,12 @@ private ExpandCollapseProviderWrapper( AutomationPeer peer, IExpandCollapseProvi public void Expand() { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( Expand ), null ); + ElementUtil.Invoke(_peer, static (state) => state.Expand(), _iface); } public void Collapse() { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( Collapse ), null ); + ElementUtil.Invoke(_peer, static (state) => state.Collapse(), _iface); } public ExpandCollapseState ExpandCollapseState @@ -88,28 +88,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object Expand( object unused ) - { - _iface.Expand(); - return null; - } - - private object Collapse( object unused ) - { - _iface.Collapse(); - return null; - } - - #endregion Private Methods - //------------------------------------------------------ // diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs index a16b8d814b6..c9d4e4c5253 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -56,7 +56,7 @@ private InvokeProviderWrapper( AutomationPeer peer, IInvokeProvider iface ) public void Invoke() { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( Invoke ), null ); + ElementUtil.Invoke(_peer, static (state) => state.Invoke(), _iface); } #endregion Interface IInvokeProvider @@ -77,23 +77,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object Invoke( object unused ) - { - _iface.Invoke(); - return null; - } - - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs index 85e9bb26b37..cbaa6a7758d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -56,8 +56,19 @@ private ItemContainerProviderWrapper(AutomationPeer peer, IItemContainerProvider public IRawElementProviderSimple FindItemByProperty(IRawElementProviderSimple startAfter, int propertyId, object value) { - object [] args = new object[]{startAfter, propertyId, value}; - return (IRawElementProviderSimple)ElementUtil.Invoke(_peer, new DispatcherOperationCallback(FindItemByProperty), args); + object[] args = [startAfter, propertyId, value]; + + // The actual invocation method that gets called on the peer's context. + static IRawElementProviderSimple FindItemByProperty(IItemContainerProvider state, object[] args) + { + IRawElementProviderSimple startAfter = (IRawElementProviderSimple)args[0]; + int propertyId = (int)args[1]; + object value = args[2]; + + return state.FindItemByProperty(startAfter, propertyId, value); + } + + return ElementUtil.Invoke(_peer, FindItemByProperty, _iface, args); } #endregion Interface IItemContainerProvider @@ -78,27 +89,6 @@ internal static object Wrap(AutomationPeer peer, object iface) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object FindItemByProperty(object arg) - { - object[] args = (object[])arg; - IRawElementProviderSimple startAfter = (IRawElementProviderSimple)args[0]; - int propertyId = (int)args[1]; - object value = (object)args[2]; - - return _iface.FindItemByProperty(startAfter, propertyId, value); - } - - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs index 6ed3674fcbe..7071b22165f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs @@ -59,10 +59,10 @@ public string GetViewName(int viewID) return ElementUtil.Invoke(_peer, static (state, viewID) => state.GetViewName(viewID), _iface, viewID); } - public void SetCurrentView( int viewID ) + public void SetCurrentView(int viewID) { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( SetCurrentView ), viewID ); - } + ElementUtil.Invoke(_peer, static (state, viewID) => state.SetCurrentView(viewID), _iface, viewID); + } public int CurrentView { diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs index 8e08454062b..e9c0f684b85 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs @@ -54,9 +54,9 @@ private RangeValueProviderWrapper( AutomationPeer peer, IRangeValueProvider ifac #region Interface IRangeValueProvider - public void SetValue( double val ) + public void SetValue(double val) { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( SetValueInternal ), val ); + ElementUtil.Invoke(_peer, static (state, val) => state.SetValue(val), _iface, val); } public double Value @@ -107,23 +107,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object SetValueInternal( object arg ) - { - _iface.SetValue( (double)arg ); - return null; - } - - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs index a1c13459f0d..d433ec0d8ae 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -56,7 +56,7 @@ private ScrollItemProviderWrapper(AutomationPeer peer, IScrollItemProvider iface public void ScrollIntoView() { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( ScrollIntoView ), null ); + ElementUtil.Invoke(_peer, static (state) => state.ScrollIntoView(), _iface); } #endregion Interface IScrollItemProvider @@ -77,23 +77,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object ScrollIntoView(object unused) - { - _iface.ScrollIntoView(); - return null; - } - - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs index e2e411faf98..aafafb4a77d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs @@ -56,16 +56,16 @@ private ScrollProviderWrapper( AutomationPeer peer, IScrollProvider iface ) #region Interface IScrollProvider - public void Scroll( ScrollAmount horizontalAmount, ScrollAmount verticalAmount ) + public void Scroll(ScrollAmount horizontalAmount, ScrollAmount verticalAmount) { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( Scroll ), new ScrollAmount [ ] { horizontalAmount, verticalAmount } ); + ElementUtil.Invoke(_peer, static (state, values) => state.Scroll(values[0], values[1]), _iface, new ScrollAmount[] { horizontalAmount, verticalAmount }); } - public void SetScrollPercent( double horizontalPercent, double verticalPercent ) + public void SetScrollPercent(double horizontalPercent, double verticalPercent) { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( SetScrollPercent ), new double [ ] { horizontalPercent, verticalPercent } ); + ElementUtil.Invoke(_peer, static (state, values) => state.SetScrollPercent(values[0], values[1]), _iface, new double[] { horizontalPercent, verticalPercent }); } - + public double HorizontalScrollPercent { get => ElementUtil.Invoke(_peer, static (state) => state.HorizontalScrollPercent, _iface); @@ -114,31 +114,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object Scroll( object arg ) - { - ScrollAmount [ ] args = (ScrollAmount [ ]) arg; - _iface.Scroll( args[ 0 ], args[ 1 ] ); - return null; - } - - private object SetScrollPercent( object arg ) - { - double [ ] args = (double [ ]) arg; - _iface.SetScrollPercent( args[ 0 ], args[ 1 ] ); - return null; - } - - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs index 8ce83b916e9..2fab991778b 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs @@ -57,17 +57,17 @@ private SelectionItemProviderWrapper( AutomationPeer peer, ISelectionItemProvide public void Select() { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( Select ), null ); + ElementUtil.Invoke(_peer, static (state) => state.Select(), _iface); } public void AddToSelection() { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( AddToSelection ), null ); + ElementUtil.Invoke(_peer, static (state) => state.AddToSelection(), _iface); } - + public void RemoveFromSelection() { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( RemoveFromSelection ), null ); + ElementUtil.Invoke(_peer, static (state) => state.RemoveFromSelection(), _iface); } public bool IsSelected @@ -98,35 +98,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object Select( object unused ) - { - _iface.Select(); - return null; - } - - private object AddToSelection( object unused ) - { - _iface.AddToSelection(); - return null; - } - - private object RemoveFromSelection( object unused ) - { - _iface.RemoveFromSelection(); - return null; - } - - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs index dbcb5d145ae..7d5a24ba0f0 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -57,13 +57,14 @@ private SynchronizedInputProviderWrapper( AutomationPeer peer, ISynchronizedInpu public void StartListening(SynchronizedInputType inputType) { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( StartListening ), inputType ); -} + ElementUtil.Invoke(_peer, static (state, inputType) => state.StartListening(inputType), _iface, inputType); + } public void Cancel() { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( Cancel ), null ); -} + ElementUtil.Invoke(_peer, static (state) => state.Cancel(), _iface); + } + #endregion Interface ISynchronizedInputProvider @@ -82,27 +83,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object StartListening( object arg ) - { - _iface.StartListening((SynchronizedInputType)arg); - return null; -} - private object Cancel( object unused ) - { - _iface.Cancel(); - return null; -} - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs index 4c0e3ad4472..1015a713754 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs @@ -51,17 +51,27 @@ public ITextRangeProvider[] GetVisibleRanges() public ITextRangeProvider RangeFromChild(IRawElementProviderSimple childElement) { - if (!(childElement is ElementProxy)) - { + if (childElement is not ElementProxy) throw new ArgumentException(SR.Format(SR.TextProvider_InvalidChild, "childElement")); + + // The actual invocation method that gets called on the peer's context. + static ITextRangeProvider RangeFromChild(TextProviderWrapper state, IRawElementProviderSimple childElement) + { + return TextRangeProviderWrapper.WrapArgument(state._iface.RangeFromChild(childElement), state._peer); } - return (ITextRangeProvider)ElementUtil.Invoke(_peer, new DispatcherOperationCallback(RangeFromChild), childElement); + return ElementUtil.Invoke(_peer, RangeFromChild, this, childElement); } public ITextRangeProvider RangeFromPoint(Point screenLocation) { - return (ITextRangeProvider)ElementUtil.Invoke(_peer, new DispatcherOperationCallback(RangeFromPoint), screenLocation); + // The actual invocation method that gets called on the peer's context. + static ITextRangeProvider RangeFromPoint(TextProviderWrapper state, Point screenLocation) + { + return TextRangeProviderWrapper.WrapArgument(state._iface.RangeFromPoint(screenLocation), state._peer); + } + + return ElementUtil.Invoke(_peer, RangeFromPoint, this, screenLocation); } public ITextRangeProvider DocumentRange @@ -92,28 +102,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object RangeFromChild(object arg) - { - IRawElementProviderSimple childElement = (IRawElementProviderSimple)arg; - return TextRangeProviderWrapper.WrapArgument( _iface.RangeFromChild(childElement), _peer ); - } - - private object RangeFromPoint(object arg) - { - Point screenLocation = (Point)arg; - return TextRangeProviderWrapper.WrapArgument( _iface.RangeFromPoint(screenLocation), _peer ); - } - - #endregion Private Methods - //------------------------------------------------------ // // Private Fields diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs index c2ce8aaa2f3..0a8424ab8d2 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs @@ -54,31 +54,61 @@ public bool Compare(ITextRangeProvider range) public int CompareEndpoints(TextPatternRangeEndpoint endpoint, ITextRangeProvider targetRange, TextPatternRangeEndpoint targetEndpoint) { - if (!(targetRange is TextRangeProviderWrapper)) + if (targetRange is not TextRangeProviderWrapper) + throw new ArgumentException(SR.Format(SR.TextRangeProvider_InvalidRangeProvider, nameof(targetRange))); + + object[] args = [endpoint, targetRange, targetEndpoint]; + + // The actual invocation method that gets called on the peer's context. + static int CompareEndpoints(ITextRangeProvider state, object[] args) { - throw new ArgumentException(SR.Format(SR.TextRangeProvider_InvalidRangeProvider, "targetRange")); + TextPatternRangeEndpoint endpoint = (TextPatternRangeEndpoint)args[0]; + ITextRangeProvider targetRange = (ITextRangeProvider)args[1]; + TextPatternRangeEndpoint targetEndpoint = (TextPatternRangeEndpoint)args[2]; + + return state.CompareEndpoints(endpoint, UnwrapArgument(targetRange), targetEndpoint); } - object[] args = new object[] { endpoint, targetRange, targetEndpoint }; - return (int)ElementUtil.Invoke(_peer, new DispatcherOperationCallback(CompareEndpoints), args); + return ElementUtil.Invoke(_peer, CompareEndpoints, _iface, args); } public void ExpandToEnclosingUnit(TextUnit unit) { - object[] args = new object[] { unit }; - ElementUtil.Invoke(_peer, new DispatcherOperationCallback(ExpandToEnclosingUnit), args); + ElementUtil.Invoke(_peer, static (state, unit) => state.ExpandToEnclosingUnit(unit), _iface, unit); } public ITextRangeProvider FindAttribute(int attribute, object val, bool backward) { - object[] args = new object[] { attribute, val, backward }; - return (ITextRangeProvider)ElementUtil.Invoke(_peer, new DispatcherOperationCallback(FindAttribute), args); + object[] args = [attribute, val, backward]; + + // The actual invocation method that gets called on the peer's context. + static ITextRangeProvider FindAttribute(TextRangeProviderWrapper state, object[] args) + { + int attribute = (int)args[0]; + object val = args[1]; + bool backward = (bool)args[2]; + + return WrapArgument(state._iface.FindAttribute(attribute, val, backward), state._peer); + } + + return ElementUtil.Invoke(_peer, FindAttribute, this, args); } public ITextRangeProvider FindText(string text, bool backward, bool ignoreCase) { - object[] args = new object[] { text, backward, ignoreCase }; - return (ITextRangeProvider)ElementUtil.Invoke(_peer, new DispatcherOperationCallback(FindText), args); + object[] args = [text, backward, ignoreCase]; + + // The actual invocation method that gets called on the peer's context. + static ITextRangeProvider FindText(TextRangeProviderWrapper state, object[] args) + { + string text = (string)args[0]; + bool backward = (bool)args[1]; + bool ignoreCase = (bool)args[2]; + + return WrapArgument(state._iface.FindText(text, backward, ignoreCase), state._peer); + } + + return ElementUtil.Invoke(_peer, FindText, this, args); } public object GetAttributeValue(int attribute) @@ -104,45 +134,75 @@ public string GetText(int maxLength) public int Move(TextUnit unit, int count) { - object[] args = new object[] { unit, count }; - return (int)ElementUtil.Invoke(_peer, new DispatcherOperationCallback(Move), args); + object[] args = [unit, count]; + + // The actual invocation method that gets called on the peer's context. + static int Move(ITextRangeProvider state, object[] args) + { + TextUnit unit = (TextUnit)args[0]; + int count = (int)args[1]; + + return state.Move(unit, count); + } + + return ElementUtil.Invoke(_peer, Move, _iface, args); } public int MoveEndpointByUnit(TextPatternRangeEndpoint endpoint, TextUnit unit, int count) { - object[] args = new object[] { endpoint, unit, count }; - return (int)ElementUtil.Invoke(_peer, new DispatcherOperationCallback(MoveEndpointByUnit), args); + object[] args = [endpoint, unit, count]; + + // The actual invocation method that gets called on the peer's context. + static int MoveEndpointByUnit(ITextRangeProvider state, object[] args) + { + TextPatternRangeEndpoint endpoint = (TextPatternRangeEndpoint)args[0]; + TextUnit unit = (TextUnit)args[1]; + int count = (int)args[2]; + + return state.MoveEndpointByUnit(endpoint, unit, count); + } + + return ElementUtil.Invoke(_peer, MoveEndpointByUnit, _iface, args); } public void MoveEndpointByRange(TextPatternRangeEndpoint endpoint, ITextRangeProvider targetRange, TextPatternRangeEndpoint targetEndpoint) { - if (!(targetRange is TextRangeProviderWrapper)) + if (targetRange is not TextRangeProviderWrapper) + throw new ArgumentException(SR.Format(SR.TextRangeProvider_InvalidRangeProvider, nameof(targetRange))); + + object[] args = [endpoint, targetRange, targetEndpoint]; + + // The actual invocation method that gets called on the peer's context. + static void MoveEndpointByRange(ITextRangeProvider state, object[] args) { - throw new ArgumentException(SR.Format(SR.TextRangeProvider_InvalidRangeProvider, "targetRange")); + TextPatternRangeEndpoint endpoint = (TextPatternRangeEndpoint)args[0]; + ITextRangeProvider targetRange = (ITextRangeProvider)args[1]; + TextPatternRangeEndpoint targetEndpoint = (TextPatternRangeEndpoint)args[2]; + + state.MoveEndpointByRange(endpoint, UnwrapArgument(targetRange), targetEndpoint); } - object[] args = new object[] { endpoint, targetRange, targetEndpoint }; - ElementUtil.Invoke(_peer, new DispatcherOperationCallback(MoveEndpointByRange), args); + ElementUtil.Invoke(_peer, MoveEndpointByRange, _iface, args); } public void Select() { - ElementUtil.Invoke(_peer, new DispatcherOperationCallback(Select), null); + ElementUtil.Invoke(_peer, static (state) => state.Select(), _iface); } public void AddToSelection() { - ElementUtil.Invoke(_peer, new DispatcherOperationCallback(AddToSelection), null); + ElementUtil.Invoke(_peer, static (state) => state.AddToSelection(), _iface); } public void RemoveFromSelection() { - ElementUtil.Invoke(_peer, new DispatcherOperationCallback(RemoveFromSelection), null); + ElementUtil.Invoke(_peer, static (state) => state.RemoveFromSelection(), _iface); } public void ScrollIntoView(bool alignToTop) { - ElementUtil.Invoke(_peer, new DispatcherOperationCallback(ScrollIntoView), alignToTop); + ElementUtil.Invoke(_peer, static (state, alignToTop) => state.ScrollIntoView(alignToTop), _iface, alignToTop); } public IRawElementProviderSimple[] GetChildren() @@ -203,104 +263,6 @@ internal static ITextRangeProvider UnwrapArgument(ITextRangeProvider argument) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object CompareEndpoints(object arg) - { - object[] args = (object[])arg; - TextPatternRangeEndpoint endpoint = (TextPatternRangeEndpoint)args[0]; - ITextRangeProvider targetRange = (ITextRangeProvider)args[1]; - TextPatternRangeEndpoint targetEndpoint = (TextPatternRangeEndpoint)args[2]; - return _iface.CompareEndpoints(endpoint, TextRangeProviderWrapper.UnwrapArgument( targetRange ), targetEndpoint); - } - - private object ExpandToEnclosingUnit(object arg) - { - object[] args = (object[])arg; - TextUnit unit = (TextUnit)args[0]; - _iface.ExpandToEnclosingUnit(unit); - return null; - } - - private object FindAttribute(object arg) - { - object[] args = (object[])arg; - int attribute = (int)args[0]; - object val = args[1]; - bool backward = (bool)args[2]; - return TextRangeProviderWrapper.WrapArgument( _iface.FindAttribute(attribute, val, backward), _peer ); - } - - private object FindText(object arg) - { - object[] args = (object[])arg; - string text = (string)args[0]; - bool backward = (bool)args[1]; - bool ignoreCase = (bool)args[2]; - return TextRangeProviderWrapper.WrapArgument( _iface.FindText(text, backward, ignoreCase), _peer ); - } - - private object Move(object arg) - { - object[] args = (object[])arg; - TextUnit unit = (TextUnit)args[0]; - int count = (int)args[1]; - return _iface.Move(unit, count); - } - - private object MoveEndpointByUnit(object arg) - { - object[] args = (object[])arg; - TextPatternRangeEndpoint endpoint = (TextPatternRangeEndpoint)args[0]; - TextUnit unit = (TextUnit)args[1]; - int count = (int)args[2]; - return _iface.MoveEndpointByUnit(endpoint, unit, count); - } - - private object MoveEndpointByRange(object arg) - { - object[] args = (object[])arg; - TextPatternRangeEndpoint endpoint = (TextPatternRangeEndpoint)args[0]; - ITextRangeProvider targetRange = (ITextRangeProvider)args[1]; - TextPatternRangeEndpoint targetEndpoint = (TextPatternRangeEndpoint)args[2]; - _iface.MoveEndpointByRange(endpoint, TextRangeProviderWrapper.UnwrapArgument( targetRange ), targetEndpoint); - return null; - } - - private object Select(object unused) - { - _iface.Select(); - return null; - } - - private object AddToSelection(object unused) - { - _iface.AddToSelection(); - return null; - } - - private object RemoveFromSelection(object unused) - { - _iface.RemoveFromSelection(); - return null; - } - - private object ScrollIntoView(object arg) - { - bool alignTop = (bool)arg; - _iface.ScrollIntoView(alignTop); - return null; - } - - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs index bba863b5889..8645edcb188 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs @@ -55,9 +55,9 @@ private ToggleProviderWrapper( AutomationPeer peer, IToggleProvider iface ) #region Interface IToggleProvider - public void Toggle( ) + public void Toggle() { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( ToggleInternal ), null ); + ElementUtil.Invoke(_peer, static (state) => state.Toggle(), _iface); } public ToggleState ToggleState @@ -83,23 +83,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object ToggleInternal( object unused ) - { - _iface.Toggle(); - return null; - } - - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs index 927d875103a..bdbed94c750 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs @@ -55,19 +55,19 @@ private TransformProviderWrapper( AutomationPeer peer, ITransformProvider iface #region Interface ITransformProvider - public void Move( double x, double y ) + public void Move(double x, double y) { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( Move ), new double [ ] { x, y } ); + ElementUtil.Invoke(_peer, static (state, coordinates) => state.Move(coordinates[0], coordinates[1]), _iface, new double[] { x, y }); } - public void Resize( double width, double height ) + public void Resize(double width, double height) { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( Resize ), new double [ ] { width, height } ); + ElementUtil.Invoke(_peer, static (state, dimensions) => state.Resize(dimensions[0], dimensions[1]), _iface, new double[] { width, height }); } - public void Rotate( double degrees ) + public void Rotate(double degrees) { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( Rotate ), degrees ); + ElementUtil.Invoke(_peer, static (state, degrees) => state.Rotate(degrees), _iface, degrees); } public bool CanMove @@ -103,37 +103,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object Move( object arg ) - { - double [ ] args = (double [ ]) arg; - _iface.Move( args[ 0 ], args[ 1 ] ); - return null; - } - - private object Resize( object arg ) - { - double [ ] args = (double [ ]) arg; - _iface.Resize( args[ 0 ], args[ 1 ] ); - return null; - } - - private object Rotate( object arg ) - { - _iface.Rotate( (double)arg ); - return null; - } - - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs index 1a3152e048b..2a911aea9cf 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs @@ -54,9 +54,9 @@ private ValueProviderWrapper( AutomationPeer peer, IValueProvider iface ) #region Interface IValueProvider - public void SetValue( string val ) + public void SetValue(string val) { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( SetValueInternal ), val ); + ElementUtil.Invoke(_peer, static (state, value) => state.SetValue(value), _iface, val); } public string Value @@ -87,23 +87,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object SetValueInternal( object arg ) - { - _iface.SetValue( (string)arg ); - return null; - } - - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs index a90545ff6eb..86b64f84750 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // @@ -56,7 +56,7 @@ private VirtualizedItemProviderWrapper(AutomationPeer peer, IVirtualizedItemProv public void Realize() { - ElementUtil.Invoke(_peer, new DispatcherOperationCallback(Realize), null); + ElementUtil.Invoke(_peer, static (state) => state.Realize(), _iface); } #endregion Interface IVirtualizedItemProvider @@ -77,24 +77,6 @@ internal static object Wrap(AutomationPeer peer, object iface) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object Realize(object unused) - { - _iface.Realize(); - return null; - } - - - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs index 7163cbdfcfd..80cff7b4957 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs @@ -55,14 +55,14 @@ private WindowProviderWrapper( AutomationPeer peer, IWindowProvider iface) #region Interface IWindowProvider - public void SetVisualState( WindowVisualState state ) + public void SetVisualState(WindowVisualState state) { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( SetVisualState ), state ); + ElementUtil.Invoke(_peer, static (state, visualState) => state.SetVisualState(visualState), _iface, state); } public void Close() { - ElementUtil.Invoke( _peer, new DispatcherOperationCallback( Close ), null ); + ElementUtil.Invoke(_peer, static (state) => state.Close(), _iface); } public bool WaitForInputIdle(int milliseconds) @@ -118,29 +118,6 @@ internal static object Wrap( AutomationPeer peer, object iface) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object SetVisualState( object arg ) - { - _iface.SetVisualState( (WindowVisualState) arg ); - return null; - } - - private object Close( object unused ) - { - _iface.Close(); - return null; - } - - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/PresentationCore.csproj b/src/Microsoft.DotNet.Wpf/src/PresentationCore/PresentationCore.csproj index a01827dcf03..9c93a7b2871 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/PresentationCore.csproj +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/PresentationCore.csproj @@ -24,6 +24,7 @@ + From 1e43057911f472a63b2a8b54177fb8ce12a50f85 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 14:23:49 +0200 Subject: [PATCH 36/54] Fix docs for ActionInfo --- .../MS/internal/Automation/ElementUtil.ActionInfo.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ActionInfo.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ActionInfo.cs index dcbd004cb36..dc5661c9069 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ActionInfo.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ActionInfo.cs @@ -14,9 +14,8 @@ namespace MS.Internal.Automation; internal static partial class ElementUtil { /// - /// Wraps the return value and exception of an asynchronous operation. + /// Wraps the exception from an asynchronous operation, tracking whether the operation has completed or timed out. /// - /// [StructLayout(LayoutKind.Auto)] private readonly struct ActionInfo { @@ -31,10 +30,10 @@ private readonly struct ActionInfo public bool HasCompleted { get; init; } /// - /// Creates a new instance of with the specified exception. + /// Creates a new instance of with the specified exception. /// /// The exception that was thrown during the operation. - /// Returns a object. + /// Returns a object. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ActionInfo FromException(Exception exception) { @@ -46,10 +45,9 @@ public static ActionInfo FromException(Exception exception) } /// - /// Creates a new instance of with the specified value. + /// Returns a singleton instance of signalizing successful completion of the operation. /// - /// The return value of the operation. - /// Returns a object. + /// Returns a object. public static ActionInfo Completed { get; } = new ActionInfo { StoredException = null, From 0b6446308245330bc9ca5f948d417000dc6f58da Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 16:40:51 +0200 Subject: [PATCH 37/54] Add more basic ElementUtil/Dispatcher consistency tests --- .../Internal/Automation/ElementUtil.Tests.cs | 112 ++++++++++++++++++ .../PresentationCore.Tests.csproj | 2 + 2 files changed, 114 insertions(+) create mode 100644 src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/ElementUtil.Tests.cs diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/ElementUtil.Tests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/ElementUtil.Tests.cs new file mode 100644 index 00000000000..caedff46e12 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/ElementUtil.Tests.cs @@ -0,0 +1,112 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Automation.Peers; +using System.Windows.Threading; + +namespace MS.Internal.Automation; + +public sealed class ElementUtilTests +{ + [WpfFact] + public void Invoke_TArg_Success() + { + UIElementAutomationPeer peer = new UIElementAutomationPeer(new UIElement()); + + ElementUtil.Invoke(peer, static (arg) => Assert.Equal("InvocationTest", arg), "InvocationTest"); + } + + [WpfFact(Skip = "This test is ignored because the default timeout is 3 minutes.")] + public void Invoke_TArg_CrossThreadInvocation_TimesOut() + { + UIElementAutomationPeer peer = new UIElementAutomationPeer(new UIElement()); + TimeoutException? expectedEx = null; + + Thread thread = new Thread(() => + { + // Wait for 5 seconds, set the timeout on ElementUtil sooner than that + expectedEx = Assert.Throws(() => ElementUtil.Invoke(peer, static (delay) => + { + while (true) + { + Thread.Sleep(delay); + } + }, 333)); + }); + + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + + // Pump the Dispatcher queue and block until it times out + thread.Join(); + + Assert.NotNull(expectedEx); + Assert.IsType(expectedEx); + } + + [WpfFact] + public async Task Invoke_TArg_CrossThreadInvocation_PropagatesException() + { + UIElementAutomationPeer peer = new UIElementAutomationPeer(new UIElement()); + InvalidOperationException? expectedEx = null; + + Thread thread = new Thread(() => + { + // Even when invoking on a different thread, the exception should propagate back to the calling thread + expectedEx = Assert.Throws(() => ElementUtil.Invoke(peer, static (text) => throw new InvalidOperationException(text), "Throw me")); + }); + + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + + // We cannot Join here as that would block the queued operation on the Dispatcher thread + await peer.Dispatcher.InvokeAsync(async () => + { + while (expectedEx is null) + { + // Wait until the exception is thrown in the other thread + await Task.Delay(100).ConfigureAwait(false); + } + + }, DispatcherPriority.Background); + + // Ensure the thread has completed execution + thread.Join(); + + Assert.NotNull(expectedEx); + Assert.IsType(expectedEx); + } + + [WpfFact] + public void Invoke_TArg1_TArg2_Success() + { + UIElementAutomationPeer peer = new UIElementAutomationPeer(new UIElement()); + + static void ActionWithTwoArgs(string arg1, string arg2) + { + Assert.Equal("InvocationTest", arg1); + Assert.Equal("AdditionalArgument", arg2); + } + + ElementUtil.Invoke(peer, ActionWithTwoArgs, "InvocationTest", "AdditionalArgument"); + } + + [WpfFact] + public void Invoke_TReturn_TArg_Success() + { + UIElementAutomationPeer peer = new UIElementAutomationPeer(new UIElement()); + + Assert.Equal(0xFF_FF, ElementUtil.Invoke(peer, static (arg) => 0xFF | arg, 0xFF_FF)); + } + + [WpfFact] + public void Invoke_TReturn_TArg1_TArg2_Success() + { + UIElementAutomationPeer peer = new UIElementAutomationPeer(new UIElement()); + + Assert.Equal(66 + 420, ElementUtil.Invoke(peer, static (arg1, arg2) => arg1 + arg2, 66, 420)); + } +} diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/PresentationCore.Tests.csproj b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/PresentationCore.Tests.csproj index 70dd2604032..7fb2e9b3d38 100644 --- a/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/PresentationCore.Tests.csproj +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/PresentationCore.Tests.csproj @@ -18,6 +18,8 @@ TargetFramework;TargetFrameworks + + From 029c0dc930e4651ad658c499ff78a51bcc68c981 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 18:17:41 +0200 Subject: [PATCH 38/54] Enable nullability, seal the wrappers, make fields readonly, cleanup usings --- .../Automation/DockProviderWrapper.cs | 14 ++++++--- .../ExpandCollapseProviderWrapper.cs | 14 ++++++--- .../Automation/GridItemProviderWrapper.cs | 4 ++- .../Automation/GridProviderWrapper.cs | 13 +++++--- .../Automation/InvokeProviderWrapper.cs | 14 ++++++--- .../ItemContainerProviderWrapper.cs | 12 ++++--- .../Automation/MultipleViewProviderWrapper.cs | 31 ++++++------------- .../Automation/RangeValueProviderWrapper.cs | 14 ++++++--- .../Automation/ScrollItemProviderWrapper.cs | 12 ++++--- .../Automation/ScrollProviderWrapper.cs | 14 ++++++--- .../SelectionItemProviderWrapper.cs | 14 ++++++--- .../Automation/SelectionProviderWrapper.cs | 14 ++++++--- .../SynchronizedInputProviderWrapper.cs | 14 ++++++--- .../Automation/TableItemProviderWrapper.cs | 14 ++++++--- .../Automation/TableProviderWrapper.cs | 14 ++++++--- .../Automation/TextProviderWrapper.cs | 14 ++++++--- .../Automation/TextRangeProviderWrapper.cs | 14 ++++++--- .../Automation/ToggleProviderWrapper.cs | 14 ++++++--- .../Automation/TransformProviderWrapper.cs | 14 ++++++--- .../Automation/ValueProviderWrapper.cs | 14 ++++++--- .../VirtualizedItemProviderWrapper.cs | 12 ++++--- .../Automation/WindowProviderWrapper.cs | 14 ++++++--- 22 files changed, 189 insertions(+), 119 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs index f175254ec6f..f488d7e2a99 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -28,7 +29,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class DockProviderWrapper: MarshalByRefObject, IDockProvider + internal sealed class DockProviderWrapper : MarshalByRefObject, IDockProvider { //------------------------------------------------------ // @@ -38,8 +39,11 @@ internal class DockProviderWrapper: MarshalByRefObject, IDockProvider #region Constructors - private DockProviderWrapper( AutomationPeer peer, IDockProvider iface ) + private DockProviderWrapper(AutomationPeer peer, IDockProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -91,8 +95,8 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Fields - private AutomationPeer _peer; - private IDockProvider _iface; + private readonly AutomationPeer _peer; + private readonly IDockProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs index 52631201204..62fbb7b9caf 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -28,7 +29,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class ExpandCollapseProviderWrapper: MarshalByRefObject, IExpandCollapseProvider + internal sealed class ExpandCollapseProviderWrapper : MarshalByRefObject, IExpandCollapseProvider { //------------------------------------------------------ // @@ -38,8 +39,11 @@ internal class ExpandCollapseProviderWrapper: MarshalByRefObject, IExpandCollaps #region Constructors - private ExpandCollapseProviderWrapper( AutomationPeer peer, IExpandCollapseProvider iface ) + private ExpandCollapseProviderWrapper(AutomationPeer peer, IExpandCollapseProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -97,8 +101,8 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Fields - private AutomationPeer _peer; - private IExpandCollapseProvider _iface; + private readonly AutomationPeer _peer; + private readonly IExpandCollapseProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs index 071ddfd0fbc..ee62613306f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs @@ -8,6 +8,8 @@ // // +#nullable enable + using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -26,7 +28,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class GridItemProviderWrapper: MarshalByRefObject, IGridItemProvider + internal sealed class GridItemProviderWrapper : MarshalByRefObject, IGridItemProvider { //------------------------------------------------------ // diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs index 3fdc956a73e..bfbde29d57e 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable + using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -19,7 +21,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class GridProviderWrapper: MarshalByRefObject, IGridProvider + internal sealed class GridProviderWrapper : MarshalByRefObject, IGridProvider { //------------------------------------------------------ // @@ -29,8 +31,11 @@ internal class GridProviderWrapper: MarshalByRefObject, IGridProvider #region Constructors - private GridProviderWrapper( AutomationPeer peer, IGridProvider iface ) + private GridProviderWrapper(AutomationPeer peer, IGridProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -87,8 +92,8 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Fields - private AutomationPeer _peer; - private IGridProvider _iface; + private readonly AutomationPeer _peer; + private readonly IGridProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs index c9d4e4c5253..e0f03cf24dc 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -27,7 +28,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class InvokeProviderWrapper: MarshalByRefObject, IInvokeProvider + internal sealed class InvokeProviderWrapper : MarshalByRefObject, IInvokeProvider { //------------------------------------------------------ // @@ -37,8 +38,11 @@ internal class InvokeProviderWrapper: MarshalByRefObject, IInvokeProvider #region Constructors - private InvokeProviderWrapper( AutomationPeer peer, IInvokeProvider iface ) + private InvokeProviderWrapper(AutomationPeer peer, IInvokeProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -85,8 +89,8 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Fields - private AutomationPeer _peer; - private IInvokeProvider _iface; + private readonly AutomationPeer _peer; + private readonly IInvokeProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs index cbaa6a7758d..570af3a129f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -27,7 +28,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class ItemContainerProviderWrapper : MarshalByRefObject, IItemContainerProvider + internal sealed class ItemContainerProviderWrapper : MarshalByRefObject, IItemContainerProvider { //------------------------------------------------------ // @@ -39,6 +40,9 @@ internal class ItemContainerProviderWrapper : MarshalByRefObject, IItemContainer private ItemContainerProviderWrapper(AutomationPeer peer, IItemContainerProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -97,8 +101,8 @@ internal static object Wrap(AutomationPeer peer, object iface) #region Private Fields - private AutomationPeer _peer; - private IItemContainerProvider _iface; + private readonly AutomationPeer _peer; + private readonly IItemContainerProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs index 7071b22165f..0d579991599 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -27,7 +28,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class MultipleViewProviderWrapper: MarshalByRefObject, IMultipleViewProvider + internal sealed class MultipleViewProviderWrapper : MarshalByRefObject, IMultipleViewProvider { //------------------------------------------------------ // @@ -37,8 +38,11 @@ internal class MultipleViewProviderWrapper: MarshalByRefObject, IMultipleViewPro #region Constructors - private MultipleViewProviderWrapper( AutomationPeer peer, IMultipleViewProvider iface ) + private MultipleViewProviderWrapper(AutomationPeer peer, IMultipleViewProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -92,23 +96,6 @@ internal static object Wrap( AutomationPeer peer, object iface ) #endregion Internal Methods - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - - private object SetCurrentView( object arg ) - { - _iface.SetCurrentView( (int) arg ); - return null; - } - - #endregion Private Methods - - //------------------------------------------------------ // // Private Fields @@ -117,8 +104,8 @@ private object SetCurrentView( object arg ) #region Private Fields - private AutomationPeer _peer; - private IMultipleViewProvider _iface; + private readonly AutomationPeer _peer; + private readonly IMultipleViewProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs index e9c0f684b85..f3d15f8305e 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -27,7 +28,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class RangeValueProviderWrapper: MarshalByRefObject, IRangeValueProvider + internal sealed class RangeValueProviderWrapper : MarshalByRefObject, IRangeValueProvider { //------------------------------------------------------ // @@ -37,8 +38,11 @@ internal class RangeValueProviderWrapper: MarshalByRefObject, IRangeValueProvide #region Constructors - private RangeValueProviderWrapper( AutomationPeer peer, IRangeValueProvider iface ) + private RangeValueProviderWrapper(AutomationPeer peer, IRangeValueProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -115,8 +119,8 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Fields - private AutomationPeer _peer; - private IRangeValueProvider _iface; + private readonly AutomationPeer _peer; + private readonly IRangeValueProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs index d433ec0d8ae..3d05ae6d8d7 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -27,7 +28,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class ScrollItemProviderWrapper: MarshalByRefObject, IScrollItemProvider + internal sealed class ScrollItemProviderWrapper : MarshalByRefObject, IScrollItemProvider { //------------------------------------------------------ // @@ -39,6 +40,9 @@ internal class ScrollItemProviderWrapper: MarshalByRefObject, IScrollItemProvide private ScrollItemProviderWrapper(AutomationPeer peer, IScrollItemProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -85,8 +89,8 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Fields - private AutomationPeer _peer; - private IScrollItemProvider _iface; + private readonly AutomationPeer _peer; + private readonly IScrollItemProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs index aafafb4a77d..10ac613e9b2 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -29,7 +30,7 @@ namespace MS.Internal.Automation // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class ScrollProviderWrapper: MarshalByRefObject, IScrollProvider + internal sealed class ScrollProviderWrapper : MarshalByRefObject, IScrollProvider { //------------------------------------------------------ // @@ -39,8 +40,11 @@ internal class ScrollProviderWrapper: MarshalByRefObject, IScrollProvider #region Constructors - private ScrollProviderWrapper( AutomationPeer peer, IScrollProvider iface ) + private ScrollProviderWrapper(AutomationPeer peer, IScrollProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -122,8 +126,8 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Fields - private AutomationPeer _peer; - private IScrollProvider _iface; + private readonly AutomationPeer _peer; + private readonly IScrollProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs index 2fab991778b..3ba06a28347 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -28,7 +29,7 @@ namespace MS.Internal.Automation // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class SelectionItemProviderWrapper: MarshalByRefObject, ISelectionItemProvider + internal sealed class SelectionItemProviderWrapper : MarshalByRefObject, ISelectionItemProvider { //------------------------------------------------------ // @@ -38,8 +39,11 @@ internal class SelectionItemProviderWrapper: MarshalByRefObject, ISelectionItemP #region Constructors - private SelectionItemProviderWrapper( AutomationPeer peer, ISelectionItemProvider iface ) + private SelectionItemProviderWrapper(AutomationPeer peer, ISelectionItemProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -106,8 +110,8 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Fields - private AutomationPeer _peer; - private ISelectionItemProvider _iface; + private readonly AutomationPeer _peer; + private readonly ISelectionItemProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs index abc82f1cc7f..30ed5c3aae6 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -27,7 +28,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class SelectionProviderWrapper: MarshalByRefObject, ISelectionProvider + internal sealed class SelectionProviderWrapper : MarshalByRefObject, ISelectionProvider { //------------------------------------------------------ // @@ -37,8 +38,11 @@ internal class SelectionProviderWrapper: MarshalByRefObject, ISelectionProvider #region Constructors - private SelectionProviderWrapper( AutomationPeer peer, ISelectionProvider iface ) + private SelectionProviderWrapper(AutomationPeer peer, ISelectionProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -95,8 +99,8 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Fields - private AutomationPeer _peer; - private ISelectionProvider _iface; + private readonly AutomationPeer _peer; + private readonly ISelectionProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs index 7d5a24ba0f0..03ee6279cc6 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -28,7 +29,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class SynchronizedInputProviderWrapper: MarshalByRefObject, ISynchronizedInputProvider + internal sealed class SynchronizedInputProviderWrapper : MarshalByRefObject, ISynchronizedInputProvider { //------------------------------------------------------ // @@ -38,8 +39,11 @@ internal class SynchronizedInputProviderWrapper: MarshalByRefObject, ISynchroniz #region Constructors - private SynchronizedInputProviderWrapper( AutomationPeer peer, ISynchronizedInputProvider iface ) + private SynchronizedInputProviderWrapper(AutomationPeer peer, ISynchronizedInputProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -91,8 +95,8 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Fields - private AutomationPeer _peer; - private ISynchronizedInputProvider _iface; + private readonly AutomationPeer _peer; + private readonly ISynchronizedInputProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs index 595597672ea..526adda11ea 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -27,7 +28,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class TableItemProviderWrapper: MarshalByRefObject, ITableItemProvider + internal sealed class TableItemProviderWrapper : MarshalByRefObject, ITableItemProvider { //------------------------------------------------------ // @@ -37,8 +38,11 @@ internal class TableItemProviderWrapper: MarshalByRefObject, ITableItemProvider #region Constructors - private TableItemProviderWrapper( AutomationPeer peer, ITableItemProvider iface ) + private TableItemProviderWrapper(AutomationPeer peer, ITableItemProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -115,8 +119,8 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Fields - private AutomationPeer _peer; - private ITableItemProvider _iface; + private readonly AutomationPeer _peer; + private readonly ITableItemProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs index dba5b147100..c166a2b7223 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -28,7 +29,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class TableProviderWrapper: MarshalByRefObject, ITableProvider + internal sealed class TableProviderWrapper : MarshalByRefObject, ITableProvider { //------------------------------------------------------ // @@ -38,8 +39,11 @@ internal class TableProviderWrapper: MarshalByRefObject, ITableProvider #region Constructors - private TableProviderWrapper( AutomationPeer peer, ITableProvider iface ) + private TableProviderWrapper(AutomationPeer peer, ITableProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -112,8 +116,8 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Fields - private AutomationPeer _peer; - private ITableProvider _iface; + private readonly AutomationPeer _peer; + private readonly ITableProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs index 1015a713754..fe763facd71 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs @@ -3,16 +3,17 @@ // Description: Text pattern provider wrapper for WCP -using System.Windows.Threading; using System.Windows; using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; +#nullable enable + namespace MS.Internal.Automation { // see comment on InvokeProviderWrapper class for explanation of purpose and organization of these wrapper classes. - internal class TextProviderWrapper : MarshalByRefObject, ITextProvider + internal sealed class TextProviderWrapper : MarshalByRefObject, ITextProvider { //------------------------------------------------------ // @@ -22,8 +23,11 @@ internal class TextProviderWrapper : MarshalByRefObject, ITextProvider #region Constructors - private TextProviderWrapper( AutomationPeer peer, ITextProvider iface ) + private TextProviderWrapper(AutomationPeer peer, ITextProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -110,8 +114,8 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Fields - private AutomationPeer _peer; - private ITextProvider _iface; + private readonly AutomationPeer _peer; + private readonly ITextProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs index 0a8424ab8d2..3b84c59b858 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs @@ -3,15 +3,16 @@ // Description: TextRange provider wrapper for WCP -using System.Windows.Threading; using System.Windows.Automation.Provider; using System.Windows.Automation.Text; using System.Windows.Automation.Peers; +#nullable enable + namespace MS.Internal.Automation { // see comment on InvokeProviderWrapper class for explanation of purpose and organization of these wrapper classes. - internal class TextRangeProviderWrapper: MarshalByRefObject, ITextRangeProvider + internal sealed class TextRangeProviderWrapper : MarshalByRefObject, ITextRangeProvider { //------------------------------------------------------ // @@ -21,8 +22,11 @@ internal class TextRangeProviderWrapper: MarshalByRefObject, ITextRangeProvider #region Constructors - internal TextRangeProviderWrapper( AutomationPeer peer, ITextRangeProvider iface ) + private TextRangeProviderWrapper(AutomationPeer peer, ITextRangeProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -271,8 +275,8 @@ internal static ITextRangeProvider UnwrapArgument(ITextRangeProvider argument) #region Private Fields - private AutomationPeer _peer; - private ITextRangeProvider _iface; + private readonly AutomationPeer _peer; + private readonly ITextRangeProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs index 8645edcb188..030d76e84cb 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -28,7 +29,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class ToggleProviderWrapper: MarshalByRefObject, IToggleProvider + internal sealed class ToggleProviderWrapper : MarshalByRefObject, IToggleProvider { //------------------------------------------------------ // @@ -38,8 +39,11 @@ internal class ToggleProviderWrapper: MarshalByRefObject, IToggleProvider #region Constructors - private ToggleProviderWrapper( AutomationPeer peer, IToggleProvider iface ) + private ToggleProviderWrapper(AutomationPeer peer, IToggleProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -91,8 +95,8 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Fields - private AutomationPeer _peer; - private IToggleProvider _iface; + private readonly AutomationPeer _peer; + private readonly IToggleProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs index bdbed94c750..c7c68376374 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -27,7 +28,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class TransformProviderWrapper: MarshalByRefObject, ITransformProvider + internal sealed class TransformProviderWrapper : MarshalByRefObject, ITransformProvider { //------------------------------------------------------ // @@ -37,8 +38,11 @@ internal class TransformProviderWrapper: MarshalByRefObject, ITransformProvider #region Constructors - private TransformProviderWrapper( AutomationPeer peer, ITransformProvider iface ) + private TransformProviderWrapper(AutomationPeer peer, ITransformProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -111,8 +115,8 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Fields - private AutomationPeer _peer; - private ITransformProvider _iface; + private readonly AutomationPeer _peer; + private readonly ITransformProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs index 2a911aea9cf..3a046b74186 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -27,7 +28,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class ValueProviderWrapper: MarshalByRefObject, IValueProvider + internal sealed class ValueProviderWrapper : MarshalByRefObject, IValueProvider { //------------------------------------------------------ // @@ -37,8 +38,11 @@ internal class ValueProviderWrapper: MarshalByRefObject, IValueProvider #region Constructors - private ValueProviderWrapper( AutomationPeer peer, IValueProvider iface ) + private ValueProviderWrapper(AutomationPeer peer, IValueProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -95,8 +99,8 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Fields - private AutomationPeer _peer; - private IValueProvider _iface; + private readonly AutomationPeer _peer; + private readonly IValueProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs index 86b64f84750..2d40d1da93d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -27,7 +28,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class VirtualizedItemProviderWrapper : MarshalByRefObject, IVirtualizedItemProvider + internal sealed class VirtualizedItemProviderWrapper : MarshalByRefObject, IVirtualizedItemProvider { //------------------------------------------------------ // @@ -39,6 +40,9 @@ internal class VirtualizedItemProviderWrapper : MarshalByRefObject, IVirtualized private VirtualizedItemProviderWrapper(AutomationPeer peer, IVirtualizedItemProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -85,8 +89,8 @@ internal static object Wrap(AutomationPeer peer, object iface) #region Private Fields - private AutomationPeer _peer; - private IVirtualizedItemProvider _iface; + private readonly AutomationPeer _peer; + private readonly IVirtualizedItemProvider _iface; #endregion Private Fields } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs index 80cff7b4957..a3a14859bc2 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs @@ -8,7 +8,8 @@ // // -using System.Windows.Threading; +#nullable enable + using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; @@ -28,7 +29,7 @@ namespace MS.Internal.Automation // * private methods - one for each interface entry point - which get called back // on the right context. These call through to the peer that's actually // implenting the I...Provider version of the interface. - internal class WindowProviderWrapper: MarshalByRefObject, IWindowProvider + internal sealed class WindowProviderWrapper : MarshalByRefObject, IWindowProvider { //------------------------------------------------------ // @@ -38,8 +39,11 @@ internal class WindowProviderWrapper: MarshalByRefObject, IWindowProvider #region Constructors - private WindowProviderWrapper( AutomationPeer peer, IWindowProvider iface) + private WindowProviderWrapper(AutomationPeer peer, IWindowProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -126,8 +130,8 @@ internal static object Wrap( AutomationPeer peer, object iface) #region Private Fields - private AutomationPeer _peer; - private IWindowProvider _iface; + private readonly AutomationPeer _peer; + private readonly IWindowProvider _iface; #endregion Private Fields } From 735a2ea9298609ca4e800daaa400016f2567ea88 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 18:20:27 +0200 Subject: [PATCH 39/54] Enable nullability, seal the wrappers, make fields readonly, cleanup usings (GridItem) --- .../MS/internal/Automation/GridItemProviderWrapper.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs index ee62613306f..a7c7b71620f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs @@ -38,8 +38,11 @@ internal sealed class GridItemProviderWrapper : MarshalByRefObject, IGridItemPro #region Constructors - private GridItemProviderWrapper( AutomationPeer peer, IGridItemProvider iface ) + private GridItemProviderWrapper(AutomationPeer peer, IGridItemProvider iface) { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + _peer = peer; _iface = iface; } @@ -107,8 +110,8 @@ internal static object Wrap( AutomationPeer peer, object iface ) #region Private Fields - private AutomationPeer _peer; - private IGridItemProvider _iface; + private readonly AutomationPeer _peer; + private readonly IGridItemProvider _iface; #endregion Private Fields } From 1e0e0b841f7ad1ab2d816114b6deb17d65193d08 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 18:41:31 +0200 Subject: [PATCH 40/54] Finish nullability on ITextRangeProvider --- .../Automation/TextProviderWrapper.cs | 2 +- .../Automation/TextRangeProviderWrapper.cs | 26 +++++++++---------- .../Automation/Provider/ITextRangeProvider.cs | 12 +++++---- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs index fe763facd71..53f81f69962 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs @@ -56,7 +56,7 @@ public ITextRangeProvider[] GetVisibleRanges() public ITextRangeProvider RangeFromChild(IRawElementProviderSimple childElement) { if (childElement is not ElementProxy) - throw new ArgumentException(SR.Format(SR.TextProvider_InvalidChild, "childElement")); + throw new ArgumentException(SR.Format(SR.TextProvider_InvalidChild, nameof(childElement))); // The actual invocation method that gets called on the peer's context. static ITextRangeProvider RangeFromChild(TextProviderWrapper state, IRawElementProviderSimple childElement) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs index 3b84c59b858..6e6f9c84380 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs @@ -6,6 +6,7 @@ using System.Windows.Automation.Provider; using System.Windows.Automation.Text; using System.Windows.Automation.Peers; +using System.Diagnostics.CodeAnalysis; #nullable enable @@ -81,12 +82,12 @@ public void ExpandToEnclosingUnit(TextUnit unit) ElementUtil.Invoke(_peer, static (state, unit) => state.ExpandToEnclosingUnit(unit), _iface, unit); } - public ITextRangeProvider FindAttribute(int attribute, object val, bool backward) + public ITextRangeProvider? FindAttribute(int attribute, object val, bool backward) { object[] args = [attribute, val, backward]; // The actual invocation method that gets called on the peer's context. - static ITextRangeProvider FindAttribute(TextRangeProviderWrapper state, object[] args) + static ITextRangeProvider? FindAttribute(TextRangeProviderWrapper state, object[] args) { int attribute = (int)args[0]; object val = args[1]; @@ -98,12 +99,12 @@ static ITextRangeProvider FindAttribute(TextRangeProviderWrapper state, object[] return ElementUtil.Invoke(_peer, FindAttribute, this, args); } - public ITextRangeProvider FindText(string text, bool backward, bool ignoreCase) + public ITextRangeProvider? FindText(string text, bool backward, bool ignoreCase) { object[] args = [text, backward, ignoreCase]; // The actual invocation method that gets called on the peer's context. - static ITextRangeProvider FindText(TextRangeProviderWrapper state, object[] args) + static ITextRangeProvider? FindText(TextRangeProviderWrapper state, object[] args) { string text = (string)args[0]; bool backward = (bool)args[1]; @@ -209,7 +210,7 @@ public void ScrollIntoView(bool alignToTop) ElementUtil.Invoke(_peer, static (state, alignToTop) => state.ScrollIntoView(alignToTop), _iface, alignToTop); } - public IRawElementProviderSimple[] GetChildren() + public IRawElementProviderSimple[]? GetChildren() { return ElementUtil.Invoke(_peer, static (state) => state.GetChildren(), _iface); } @@ -227,7 +228,8 @@ public IRawElementProviderSimple[] GetChildren() #region Internal Methods // Wrap arguments that are being returned, assuming they're not null or already wrapped. - internal static ITextRangeProvider WrapArgument(ITextRangeProvider argument, AutomationPeer peer) + [return: NotNullIfNotNull(nameof(argument))] + internal static ITextRangeProvider? WrapArgument(ITextRangeProvider? argument, AutomationPeer peer) { if (argument == null) return null; @@ -238,12 +240,13 @@ internal static ITextRangeProvider WrapArgument(ITextRangeProvider argument, Aut return new TextRangeProviderWrapper(peer, argument); } - internal static ITextRangeProvider [] WrapArgument(ITextRangeProvider [] argument, AutomationPeer peer) + [return: NotNullIfNotNull(nameof(argument))] + internal static ITextRangeProvider[]? WrapArgument(ITextRangeProvider[]? argument, AutomationPeer peer) { if (argument == null) return null; - if (argument is TextRangeProviderWrapper []) + if (argument is TextRangeProviderWrapper[]) return argument; ITextRangeProvider[] outArray = new ITextRangeProvider[argument.Length]; @@ -257,12 +260,7 @@ internal static ITextRangeProvider [] WrapArgument(ITextRangeProvider [] argumen // Remove the wrapper from the argument if a wrapper exists internal static ITextRangeProvider UnwrapArgument(ITextRangeProvider argument) { - if (argument is TextRangeProviderWrapper) - { - return ((TextRangeProviderWrapper)argument)._iface; - } - - return argument; + return argument is TextRangeProviderWrapper wrapper ? wrapper._iface : argument; } #endregion Internal Methods diff --git a/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationTypes/System/Windows/Automation/Provider/ITextRangeProvider.cs b/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationTypes/System/Windows/Automation/Provider/ITextRangeProvider.cs index b5bc9f12786..bea04493644 100644 --- a/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationTypes/System/Windows/Automation/Provider/ITextRangeProvider.cs +++ b/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationTypes/System/Windows/Automation/Provider/ITextRangeProvider.cs @@ -1,8 +1,10 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // Description: Interface exposes a control's ability to manipulate text ranges +#nullable enable + using System.Windows.Automation.Text; using System.Runtime.InteropServices; @@ -71,7 +73,7 @@ public interface ITextRangeProvider /// The value of the specified attribute to search for. /// true if the last occurring range should be returned instead of the first. /// A subrange with the specified attribute, or null if no such subrange exists. - ITextRangeProvider FindAttribute(int attribute, object value, [MarshalAs(UnmanagedType.Bool)] bool backward); + ITextRangeProvider? FindAttribute(int attribute, object value, [MarshalAs(UnmanagedType.Bool)] bool backward); /// /// Searches for an occurrence of text within the range. @@ -80,7 +82,7 @@ public interface ITextRangeProvider /// true if the last occurring range should be returned instead of the first. /// true if case should be ignored for the purposes of comparison. /// A subrange with the specified text, or null if no such subrange exists. - ITextRangeProvider FindText(string text, [MarshalAs(UnmanagedType.Bool)] bool backward, [MarshalAs(UnmanagedType.Bool)] bool ignoreCase); + ITextRangeProvider? FindText(string text, [MarshalAs(UnmanagedType.Bool)] bool backward, [MarshalAs(UnmanagedType.Bool)] bool ignoreCase); /// /// Retrieves the value of a text attribute over the entire range. @@ -100,7 +102,7 @@ public interface ITextRangeProvider /// /// /// - double [] GetBoundingRectangles(); + double[] GetBoundingRectangles(); /// /// Retrieves the innermost element that encloses this range. @@ -204,7 +206,7 @@ public interface ITextRangeProvider /// that overlap with the range but are not entirely enclosed by it will /// also be included in the collection. If there are no children then /// this can return either null or an empty enumeration. - IRawElementProviderSimple[] GetChildren(); + IRawElementProviderSimple[]? GetChildren(); #endregion Public Properties } From cf6d0efd187f656c8c3dff2c7929dcc678b3e159 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 18:45:54 +0200 Subject: [PATCH 41/54] Formatting changes ONLY - region cleanup, whitespaces --- .../Automation/DockProviderWrapper.cs | 50 ++---------------- .../ExpandCollapseProviderWrapper.cs | 51 ++----------------- .../Automation/GridItemProviderWrapper.cs | 51 ++----------------- .../Automation/GridProviderWrapper.cs | 50 ++---------------- .../Automation/InvokeProviderWrapper.cs | 50 ++---------------- .../ItemContainerProviderWrapper.cs | 46 +---------------- .../Automation/MultipleViewProviderWrapper.cs | 50 ++---------------- .../Automation/RangeValueProviderWrapper.cs | 50 ++---------------- .../Automation/ScrollItemProviderWrapper.cs | 48 ++--------------- .../Automation/ScrollProviderWrapper.cs | 50 ++---------------- .../SelectionItemProviderWrapper.cs | 50 ++---------------- .../Automation/SelectionProviderWrapper.cs | 50 ++---------------- .../SynchronizedInputProviderWrapper.cs | 50 ++---------------- .../Automation/TableItemProviderWrapper.cs | 50 ++---------------- .../Automation/TableProviderWrapper.cs | 51 ++----------------- .../Automation/TextProviderWrapper.cs | 50 ++---------------- .../Automation/TextRangeProviderWrapper.cs | 47 +---------------- .../Automation/ToggleProviderWrapper.cs | 50 ++---------------- .../Automation/TransformProviderWrapper.cs | 51 ++----------------- .../Automation/ValueProviderWrapper.cs | 50 ++---------------- .../VirtualizedItemProviderWrapper.cs | 46 +---------------- .../Automation/WindowProviderWrapper.cs | 50 ++---------------- 22 files changed, 81 insertions(+), 1010 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs index f488d7e2a99..848c1098672 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs @@ -31,13 +31,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class DockProviderWrapper : MarshalByRefObject, IDockProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly IDockProvider _iface; private DockProviderWrapper(AutomationPeer peer, IDockProvider iface) { @@ -48,17 +43,6 @@ private DockProviderWrapper(AutomationPeer peer, IDockProvider iface) _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface IDockProvider - // - //------------------------------------------------------ - - #region Interface IDockProvider - public void SetDockPosition(DockPosition dockPosition) { ElementUtil.Invoke(_peer, static (state, dockPosition) => state.SetDockPosition(dockPosition), _iface, dockPosition); @@ -69,35 +53,9 @@ public DockPosition DockPosition get => ElementUtil.Invoke(_peer, static (state) => state.DockPosition, _iface); } - #endregion Interface IDockProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { - return new DockProviderWrapper( peer, (IDockProvider) iface ); + return new DockProviderWrapper(peer, (IDockProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly IDockProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs index 62fbb7b9caf..41e5e54d198 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs @@ -31,13 +31,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class ExpandCollapseProviderWrapper : MarshalByRefObject, IExpandCollapseProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly IExpandCollapseProvider _iface; private ExpandCollapseProviderWrapper(AutomationPeer peer, IExpandCollapseProvider iface) { @@ -48,17 +43,6 @@ private ExpandCollapseProviderWrapper(AutomationPeer peer, IExpandCollapseProvid _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface IExpandCollapseProvider - // - //------------------------------------------------------ - - #region Interface IExpandCollapseProvider - public void Expand() { ElementUtil.Invoke(_peer, static (state) => state.Expand(), _iface); @@ -74,36 +58,9 @@ public ExpandCollapseState ExpandCollapseState get => ElementUtil.Invoke(_peer, static (state) => state.ExpandCollapseState, _iface); } - #endregion Interface IExpandCollapseProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { - return new ExpandCollapseProviderWrapper( peer, (IExpandCollapseProvider) iface ); + return new ExpandCollapseProviderWrapper(peer, (IExpandCollapseProvider)iface); } - - #endregion Internal Methods - - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly IExpandCollapseProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs index a7c7b71620f..6204db9e236 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs @@ -30,13 +30,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class GridItemProviderWrapper : MarshalByRefObject, IGridItemProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly IGridItemProvider _iface; private GridItemProviderWrapper(AutomationPeer peer, IGridItemProvider iface) { @@ -47,17 +42,6 @@ private GridItemProviderWrapper(AutomationPeer peer, IGridItemProvider iface) _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface IGridItemProvider - // - //------------------------------------------------------ - - #region Interface IGridItemProvider - public int Row { get => ElementUtil.Invoke(_peer, static (state) => state.Row, _iface); @@ -83,36 +67,9 @@ public IRawElementProviderSimple ContainingGrid get => ElementUtil.Invoke(_peer, static (state) => state.ContainingGrid, _iface); } - #endregion Interface IGridItemProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { - return new GridItemProviderWrapper( peer, (IGridItemProvider) iface ); + return new GridItemProviderWrapper(peer, (IGridItemProvider)iface); } - - #endregion Internal Methods - - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly IGridItemProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs index bfbde29d57e..509ee523aec 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs @@ -23,13 +23,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class GridProviderWrapper : MarshalByRefObject, IGridProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly IGridProvider _iface; private GridProviderWrapper(AutomationPeer peer, IGridProvider iface) { @@ -40,17 +35,6 @@ private GridProviderWrapper(AutomationPeer peer, IGridProvider iface) _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface IGridProvider - // - //------------------------------------------------------ - - #region Interface IGridProvider - public IRawElementProviderSimple GetItem(int row, int column) { return ElementUtil.Invoke(_peer, static (state, rowColumn) => state.GetItem(rowColumn[0], rowColumn[1]), _iface, new int[] { row, column }); @@ -66,35 +50,9 @@ public int ColumnCount get => ElementUtil.Invoke(_peer, static (state) => state.ColumnCount, _iface); } - #endregion Interface IGridProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { - return new GridProviderWrapper( peer, (IGridProvider) iface ); + return new GridProviderWrapper(peer, (IGridProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly IGridProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs index e0f03cf24dc..862b1e9cdb3 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs @@ -30,13 +30,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class InvokeProviderWrapper : MarshalByRefObject, IInvokeProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly IInvokeProvider _iface; private InvokeProviderWrapper(AutomationPeer peer, IInvokeProvider iface) { @@ -47,51 +42,14 @@ private InvokeProviderWrapper(AutomationPeer peer, IInvokeProvider iface) _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface IInvokeProvider - // - //------------------------------------------------------ - - #region Interface IInvokeProvider - public void Invoke() { ElementUtil.Invoke(_peer, static (state) => state.Invoke(), _iface); } - #endregion Interface IInvokeProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { - return new InvokeProviderWrapper( peer, (IInvokeProvider) iface ); + return new InvokeProviderWrapper(peer, (IInvokeProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly IInvokeProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs index 570af3a129f..84fc5f4ea95 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs @@ -30,13 +30,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class ItemContainerProviderWrapper : MarshalByRefObject, IItemContainerProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly IItemContainerProvider _iface; private ItemContainerProviderWrapper(AutomationPeer peer, IItemContainerProvider iface) { @@ -47,17 +42,6 @@ private ItemContainerProviderWrapper(AutomationPeer peer, IItemContainerProvider _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface IItemContainerProvider - // - //------------------------------------------------------ - - #region Interface IItemContainerProvider - public IRawElementProviderSimple FindItemByProperty(IRawElementProviderSimple startAfter, int propertyId, object value) { object[] args = [startAfter, propertyId, value]; @@ -75,35 +59,9 @@ static IRawElementProviderSimple FindItemByProperty(IItemContainerProvider state return ElementUtil.Invoke(_peer, FindItemByProperty, _iface, args); } - #endregion Interface IItemContainerProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - internal static object Wrap(AutomationPeer peer, object iface) { return new ItemContainerProviderWrapper(peer, (IItemContainerProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly IItemContainerProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs index 0d579991599..34a60724b24 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs @@ -30,13 +30,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class MultipleViewProviderWrapper : MarshalByRefObject, IMultipleViewProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly IMultipleViewProvider _iface; private MultipleViewProviderWrapper(AutomationPeer peer, IMultipleViewProvider iface) { @@ -47,17 +42,6 @@ private MultipleViewProviderWrapper(AutomationPeer peer, IMultipleViewProvider i _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface IMultipleViewProvider - // - //------------------------------------------------------ - - #region Interface IMultipleViewProvider - public string GetViewName(int viewID) { return ElementUtil.Invoke(_peer, static (state, viewID) => state.GetViewName(viewID), _iface, viewID); @@ -78,35 +62,9 @@ public int[] GetSupportedViews() return ElementUtil.Invoke(_peer, static (state) => state.GetSupportedViews(), _iface); } - #endregion Interface IMultipleViewProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { - return new MultipleViewProviderWrapper( peer, (IMultipleViewProvider) iface ); + return new MultipleViewProviderWrapper(peer, (IMultipleViewProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly IMultipleViewProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs index f3d15f8305e..0428d008133 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs @@ -30,13 +30,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class RangeValueProviderWrapper : MarshalByRefObject, IRangeValueProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly IRangeValueProvider _iface; private RangeValueProviderWrapper(AutomationPeer peer, IRangeValueProvider iface) { @@ -47,17 +42,6 @@ private RangeValueProviderWrapper(AutomationPeer peer, IRangeValueProvider iface _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface IRangeValueProvider - // - //------------------------------------------------------ - - #region Interface IRangeValueProvider - public void SetValue(double val) { ElementUtil.Invoke(_peer, static (state, val) => state.SetValue(val), _iface, val); @@ -93,35 +77,9 @@ public double SmallChange get => ElementUtil.Invoke(_peer, static (state) => state.SmallChange, _iface); } - #endregion Interface IRangeValueProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { - return new RangeValueProviderWrapper( peer, (IRangeValueProvider) iface ); + return new RangeValueProviderWrapper(peer, (IRangeValueProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly IRangeValueProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs index 3d05ae6d8d7..b1de17e564c 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs @@ -30,13 +30,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class ScrollItemProviderWrapper : MarshalByRefObject, IScrollItemProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly IScrollItemProvider _iface; private ScrollItemProviderWrapper(AutomationPeer peer, IScrollItemProvider iface) { @@ -47,51 +42,14 @@ private ScrollItemProviderWrapper(AutomationPeer peer, IScrollItemProvider iface _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface IInvokeProvider - // - //------------------------------------------------------ - - #region Interface IScrollItemProvider - public void ScrollIntoView() { ElementUtil.Invoke(_peer, static (state) => state.ScrollIntoView(), _iface); } - #endregion Interface IScrollItemProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { return new ScrollItemProviderWrapper(peer, (IScrollItemProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly IScrollItemProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs index 10ac613e9b2..4d4a7a58c4e 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs @@ -32,13 +32,8 @@ namespace MS.Internal.Automation internal sealed class ScrollProviderWrapper : MarshalByRefObject, IScrollProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly IScrollProvider _iface; private ScrollProviderWrapper(AutomationPeer peer, IScrollProvider iface) { @@ -49,17 +44,6 @@ private ScrollProviderWrapper(AutomationPeer peer, IScrollProvider iface) _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface IScrollProvider - // - //------------------------------------------------------ - - #region Interface IScrollProvider - public void Scroll(ScrollAmount horizontalAmount, ScrollAmount verticalAmount) { ElementUtil.Invoke(_peer, static (state, values) => state.Scroll(values[0], values[1]), _iface, new ScrollAmount[] { horizontalAmount, verticalAmount }); @@ -100,35 +84,9 @@ public bool VerticallyScrollable get => ElementUtil.Invoke(_peer, static (state) => state.VerticallyScrollable, _iface); } - #endregion Interface IScrollProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { - return new ScrollProviderWrapper( peer, (IScrollProvider) iface ); + return new ScrollProviderWrapper(peer, (IScrollProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly IScrollProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs index 3ba06a28347..09e79342e6c 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs @@ -31,13 +31,8 @@ namespace MS.Internal.Automation internal sealed class SelectionItemProviderWrapper : MarshalByRefObject, ISelectionItemProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly ISelectionItemProvider _iface; private SelectionItemProviderWrapper(AutomationPeer peer, ISelectionItemProvider iface) { @@ -48,17 +43,6 @@ private SelectionItemProviderWrapper(AutomationPeer peer, ISelectionItemProvider _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface ISelectionItemProvider - // - //------------------------------------------------------ - - #region Interface ISelectionItemProvider - public void Select() { ElementUtil.Invoke(_peer, static (state) => state.Select(), _iface); @@ -84,35 +68,9 @@ public IRawElementProviderSimple SelectionContainer get => ElementUtil.Invoke(_peer, static (state) => state.SelectionContainer, _iface); } - #endregion Interface ISelectionItemProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { - return new SelectionItemProviderWrapper( peer, (ISelectionItemProvider) iface ); + return new SelectionItemProviderWrapper(peer, (ISelectionItemProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly ISelectionItemProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs index 30ed5c3aae6..db10dd720b6 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs @@ -30,13 +30,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class SelectionProviderWrapper : MarshalByRefObject, ISelectionProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly ISelectionProvider _iface; private SelectionProviderWrapper(AutomationPeer peer, ISelectionProvider iface) { @@ -47,17 +42,6 @@ private SelectionProviderWrapper(AutomationPeer peer, ISelectionProvider iface) _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface ISelectionProvider - // - //------------------------------------------------------ - - #region Interface ISelectionProvider - public IRawElementProviderSimple[] GetSelection() { return ElementUtil.Invoke(_peer, static (state) => state.GetSelection(), _iface); @@ -73,35 +57,9 @@ public bool IsSelectionRequired get => ElementUtil.Invoke(_peer, static (state) => state.IsSelectionRequired, _iface); } - #endregion Interface ISelectionProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { - return new SelectionProviderWrapper( peer, (ISelectionProvider) iface ); + return new SelectionProviderWrapper(peer, (ISelectionProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly ISelectionProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs index 03ee6279cc6..88f3839a9ad 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs @@ -31,13 +31,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class SynchronizedInputProviderWrapper : MarshalByRefObject, ISynchronizedInputProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly ISynchronizedInputProvider _iface; private SynchronizedInputProviderWrapper(AutomationPeer peer, ISynchronizedInputProvider iface) { @@ -48,17 +43,6 @@ private SynchronizedInputProviderWrapper(AutomationPeer peer, ISynchronizedInput _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface ISynchronizedInputProvider - // - //------------------------------------------------------ - - #region Interface ISynchronizedInputProvider - public void StartListening(SynchronizedInputType inputType) { ElementUtil.Invoke(_peer, static (state, inputType) => state.StartListening(inputType), _iface, inputType); @@ -69,35 +53,9 @@ public void Cancel() ElementUtil.Invoke(_peer, static (state) => state.Cancel(), _iface); } - #endregion Interface ISynchronizedInputProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { - return new SynchronizedInputProviderWrapper( peer, (ISynchronizedInputProvider) iface ); + return new SynchronizedInputProviderWrapper(peer, (ISynchronizedInputProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly ISynchronizedInputProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs index 526adda11ea..bb756f0331c 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs @@ -30,13 +30,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class TableItemProviderWrapper : MarshalByRefObject, ITableItemProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly ITableItemProvider _iface; private TableItemProviderWrapper(AutomationPeer peer, ITableItemProvider iface) { @@ -47,17 +42,6 @@ private TableItemProviderWrapper(AutomationPeer peer, ITableItemProvider iface) _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface ITableItemProvider - // - //------------------------------------------------------ - - #region Interface ITableItemProvider - public int Row { get => ElementUtil.Invoke(_peer, static (state) => state.Row, _iface); @@ -93,35 +77,9 @@ public IRawElementProviderSimple[] GetColumnHeaderItems() return ElementUtil.Invoke(_peer, static (state) => state.GetColumnHeaderItems(), _iface); } - #endregion Interface ITableItemProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { - return new TableItemProviderWrapper( peer, (ITableItemProvider) iface ); + return new TableItemProviderWrapper(peer, (ITableItemProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly ITableItemProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs index c166a2b7223..5765070ca9d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs @@ -31,13 +31,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class TableProviderWrapper : MarshalByRefObject, ITableProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly ITableProvider _iface; private TableProviderWrapper(AutomationPeer peer, ITableProvider iface) { @@ -48,17 +43,6 @@ private TableProviderWrapper(AutomationPeer peer, ITableProvider iface) _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface ITableProvider - // - //------------------------------------------------------ - - #region Interface ITableProvider - public IRawElementProviderSimple GetItem(int row, int column) { return ElementUtil.Invoke(_peer, static (state, rowColumn) => state.GetItem(rowColumn[0], rowColumn[1]), _iface, new int[] { row, column }); @@ -89,36 +73,9 @@ public RowOrColumnMajor RowOrColumnMajor get => ElementUtil.Invoke(_peer, static (state) => state.RowOrColumnMajor, _iface); } - #endregion Interface ITableProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { - return new TableProviderWrapper( peer, (ITableProvider) iface ); + return new TableProviderWrapper(peer, (ITableProvider)iface); } - - #endregion Internal Methods - - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly ITableProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs index 53f81f69962..1ffef63acc5 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs @@ -15,13 +15,8 @@ namespace MS.Internal.Automation // see comment on InvokeProviderWrapper class for explanation of purpose and organization of these wrapper classes. internal sealed class TextProviderWrapper : MarshalByRefObject, ITextProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly ITextProvider _iface; private TextProviderWrapper(AutomationPeer peer, ITextProvider iface) { @@ -32,17 +27,6 @@ private TextProviderWrapper(AutomationPeer peer, ITextProvider iface) _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface ITextProvider - // - //------------------------------------------------------ - - #region Interface ITextProvider - public ITextRangeProvider[] GetSelection() { return ElementUtil.Invoke(_peer, static (state, peer) => TextRangeProviderWrapper.WrapArgument(state.GetSelection(), peer), _iface, _peer); @@ -88,36 +72,10 @@ public SupportedTextSelection SupportedTextSelection get => ElementUtil.Invoke(_peer, static (state) => state.SupportedTextSelection, _iface); } - #endregion Interface ITextProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { - return new TextProviderWrapper( peer, (ITextProvider) iface ); + return new TextProviderWrapper(peer, (ITextProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly ITextProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs index 6e6f9c84380..b67b4dae8a5 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs @@ -15,13 +15,8 @@ namespace MS.Internal.Automation // see comment on InvokeProviderWrapper class for explanation of purpose and organization of these wrapper classes. internal sealed class TextRangeProviderWrapper : MarshalByRefObject, ITextRangeProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly ITextRangeProvider _iface; private TextRangeProviderWrapper(AutomationPeer peer, ITextRangeProvider iface) { @@ -32,17 +27,6 @@ private TextRangeProviderWrapper(AutomationPeer peer, ITextRangeProvider iface) _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface ITextRangeProvider - // - //------------------------------------------------------ - - #region Interface ITextRangeProvider - public ITextRangeProvider Clone() { return ElementUtil.Invoke(_peer, static (state, peer) => WrapArgument(state.Clone(), peer), _iface, _peer); @@ -215,18 +199,6 @@ public void ScrollIntoView(bool alignToTop) return ElementUtil.Invoke(_peer, static (state) => state.GetChildren(), _iface); } - - #endregion Interface ITextRangeProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - // Wrap arguments that are being returned, assuming they're not null or already wrapped. [return: NotNullIfNotNull(nameof(argument))] internal static ITextRangeProvider? WrapArgument(ITextRangeProvider? argument, AutomationPeer peer) @@ -262,21 +234,6 @@ internal static ITextRangeProvider UnwrapArgument(ITextRangeProvider argument) { return argument is TextRangeProviderWrapper wrapper ? wrapper._iface : argument; } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly ITextRangeProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs index 030d76e84cb..78ac89bc529 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs @@ -31,13 +31,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class ToggleProviderWrapper : MarshalByRefObject, IToggleProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly IToggleProvider _iface; private ToggleProviderWrapper(AutomationPeer peer, IToggleProvider iface) { @@ -48,17 +43,6 @@ private ToggleProviderWrapper(AutomationPeer peer, IToggleProvider iface) _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface IValueProvider - // - //------------------------------------------------------ - - #region Interface IToggleProvider - public void Toggle() { ElementUtil.Invoke(_peer, static (state) => state.Toggle(), _iface); @@ -69,35 +53,9 @@ public ToggleState ToggleState get => ElementUtil.Invoke(_peer, static (state) => state.ToggleState, _iface); } - #endregion Interface IToggleProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { - return new ToggleProviderWrapper( peer, (IToggleProvider) iface ); + return new ToggleProviderWrapper(peer, (IToggleProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly IToggleProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs index c7c68376374..44fb253c696 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs @@ -30,13 +30,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class TransformProviderWrapper : MarshalByRefObject, ITransformProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly ITransformProvider _iface; private TransformProviderWrapper(AutomationPeer peer, ITransformProvider iface) { @@ -47,18 +42,6 @@ private TransformProviderWrapper(AutomationPeer peer, ITransformProvider iface) _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface IWindowProvider - // - //------------------------------------------------------ - - #region Interface ITransformProvider - - public void Move(double x, double y) { ElementUtil.Invoke(_peer, static (state, coordinates) => state.Move(coordinates[0], coordinates[1]), _iface, new double[] { x, y }); @@ -89,35 +72,9 @@ public bool CanRotate get => ElementUtil.Invoke(_peer, static (state) => state.CanRotate, _iface); } - #endregion Interface ITransformProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { - return new TransformProviderWrapper( peer, (ITransformProvider) iface ); + return new TransformProviderWrapper(peer, (ITransformProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly ITransformProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs index 3a046b74186..283d932a003 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs @@ -30,13 +30,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class ValueProviderWrapper : MarshalByRefObject, IValueProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly IValueProvider _iface; private ValueProviderWrapper(AutomationPeer peer, IValueProvider iface) { @@ -47,17 +42,6 @@ private ValueProviderWrapper(AutomationPeer peer, IValueProvider iface) _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface IValueProvider - // - //------------------------------------------------------ - - #region Interface IValueProvider - public void SetValue(string val) { ElementUtil.Invoke(_peer, static (state, value) => state.SetValue(value), _iface, val); @@ -73,35 +57,9 @@ public bool IsReadOnly get => ElementUtil.Invoke(_peer, static (state) => state.IsReadOnly, _iface); } - #endregion Interface IValueProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface ) + internal static object Wrap(AutomationPeer peer, object iface) { - return new ValueProviderWrapper( peer, (IValueProvider) iface ); + return new ValueProviderWrapper(peer, (IValueProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly IValueProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs index 2d40d1da93d..1576eaae8dc 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs @@ -30,13 +30,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class VirtualizedItemProviderWrapper : MarshalByRefObject, IVirtualizedItemProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly IVirtualizedItemProvider _iface; private VirtualizedItemProviderWrapper(AutomationPeer peer, IVirtualizedItemProvider iface) { @@ -47,51 +42,14 @@ private VirtualizedItemProviderWrapper(AutomationPeer peer, IVirtualizedItemProv _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface IVirtualizedItemProvider - // - //------------------------------------------------------ - - #region Interface IVirtualizedItemProvider - public void Realize() { ElementUtil.Invoke(_peer, static (state) => state.Realize(), _iface); } - #endregion Interface IVirtualizedItemProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - internal static object Wrap(AutomationPeer peer, object iface) { return new VirtualizedItemProviderWrapper(peer, (IVirtualizedItemProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly IVirtualizedItemProvider _iface; - - #endregion Private Fields } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs index a3a14859bc2..7dbbed8c642 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs @@ -31,13 +31,8 @@ namespace MS.Internal.Automation // implenting the I...Provider version of the interface. internal sealed class WindowProviderWrapper : MarshalByRefObject, IWindowProvider { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors + private readonly AutomationPeer _peer; + private readonly IWindowProvider _iface; private WindowProviderWrapper(AutomationPeer peer, IWindowProvider iface) { @@ -48,17 +43,6 @@ private WindowProviderWrapper(AutomationPeer peer, IWindowProvider iface) _iface = iface; } - #endregion Constructors - - - //------------------------------------------------------ - // - // Interface IWindowProvider - // - //------------------------------------------------------ - - #region Interface IWindowProvider - public void SetVisualState(WindowVisualState state) { ElementUtil.Invoke(_peer, static (state, visualState) => state.SetVisualState(visualState), _iface, state); @@ -104,35 +88,9 @@ public bool IsTopmost get => ElementUtil.Invoke(_peer, static (state) => state.IsTopmost, _iface); } - #endregion Interface IWindowProvider - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static object Wrap( AutomationPeer peer, object iface) + internal static object Wrap(AutomationPeer peer, object iface) { - return new WindowProviderWrapper( peer, (IWindowProvider) iface ); + return new WindowProviderWrapper(peer, (IWindowProvider)iface); } - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - private readonly AutomationPeer _peer; - private readonly IWindowProvider _iface; - - #endregion Private Fields } } From c59ba7145a785f1de3122ef059b0b3df6c0d397c Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 18:57:47 +0200 Subject: [PATCH 42/54] Formatting changes ONLY - Flatten all namespaces on wrappers --- .../Automation/DockProviderWrapper.cs | 78 ++-- .../ExpandCollapseProviderWrapper.cs | 75 ++-- .../Automation/GridItemProviderWrapper.cs | 94 ++--- .../Automation/GridProviderWrapper.cs | 81 ++--- .../Automation/InvokeProviderWrapper.cs | 68 ++-- .../ItemContainerProviderWrapper.cs | 78 ++-- .../Automation/MultipleViewProviderWrapper.cs | 86 ++--- .../Automation/RangeValueProviderWrapper.cs | 110 +++--- .../Automation/ScrollItemProviderWrapper.cs | 68 ++-- .../Automation/ScrollProviderWrapper.cs | 117 +++--- .../SelectionItemProviderWrapper.cs | 93 ++--- .../Automation/SelectionProviderWrapper.cs | 78 ++-- .../SynchronizedInputProviderWrapper.cs | 78 ++-- .../Automation/TableItemProviderWrapper.cs | 110 +++--- .../Automation/TableProviderWrapper.cs | 102 +++--- .../Automation/TextProviderWrapper.cs | 105 +++--- .../Automation/TextRangeProviderWrapper.cs | 336 +++++++++--------- .../Automation/ToggleProviderWrapper.cs | 78 ++-- .../Automation/TransformProviderWrapper.cs | 102 +++--- .../Automation/ValueProviderWrapper.cs | 78 ++-- .../VirtualizedItemProviderWrapper.cs | 68 ++-- .../Automation/WindowProviderWrapper.cs | 148 ++++---- 22 files changed, 935 insertions(+), 1296 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs index 848c1098672..265ba05885a 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs @@ -1,61 +1,43 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Dock pattern provider wrapper for WCP -// -// - #nullable enable using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class DockProviderWrapper : MarshalByRefObject, IDockProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class DockProviderWrapper : MarshalByRefObject, IDockProvider + private readonly AutomationPeer _peer; + private readonly IDockProvider _iface; + + private DockProviderWrapper(AutomationPeer peer, IDockProvider iface) + { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + + _peer = peer; + _iface = iface; + } + + public void SetDockPosition(DockPosition dockPosition) + { + ElementUtil.Invoke(_peer, static (state, dockPosition) => state.SetDockPosition(dockPosition), _iface, dockPosition); + } + + public DockPosition DockPosition + { + get => ElementUtil.Invoke(_peer, static (state) => state.DockPosition, _iface); + } + + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) { - private readonly AutomationPeer _peer; - private readonly IDockProvider _iface; - - private DockProviderWrapper(AutomationPeer peer, IDockProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); - - _peer = peer; - _iface = iface; - } - - public void SetDockPosition(DockPosition dockPosition) - { - ElementUtil.Invoke(_peer, static (state, dockPosition) => state.SetDockPosition(dockPosition), _iface, dockPosition); - } - - public DockPosition DockPosition - { - get => ElementUtil.Invoke(_peer, static (state) => state.DockPosition, _iface); - } - - internal static object Wrap(AutomationPeer peer, object iface) - { - return new DockProviderWrapper(peer, (IDockProvider)iface); - } + return new DockProviderWrapper(peer, (IDockProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs index 41e5e54d198..e55235ad435 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs @@ -1,66 +1,45 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Expand Collapse pattern provider wrapper for WCP -// -// - #nullable enable using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class ExpandCollapseProviderWrapper : MarshalByRefObject, IExpandCollapseProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class ExpandCollapseProviderWrapper : MarshalByRefObject, IExpandCollapseProvider - { - private readonly AutomationPeer _peer; - private readonly IExpandCollapseProvider _iface; + private readonly AutomationPeer _peer; + private readonly IExpandCollapseProvider _iface; - private ExpandCollapseProviderWrapper(AutomationPeer peer, IExpandCollapseProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); + private ExpandCollapseProviderWrapper(AutomationPeer peer, IExpandCollapseProvider iface) + { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); - _peer = peer; - _iface = iface; - } + _peer = peer; + _iface = iface; + } - public void Expand() - { - ElementUtil.Invoke(_peer, static (state) => state.Expand(), _iface); - } + public void Expand() + { + ElementUtil.Invoke(_peer, static (state) => state.Expand(), _iface); + } - public void Collapse() - { - ElementUtil.Invoke(_peer, static (state) => state.Collapse(), _iface); - } + public void Collapse() + { + ElementUtil.Invoke(_peer, static (state) => state.Collapse(), _iface); + } - public ExpandCollapseState ExpandCollapseState - { - get => ElementUtil.Invoke(_peer, static (state) => state.ExpandCollapseState, _iface); - } + public ExpandCollapseState ExpandCollapseState + { + get => ElementUtil.Invoke(_peer, static (state) => state.ExpandCollapseState, _iface); + } - internal static object Wrap(AutomationPeer peer, object iface) - { - return new ExpandCollapseProviderWrapper(peer, (IExpandCollapseProvider)iface); - } + internal static object Wrap(AutomationPeer peer, object iface) + { + return new ExpandCollapseProviderWrapper(peer, (IExpandCollapseProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs index 6204db9e236..60630520979 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs @@ -1,75 +1,57 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Grid Item pattern provider wrapper for WCP -// -// - #nullable enable using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class GridItemProviderWrapper : MarshalByRefObject, IGridItemProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class GridItemProviderWrapper : MarshalByRefObject, IGridItemProvider - { - private readonly AutomationPeer _peer; - private readonly IGridItemProvider _iface; + private readonly AutomationPeer _peer; + private readonly IGridItemProvider _iface; - private GridItemProviderWrapper(AutomationPeer peer, IGridItemProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); + private GridItemProviderWrapper(AutomationPeer peer, IGridItemProvider iface) + { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); - _peer = peer; - _iface = iface; - } + _peer = peer; + _iface = iface; + } - public int Row - { - get => ElementUtil.Invoke(_peer, static (state) => state.Row, _iface); - } + public int Row + { + get => ElementUtil.Invoke(_peer, static (state) => state.Row, _iface); + } - public int Column - { - get => ElementUtil.Invoke(_peer, static (state) => state.Column, _iface); - } + public int Column + { + get => ElementUtil.Invoke(_peer, static (state) => state.Column, _iface); + } - public int RowSpan - { - get => ElementUtil.Invoke(_peer, static (state) => state.RowSpan, _iface); - } + public int RowSpan + { + get => ElementUtil.Invoke(_peer, static (state) => state.RowSpan, _iface); + } - public int ColumnSpan - { - get => ElementUtil.Invoke(_peer, static (state) => state.ColumnSpan, _iface); - } + public int ColumnSpan + { + get => ElementUtil.Invoke(_peer, static (state) => state.ColumnSpan, _iface); + } - public IRawElementProviderSimple ContainingGrid - { - get => ElementUtil.Invoke(_peer, static (state) => state.ContainingGrid, _iface); - } + public IRawElementProviderSimple ContainingGrid + { + get => ElementUtil.Invoke(_peer, static (state) => state.ContainingGrid, _iface); + } - internal static object Wrap(AutomationPeer peer, object iface) - { - return new GridItemProviderWrapper(peer, (IGridItemProvider)iface); - } + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) + { + return new GridItemProviderWrapper(peer, (IGridItemProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs index 509ee523aec..279dd1ff196 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs @@ -6,53 +6,42 @@ using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class GridProviderWrapper : MarshalByRefObject, IGridProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class GridProviderWrapper : MarshalByRefObject, IGridProvider + private readonly AutomationPeer _peer; + private readonly IGridProvider _iface; + + private GridProviderWrapper(AutomationPeer peer, IGridProvider iface) + { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + + _peer = peer; + _iface = iface; + } + + public IRawElementProviderSimple GetItem(int row, int column) + { + return ElementUtil.Invoke(_peer, static (state, rowColumn) => state.GetItem(rowColumn[0], rowColumn[1]), _iface, new int[] { row, column }); + } + + public int RowCount + { + get => ElementUtil.Invoke(_peer, static (state) => state.RowCount, _iface); + } + + public int ColumnCount + { + get => ElementUtil.Invoke(_peer, static (state) => state.ColumnCount, _iface); + } + + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) { - private readonly AutomationPeer _peer; - private readonly IGridProvider _iface; - - private GridProviderWrapper(AutomationPeer peer, IGridProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); - - _peer = peer; - _iface = iface; - } - - public IRawElementProviderSimple GetItem(int row, int column) - { - return ElementUtil.Invoke(_peer, static (state, rowColumn) => state.GetItem(rowColumn[0], rowColumn[1]), _iface, new int[] { row, column }); - } - - public int RowCount - { - get => ElementUtil.Invoke(_peer, static (state) => state.RowCount, _iface); - } - - public int ColumnCount - { - get => ElementUtil.Invoke(_peer, static (state) => state.ColumnCount, _iface); - } - - internal static object Wrap(AutomationPeer peer, object iface) - { - return new GridProviderWrapper(peer, (IGridProvider)iface); - } + return new GridProviderWrapper(peer, (IGridProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs index 862b1e9cdb3..7ccf95c45ee 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs @@ -1,55 +1,37 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Invoke pattern provider wrapper for WCP -// -// - #nullable enable using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class InvokeProviderWrapper : MarshalByRefObject, IInvokeProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class InvokeProviderWrapper : MarshalByRefObject, IInvokeProvider + private readonly AutomationPeer _peer; + private readonly IInvokeProvider _iface; + + private InvokeProviderWrapper(AutomationPeer peer, IInvokeProvider iface) + { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + + _peer = peer; + _iface = iface; + } + + public void Invoke() + { + ElementUtil.Invoke(_peer, static (state) => state.Invoke(), _iface); + } + + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) { - private readonly AutomationPeer _peer; - private readonly IInvokeProvider _iface; - - private InvokeProviderWrapper(AutomationPeer peer, IInvokeProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); - - _peer = peer; - _iface = iface; - } - - public void Invoke() - { - ElementUtil.Invoke(_peer, static (state) => state.Invoke(), _iface); - } - - internal static object Wrap(AutomationPeer peer, object iface) - { - return new InvokeProviderWrapper(peer, (IInvokeProvider)iface); - } + return new InvokeProviderWrapper(peer, (IInvokeProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs index 84fc5f4ea95..37fe92593ef 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs @@ -1,67 +1,49 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Item Container pattern provider wrapper for WPF -// -// - #nullable enable using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class ItemContainerProviderWrapper : MarshalByRefObject, IItemContainerProvider { - // Automation/WPF Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WPF AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class ItemContainerProviderWrapper : MarshalByRefObject, IItemContainerProvider + private readonly AutomationPeer _peer; + private readonly IItemContainerProvider _iface; + + private ItemContainerProviderWrapper(AutomationPeer peer, IItemContainerProvider iface) { - private readonly AutomationPeer _peer; - private readonly IItemContainerProvider _iface; + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); - private ItemContainerProviderWrapper(AutomationPeer peer, IItemContainerProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); + _peer = peer; + _iface = iface; + } - _peer = peer; - _iface = iface; - } + public IRawElementProviderSimple FindItemByProperty(IRawElementProviderSimple startAfter, int propertyId, object value) + { + object[] args = [startAfter, propertyId, value]; - public IRawElementProviderSimple FindItemByProperty(IRawElementProviderSimple startAfter, int propertyId, object value) + // The actual invocation method that gets called on the peer's context. + static IRawElementProviderSimple FindItemByProperty(IItemContainerProvider state, object[] args) { - object[] args = [startAfter, propertyId, value]; + IRawElementProviderSimple startAfter = (IRawElementProviderSimple)args[0]; + int propertyId = (int)args[1]; + object value = args[2]; - // The actual invocation method that gets called on the peer's context. - static IRawElementProviderSimple FindItemByProperty(IItemContainerProvider state, object[] args) - { - IRawElementProviderSimple startAfter = (IRawElementProviderSimple)args[0]; - int propertyId = (int)args[1]; - object value = args[2]; - - return state.FindItemByProperty(startAfter, propertyId, value); - } - - return ElementUtil.Invoke(_peer, FindItemByProperty, _iface, args); + return state.FindItemByProperty(startAfter, propertyId, value); } - internal static object Wrap(AutomationPeer peer, object iface) - { - return new ItemContainerProviderWrapper(peer, (IItemContainerProvider)iface); - } + return ElementUtil.Invoke(_peer, FindItemByProperty, _iface, args); + } + + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) + { + return new ItemContainerProviderWrapper(peer, (IItemContainerProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs index 34a60724b24..b8cb3614df6 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs @@ -1,70 +1,52 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Multiple View pattern provider wrapper for WCP -// -// - #nullable enable using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class MultipleViewProviderWrapper : MarshalByRefObject, IMultipleViewProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class MultipleViewProviderWrapper : MarshalByRefObject, IMultipleViewProvider - { - private readonly AutomationPeer _peer; - private readonly IMultipleViewProvider _iface; + private readonly AutomationPeer _peer; + private readonly IMultipleViewProvider _iface; - private MultipleViewProviderWrapper(AutomationPeer peer, IMultipleViewProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); + private MultipleViewProviderWrapper(AutomationPeer peer, IMultipleViewProvider iface) + { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); - _peer = peer; - _iface = iface; - } + _peer = peer; + _iface = iface; + } - public string GetViewName(int viewID) - { - return ElementUtil.Invoke(_peer, static (state, viewID) => state.GetViewName(viewID), _iface, viewID); - } + public string GetViewName(int viewID) + { + return ElementUtil.Invoke(_peer, static (state, viewID) => state.GetViewName(viewID), _iface, viewID); + } - public void SetCurrentView(int viewID) - { - ElementUtil.Invoke(_peer, static (state, viewID) => state.SetCurrentView(viewID), _iface, viewID); - } + public void SetCurrentView(int viewID) + { + ElementUtil.Invoke(_peer, static (state, viewID) => state.SetCurrentView(viewID), _iface, viewID); + } - public int CurrentView - { - get => ElementUtil.Invoke(_peer, static (state) => state.CurrentView, _iface); - } + public int CurrentView + { + get => ElementUtil.Invoke(_peer, static (state) => state.CurrentView, _iface); + } - public int[] GetSupportedViews() - { - return ElementUtil.Invoke(_peer, static (state) => state.GetSupportedViews(), _iface); - } + public int[] GetSupportedViews() + { + return ElementUtil.Invoke(_peer, static (state) => state.GetSupportedViews(), _iface); + } - internal static object Wrap(AutomationPeer peer, object iface) - { - return new MultipleViewProviderWrapper(peer, (IMultipleViewProvider)iface); - } + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) + { + return new MultipleViewProviderWrapper(peer, (IMultipleViewProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs index 0428d008133..e277e85a4df 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs @@ -1,85 +1,67 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Range Value pattern provider wrapper for WCP -// -// - #nullable enable using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class RangeValueProviderWrapper : MarshalByRefObject, IRangeValueProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class RangeValueProviderWrapper : MarshalByRefObject, IRangeValueProvider - { - private readonly AutomationPeer _peer; - private readonly IRangeValueProvider _iface; + private readonly AutomationPeer _peer; + private readonly IRangeValueProvider _iface; - private RangeValueProviderWrapper(AutomationPeer peer, IRangeValueProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); + private RangeValueProviderWrapper(AutomationPeer peer, IRangeValueProvider iface) + { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); - _peer = peer; - _iface = iface; - } + _peer = peer; + _iface = iface; + } - public void SetValue(double val) - { - ElementUtil.Invoke(_peer, static (state, val) => state.SetValue(val), _iface, val); - } + public void SetValue(double val) + { + ElementUtil.Invoke(_peer, static (state, val) => state.SetValue(val), _iface, val); + } - public double Value - { - get => ElementUtil.Invoke(_peer, static (state) => state.Value, _iface); - } + public double Value + { + get => ElementUtil.Invoke(_peer, static (state) => state.Value, _iface); + } - public bool IsReadOnly - { - get => ElementUtil.Invoke(_peer, static (state) => state.IsReadOnly, _iface); - } + public bool IsReadOnly + { + get => ElementUtil.Invoke(_peer, static (state) => state.IsReadOnly, _iface); + } - public double Maximum - { - get => ElementUtil.Invoke(_peer, static (state) => state.Maximum, _iface); - } + public double Maximum + { + get => ElementUtil.Invoke(_peer, static (state) => state.Maximum, _iface); + } - public double Minimum - { - get => ElementUtil.Invoke(_peer, static (state) => state.Minimum, _iface); - } + public double Minimum + { + get => ElementUtil.Invoke(_peer, static (state) => state.Minimum, _iface); + } - public double LargeChange - { - get => ElementUtil.Invoke(_peer, static (state) => state.LargeChange, _iface); - } + public double LargeChange + { + get => ElementUtil.Invoke(_peer, static (state) => state.LargeChange, _iface); + } - public double SmallChange - { - get => ElementUtil.Invoke(_peer, static (state) => state.SmallChange, _iface); - } + public double SmallChange + { + get => ElementUtil.Invoke(_peer, static (state) => state.SmallChange, _iface); + } - internal static object Wrap(AutomationPeer peer, object iface) - { - return new RangeValueProviderWrapper(peer, (IRangeValueProvider)iface); - } + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) + { + return new RangeValueProviderWrapper(peer, (IRangeValueProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs index b1de17e564c..87fa4bcb892 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs @@ -1,55 +1,37 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Scroll Item pattern provider wrapper for WCP -// -// - #nullable enable using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class ScrollItemProviderWrapper : MarshalByRefObject, IScrollItemProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class ScrollItemProviderWrapper : MarshalByRefObject, IScrollItemProvider + private readonly AutomationPeer _peer; + private readonly IScrollItemProvider _iface; + + private ScrollItemProviderWrapper(AutomationPeer peer, IScrollItemProvider iface) + { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + + _peer = peer; + _iface = iface; + } + + public void ScrollIntoView() + { + ElementUtil.Invoke(_peer, static (state) => state.ScrollIntoView(), _iface); + } + + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) { - private readonly AutomationPeer _peer; - private readonly IScrollItemProvider _iface; - - private ScrollItemProviderWrapper(AutomationPeer peer, IScrollItemProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); - - _peer = peer; - _iface = iface; - } - - public void ScrollIntoView() - { - ElementUtil.Invoke(_peer, static (state) => state.ScrollIntoView(), _iface); - } - - internal static object Wrap(AutomationPeer peer, object iface) - { - return new ScrollItemProviderWrapper(peer, (IScrollItemProvider)iface); - } + return new ScrollItemProviderWrapper(peer, (IScrollItemProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs index 4d4a7a58c4e..093e975c269 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs @@ -1,92 +1,73 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Scroll pattern provider wrapper for WCP -// -// - #nullable enable using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class ScrollProviderWrapper : MarshalByRefObject, IScrollProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. + private readonly AutomationPeer _peer; + private readonly IScrollProvider _iface; - internal sealed class ScrollProviderWrapper : MarshalByRefObject, IScrollProvider + private ScrollProviderWrapper(AutomationPeer peer, IScrollProvider iface) { - private readonly AutomationPeer _peer; - private readonly IScrollProvider _iface; - - private ScrollProviderWrapper(AutomationPeer peer, IScrollProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); - _peer = peer; - _iface = iface; - } + _peer = peer; + _iface = iface; + } - public void Scroll(ScrollAmount horizontalAmount, ScrollAmount verticalAmount) - { - ElementUtil.Invoke(_peer, static (state, values) => state.Scroll(values[0], values[1]), _iface, new ScrollAmount[] { horizontalAmount, verticalAmount }); - } + public void Scroll(ScrollAmount horizontalAmount, ScrollAmount verticalAmount) + { + ElementUtil.Invoke(_peer, static (state, values) => state.Scroll(values[0], values[1]), _iface, new ScrollAmount[] { horizontalAmount, verticalAmount }); + } - public void SetScrollPercent(double horizontalPercent, double verticalPercent) - { - ElementUtil.Invoke(_peer, static (state, values) => state.SetScrollPercent(values[0], values[1]), _iface, new double[] { horizontalPercent, verticalPercent }); - } + public void SetScrollPercent(double horizontalPercent, double verticalPercent) + { + ElementUtil.Invoke(_peer, static (state, values) => state.SetScrollPercent(values[0], values[1]), _iface, new double[] { horizontalPercent, verticalPercent }); + } - public double HorizontalScrollPercent - { - get => ElementUtil.Invoke(_peer, static (state) => state.HorizontalScrollPercent, _iface); - } + public double HorizontalScrollPercent + { + get => ElementUtil.Invoke(_peer, static (state) => state.HorizontalScrollPercent, _iface); + } - public double VerticalScrollPercent - { - get => ElementUtil.Invoke(_peer, static (state) => state.VerticalScrollPercent, _iface); - } + public double VerticalScrollPercent + { + get => ElementUtil.Invoke(_peer, static (state) => state.VerticalScrollPercent, _iface); + } - public double HorizontalViewSize - { - get => ElementUtil.Invoke(_peer, static (state) => state.HorizontalViewSize, _iface); - } + public double HorizontalViewSize + { + get => ElementUtil.Invoke(_peer, static (state) => state.HorizontalViewSize, _iface); + } - public double VerticalViewSize - { - get => ElementUtil.Invoke(_peer, static (state) => state.VerticalViewSize, _iface); - } + public double VerticalViewSize + { + get => ElementUtil.Invoke(_peer, static (state) => state.VerticalViewSize, _iface); + } - public bool HorizontallyScrollable - { - get => ElementUtil.Invoke(_peer, static (state) => state.HorizontallyScrollable, _iface); - } + public bool HorizontallyScrollable + { + get => ElementUtil.Invoke(_peer, static (state) => state.HorizontallyScrollable, _iface); + } - public bool VerticallyScrollable - { - get => ElementUtil.Invoke(_peer, static (state) => state.VerticallyScrollable, _iface); - } + public bool VerticallyScrollable + { + get => ElementUtil.Invoke(_peer, static (state) => state.VerticallyScrollable, _iface); + } - internal static object Wrap(AutomationPeer peer, object iface) - { - return new ScrollProviderWrapper(peer, (IScrollProvider)iface); - } + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) + { + return new ScrollProviderWrapper(peer, (IScrollProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs index 09e79342e6c..ccef048ba46 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs @@ -1,76 +1,57 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Selection Item pattern provider wrapper for WCP -// -// - #nullable enable using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class SelectionItemProviderWrapper : MarshalByRefObject, ISelectionItemProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. + private readonly AutomationPeer _peer; + private readonly ISelectionItemProvider _iface; - internal sealed class SelectionItemProviderWrapper : MarshalByRefObject, ISelectionItemProvider + private SelectionItemProviderWrapper(AutomationPeer peer, ISelectionItemProvider iface) { - private readonly AutomationPeer _peer; - private readonly ISelectionItemProvider _iface; + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); - private SelectionItemProviderWrapper(AutomationPeer peer, ISelectionItemProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); - - _peer = peer; - _iface = iface; - } + _peer = peer; + _iface = iface; + } - public void Select() - { - ElementUtil.Invoke(_peer, static (state) => state.Select(), _iface); - } + public void Select() + { + ElementUtil.Invoke(_peer, static (state) => state.Select(), _iface); + } - public void AddToSelection() - { - ElementUtil.Invoke(_peer, static (state) => state.AddToSelection(), _iface); - } + public void AddToSelection() + { + ElementUtil.Invoke(_peer, static (state) => state.AddToSelection(), _iface); + } - public void RemoveFromSelection() - { - ElementUtil.Invoke(_peer, static (state) => state.RemoveFromSelection(), _iface); - } + public void RemoveFromSelection() + { + ElementUtil.Invoke(_peer, static (state) => state.RemoveFromSelection(), _iface); + } - public bool IsSelected - { - get => ElementUtil.Invoke(_peer, static (state) => state.IsSelected, _iface); - } + public bool IsSelected + { + get => ElementUtil.Invoke(_peer, static (state) => state.IsSelected, _iface); + } - public IRawElementProviderSimple SelectionContainer - { - get => ElementUtil.Invoke(_peer, static (state) => state.SelectionContainer, _iface); - } + public IRawElementProviderSimple SelectionContainer + { + get => ElementUtil.Invoke(_peer, static (state) => state.SelectionContainer, _iface); + } - internal static object Wrap(AutomationPeer peer, object iface) - { - return new SelectionItemProviderWrapper(peer, (ISelectionItemProvider)iface); - } + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) + { + return new SelectionItemProviderWrapper(peer, (ISelectionItemProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs index db10dd720b6..812be89d4fe 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs @@ -1,65 +1,47 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Selection pattern provider wrapper for WCP -// -// - #nullable enable using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class SelectionProviderWrapper : MarshalByRefObject, ISelectionProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class SelectionProviderWrapper : MarshalByRefObject, ISelectionProvider - { - private readonly AutomationPeer _peer; - private readonly ISelectionProvider _iface; + private readonly AutomationPeer _peer; + private readonly ISelectionProvider _iface; - private SelectionProviderWrapper(AutomationPeer peer, ISelectionProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); + private SelectionProviderWrapper(AutomationPeer peer, ISelectionProvider iface) + { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); - _peer = peer; - _iface = iface; - } + _peer = peer; + _iface = iface; + } - public IRawElementProviderSimple[] GetSelection() - { - return ElementUtil.Invoke(_peer, static (state) => state.GetSelection(), _iface); - } + public IRawElementProviderSimple[] GetSelection() + { + return ElementUtil.Invoke(_peer, static (state) => state.GetSelection(), _iface); + } - public bool CanSelectMultiple - { - get => ElementUtil.Invoke(_peer, static (state) => state.CanSelectMultiple, _iface); - } + public bool CanSelectMultiple + { + get => ElementUtil.Invoke(_peer, static (state) => state.CanSelectMultiple, _iface); + } - public bool IsSelectionRequired - { - get => ElementUtil.Invoke(_peer, static (state) => state.IsSelectionRequired, _iface); - } + public bool IsSelectionRequired + { + get => ElementUtil.Invoke(_peer, static (state) => state.IsSelectionRequired, _iface); + } - internal static object Wrap(AutomationPeer peer, object iface) - { - return new SelectionProviderWrapper(peer, (ISelectionProvider)iface); - } + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) + { + return new SelectionProviderWrapper(peer, (ISelectionProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs index 88f3839a9ad..311e9486493 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs @@ -1,61 +1,43 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: SynchronizedInput pattern provider wrapper for WCP -// -// - #nullable enable using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class SynchronizedInputProviderWrapper : MarshalByRefObject, ISynchronizedInputProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class SynchronizedInputProviderWrapper : MarshalByRefObject, ISynchronizedInputProvider + private readonly AutomationPeer _peer; + private readonly ISynchronizedInputProvider _iface; + + private SynchronizedInputProviderWrapper(AutomationPeer peer, ISynchronizedInputProvider iface) + { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + + _peer = peer; + _iface = iface; + } + + public void StartListening(SynchronizedInputType inputType) + { + ElementUtil.Invoke(_peer, static (state, inputType) => state.StartListening(inputType), _iface, inputType); + } + + public void Cancel() + { + ElementUtil.Invoke(_peer, static (state) => state.Cancel(), _iface); + } + + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) { - private readonly AutomationPeer _peer; - private readonly ISynchronizedInputProvider _iface; - - private SynchronizedInputProviderWrapper(AutomationPeer peer, ISynchronizedInputProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); - - _peer = peer; - _iface = iface; - } - - public void StartListening(SynchronizedInputType inputType) - { - ElementUtil.Invoke(_peer, static (state, inputType) => state.StartListening(inputType), _iface, inputType); - } - - public void Cancel() - { - ElementUtil.Invoke(_peer, static (state) => state.Cancel(), _iface); - } - - internal static object Wrap(AutomationPeer peer, object iface) - { - return new SynchronizedInputProviderWrapper(peer, (ISynchronizedInputProvider)iface); - } + return new SynchronizedInputProviderWrapper(peer, (ISynchronizedInputProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs index bb756f0331c..d9401e988b8 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs @@ -1,85 +1,67 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Table Item pattern provider wrapper for WCP -// -// - #nullable enable using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class TableItemProviderWrapper : MarshalByRefObject, ITableItemProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class TableItemProviderWrapper : MarshalByRefObject, ITableItemProvider - { - private readonly AutomationPeer _peer; - private readonly ITableItemProvider _iface; + private readonly AutomationPeer _peer; + private readonly ITableItemProvider _iface; - private TableItemProviderWrapper(AutomationPeer peer, ITableItemProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); + private TableItemProviderWrapper(AutomationPeer peer, ITableItemProvider iface) + { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); - _peer = peer; - _iface = iface; - } + _peer = peer; + _iface = iface; + } - public int Row - { - get => ElementUtil.Invoke(_peer, static (state) => state.Row, _iface); - } + public int Row + { + get => ElementUtil.Invoke(_peer, static (state) => state.Row, _iface); + } - public int Column - { - get => ElementUtil.Invoke(_peer, static (state) => state.Column, _iface); - } + public int Column + { + get => ElementUtil.Invoke(_peer, static (state) => state.Column, _iface); + } - public int RowSpan - { - get => ElementUtil.Invoke(_peer, static (state) => state.RowSpan, _iface); - } + public int RowSpan + { + get => ElementUtil.Invoke(_peer, static (state) => state.RowSpan, _iface); + } - public int ColumnSpan - { - get => ElementUtil.Invoke(_peer, static (state) => state.ColumnSpan, _iface); - } + public int ColumnSpan + { + get => ElementUtil.Invoke(_peer, static (state) => state.ColumnSpan, _iface); + } - public IRawElementProviderSimple ContainingGrid - { - get => ElementUtil.Invoke(_peer, static (state) => state.ContainingGrid, _iface); - } + public IRawElementProviderSimple ContainingGrid + { + get => ElementUtil.Invoke(_peer, static (state) => state.ContainingGrid, _iface); + } - public IRawElementProviderSimple[] GetRowHeaderItems() - { - return ElementUtil.Invoke(_peer, static (state) => state.GetRowHeaderItems(), _iface); - } + public IRawElementProviderSimple[] GetRowHeaderItems() + { + return ElementUtil.Invoke(_peer, static (state) => state.GetRowHeaderItems(), _iface); + } - public IRawElementProviderSimple[] GetColumnHeaderItems() - { - return ElementUtil.Invoke(_peer, static (state) => state.GetColumnHeaderItems(), _iface); - } + public IRawElementProviderSimple[] GetColumnHeaderItems() + { + return ElementUtil.Invoke(_peer, static (state) => state.GetColumnHeaderItems(), _iface); + } - internal static object Wrap(AutomationPeer peer, object iface) - { - return new TableItemProviderWrapper(peer, (ITableItemProvider)iface); - } + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) + { + return new TableItemProviderWrapper(peer, (ITableItemProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs index 5765070ca9d..c33c0b4e024 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs @@ -1,81 +1,63 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Table pattern provider wrapper for WCP -// -// - #nullable enable using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class TableProviderWrapper : MarshalByRefObject, ITableProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class TableProviderWrapper : MarshalByRefObject, ITableProvider - { - private readonly AutomationPeer _peer; - private readonly ITableProvider _iface; + private readonly AutomationPeer _peer; + private readonly ITableProvider _iface; - private TableProviderWrapper(AutomationPeer peer, ITableProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); + private TableProviderWrapper(AutomationPeer peer, ITableProvider iface) + { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); - _peer = peer; - _iface = iface; - } + _peer = peer; + _iface = iface; + } - public IRawElementProviderSimple GetItem(int row, int column) - { - return ElementUtil.Invoke(_peer, static (state, rowColumn) => state.GetItem(rowColumn[0], rowColumn[1]), _iface, new int[] { row, column }); - } + public IRawElementProviderSimple GetItem(int row, int column) + { + return ElementUtil.Invoke(_peer, static (state, rowColumn) => state.GetItem(rowColumn[0], rowColumn[1]), _iface, new int[] { row, column }); + } - public int RowCount - { - get => ElementUtil.Invoke(_peer, static (state) => state.RowCount, _iface); - } + public int RowCount + { + get => ElementUtil.Invoke(_peer, static (state) => state.RowCount, _iface); + } - public int ColumnCount - { - get => ElementUtil.Invoke(_peer, static (state) => state.ColumnCount, _iface); - } + public int ColumnCount + { + get => ElementUtil.Invoke(_peer, static (state) => state.ColumnCount, _iface); + } - public IRawElementProviderSimple[] GetRowHeaders() - { - return ElementUtil.Invoke(_peer, static (state) => state.GetRowHeaders(), _iface); - } + public IRawElementProviderSimple[] GetRowHeaders() + { + return ElementUtil.Invoke(_peer, static (state) => state.GetRowHeaders(), _iface); + } - public IRawElementProviderSimple[] GetColumnHeaders() - { - return ElementUtil.Invoke(_peer, static (state) => state.GetColumnHeaders(), _iface); - } + public IRawElementProviderSimple[] GetColumnHeaders() + { + return ElementUtil.Invoke(_peer, static (state) => state.GetColumnHeaders(), _iface); + } - public RowOrColumnMajor RowOrColumnMajor - { - get => ElementUtil.Invoke(_peer, static (state) => state.RowOrColumnMajor, _iface); - } + public RowOrColumnMajor RowOrColumnMajor + { + get => ElementUtil.Invoke(_peer, static (state) => state.RowOrColumnMajor, _iface); + } - internal static object Wrap(AutomationPeer peer, object iface) - { - return new TableProviderWrapper(peer, (ITableProvider)iface); - } + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) + { + return new TableProviderWrapper(peer, (ITableProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs index 1ffef63acc5..3eb2f80b872 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// Description: Text pattern provider wrapper for WCP - using System.Windows; using System.Windows.Automation; using System.Windows.Automation.Provider; @@ -10,72 +8,73 @@ #nullable enable -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class TextProviderWrapper : MarshalByRefObject, ITextProvider { - // see comment on InvokeProviderWrapper class for explanation of purpose and organization of these wrapper classes. - internal sealed class TextProviderWrapper : MarshalByRefObject, ITextProvider + private readonly AutomationPeer _peer; + private readonly ITextProvider _iface; + + private TextProviderWrapper(AutomationPeer peer, ITextProvider iface) { - private readonly AutomationPeer _peer; - private readonly ITextProvider _iface; + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); - private TextProviderWrapper(AutomationPeer peer, ITextProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); + _peer = peer; + _iface = iface; + } - _peer = peer; - _iface = iface; - } + public ITextRangeProvider[] GetSelection() + { + return ElementUtil.Invoke(_peer, static (state, peer) => TextRangeProviderWrapper.WrapArgument(state.GetSelection(), peer), _iface, _peer); + } - public ITextRangeProvider[] GetSelection() - { - return ElementUtil.Invoke(_peer, static (state, peer) => TextRangeProviderWrapper.WrapArgument(state.GetSelection(), peer), _iface, _peer); - } + public ITextRangeProvider[] GetVisibleRanges() + { + return ElementUtil.Invoke(_peer, static (state, peer) => TextRangeProviderWrapper.WrapArgument(state.GetVisibleRanges(), peer), _iface, _peer); + } - public ITextRangeProvider[] GetVisibleRanges() - { - return ElementUtil.Invoke(_peer, static (state, peer) => TextRangeProviderWrapper.WrapArgument(state.GetVisibleRanges(), peer), _iface, _peer); - } + public ITextRangeProvider RangeFromChild(IRawElementProviderSimple childElement) + { + if (childElement is not ElementProxy) + throw new ArgumentException(SR.Format(SR.TextProvider_InvalidChild, nameof(childElement))); - public ITextRangeProvider RangeFromChild(IRawElementProviderSimple childElement) + // The actual invocation method that gets called on the peer's context. + static ITextRangeProvider RangeFromChild(TextProviderWrapper state, IRawElementProviderSimple childElement) { - if (childElement is not ElementProxy) - throw new ArgumentException(SR.Format(SR.TextProvider_InvalidChild, nameof(childElement))); - - // The actual invocation method that gets called on the peer's context. - static ITextRangeProvider RangeFromChild(TextProviderWrapper state, IRawElementProviderSimple childElement) - { - return TextRangeProviderWrapper.WrapArgument(state._iface.RangeFromChild(childElement), state._peer); - } - - return ElementUtil.Invoke(_peer, RangeFromChild, this, childElement); + return TextRangeProviderWrapper.WrapArgument(state._iface.RangeFromChild(childElement), state._peer); } - public ITextRangeProvider RangeFromPoint(Point screenLocation) - { - // The actual invocation method that gets called on the peer's context. - static ITextRangeProvider RangeFromPoint(TextProviderWrapper state, Point screenLocation) - { - return TextRangeProviderWrapper.WrapArgument(state._iface.RangeFromPoint(screenLocation), state._peer); - } - - return ElementUtil.Invoke(_peer, RangeFromPoint, this, screenLocation); - } + return ElementUtil.Invoke(_peer, RangeFromChild, this, childElement); + } - public ITextRangeProvider DocumentRange + public ITextRangeProvider RangeFromPoint(Point screenLocation) + { + // The actual invocation method that gets called on the peer's context. + static ITextRangeProvider RangeFromPoint(TextProviderWrapper state, Point screenLocation) { - get => ElementUtil.Invoke(_peer, static (state, peer) => TextRangeProviderWrapper.WrapArgument(state.DocumentRange, peer), _iface, _peer); + return TextRangeProviderWrapper.WrapArgument(state._iface.RangeFromPoint(screenLocation), state._peer); } - public SupportedTextSelection SupportedTextSelection - { - get => ElementUtil.Invoke(_peer, static (state) => state.SupportedTextSelection, _iface); - } + return ElementUtil.Invoke(_peer, RangeFromPoint, this, screenLocation); + } - internal static object Wrap(AutomationPeer peer, object iface) - { - return new TextProviderWrapper(peer, (ITextProvider)iface); - } + public ITextRangeProvider DocumentRange + { + get => ElementUtil.Invoke(_peer, static (state, peer) => TextRangeProviderWrapper.WrapArgument(state.DocumentRange, peer), _iface, _peer); + } + + public SupportedTextSelection SupportedTextSelection + { + get => ElementUtil.Invoke(_peer, static (state) => state.SupportedTextSelection, _iface); + } + + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) + { + return new TextProviderWrapper(peer, (ITextProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs index b67b4dae8a5..0125863ad2d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// Description: TextRange provider wrapper for WCP - using System.Windows.Automation.Provider; using System.Windows.Automation.Text; using System.Windows.Automation.Peers; @@ -10,230 +8,230 @@ #nullable enable -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class TextRangeProviderWrapper : MarshalByRefObject, ITextRangeProvider { - // see comment on InvokeProviderWrapper class for explanation of purpose and organization of these wrapper classes. - internal sealed class TextRangeProviderWrapper : MarshalByRefObject, ITextRangeProvider + private readonly AutomationPeer _peer; + private readonly ITextRangeProvider _iface; + + private TextRangeProviderWrapper(AutomationPeer peer, ITextRangeProvider iface) { - private readonly AutomationPeer _peer; - private readonly ITextRangeProvider _iface; + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); - private TextRangeProviderWrapper(AutomationPeer peer, ITextRangeProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); + _peer = peer; + _iface = iface; + } - _peer = peer; - _iface = iface; - } + public ITextRangeProvider Clone() + { + return ElementUtil.Invoke(_peer, static (state, peer) => WrapArgument(state.Clone(), peer), _iface, _peer); + } - public ITextRangeProvider Clone() - { - return ElementUtil.Invoke(_peer, static (state, peer) => WrapArgument(state.Clone(), peer), _iface, _peer); - } + public bool Compare(ITextRangeProvider range) + { + if (range is not TextRangeProviderWrapper) + throw new ArgumentException(SR.Format(SR.TextRangeProvider_InvalidRangeProvider, nameof(range))); - public bool Compare(ITextRangeProvider range) - { - if (range is not TextRangeProviderWrapper) - throw new ArgumentException(SR.Format(SR.TextRangeProvider_InvalidRangeProvider, nameof(range))); + // Note: We always need to unwrap the range argument here. + return ElementUtil.Invoke(_peer, static (state, range) => state.Compare(UnwrapArgument(range)), _iface, range); + } - // Note: We always need to unwrap the range argument here. - return ElementUtil.Invoke(_peer, static (state, range) => state.Compare(UnwrapArgument(range)), _iface, range); - } + public int CompareEndpoints(TextPatternRangeEndpoint endpoint, ITextRangeProvider targetRange, TextPatternRangeEndpoint targetEndpoint) + { + if (targetRange is not TextRangeProviderWrapper) + throw new ArgumentException(SR.Format(SR.TextRangeProvider_InvalidRangeProvider, nameof(targetRange))); + + object[] args = [endpoint, targetRange, targetEndpoint]; - public int CompareEndpoints(TextPatternRangeEndpoint endpoint, ITextRangeProvider targetRange, TextPatternRangeEndpoint targetEndpoint) + // The actual invocation method that gets called on the peer's context. + static int CompareEndpoints(ITextRangeProvider state, object[] args) { - if (targetRange is not TextRangeProviderWrapper) - throw new ArgumentException(SR.Format(SR.TextRangeProvider_InvalidRangeProvider, nameof(targetRange))); + TextPatternRangeEndpoint endpoint = (TextPatternRangeEndpoint)args[0]; + ITextRangeProvider targetRange = (ITextRangeProvider)args[1]; + TextPatternRangeEndpoint targetEndpoint = (TextPatternRangeEndpoint)args[2]; - object[] args = [endpoint, targetRange, targetEndpoint]; + return state.CompareEndpoints(endpoint, UnwrapArgument(targetRange), targetEndpoint); + } - // The actual invocation method that gets called on the peer's context. - static int CompareEndpoints(ITextRangeProvider state, object[] args) - { - TextPatternRangeEndpoint endpoint = (TextPatternRangeEndpoint)args[0]; - ITextRangeProvider targetRange = (ITextRangeProvider)args[1]; - TextPatternRangeEndpoint targetEndpoint = (TextPatternRangeEndpoint)args[2]; + return ElementUtil.Invoke(_peer, CompareEndpoints, _iface, args); + } - return state.CompareEndpoints(endpoint, UnwrapArgument(targetRange), targetEndpoint); - } + public void ExpandToEnclosingUnit(TextUnit unit) + { + ElementUtil.Invoke(_peer, static (state, unit) => state.ExpandToEnclosingUnit(unit), _iface, unit); + } - return ElementUtil.Invoke(_peer, CompareEndpoints, _iface, args); - } + public ITextRangeProvider? FindAttribute(int attribute, object val, bool backward) + { + object[] args = [attribute, val, backward]; - public void ExpandToEnclosingUnit(TextUnit unit) + // The actual invocation method that gets called on the peer's context. + static ITextRangeProvider? FindAttribute(TextRangeProviderWrapper state, object[] args) { - ElementUtil.Invoke(_peer, static (state, unit) => state.ExpandToEnclosingUnit(unit), _iface, unit); + int attribute = (int)args[0]; + object val = args[1]; + bool backward = (bool)args[2]; + + return WrapArgument(state._iface.FindAttribute(attribute, val, backward), state._peer); } - public ITextRangeProvider? FindAttribute(int attribute, object val, bool backward) - { - object[] args = [attribute, val, backward]; + return ElementUtil.Invoke(_peer, FindAttribute, this, args); + } - // The actual invocation method that gets called on the peer's context. - static ITextRangeProvider? FindAttribute(TextRangeProviderWrapper state, object[] args) - { - int attribute = (int)args[0]; - object val = args[1]; - bool backward = (bool)args[2]; + public ITextRangeProvider? FindText(string text, bool backward, bool ignoreCase) + { + object[] args = [text, backward, ignoreCase]; - return WrapArgument(state._iface.FindAttribute(attribute, val, backward), state._peer); - } + // The actual invocation method that gets called on the peer's context. + static ITextRangeProvider? FindText(TextRangeProviderWrapper state, object[] args) + { + string text = (string)args[0]; + bool backward = (bool)args[1]; + bool ignoreCase = (bool)args[2]; - return ElementUtil.Invoke(_peer, FindAttribute, this, args); + return WrapArgument(state._iface.FindText(text, backward, ignoreCase), state._peer); } - public ITextRangeProvider? FindText(string text, bool backward, bool ignoreCase) - { - object[] args = [text, backward, ignoreCase]; + return ElementUtil.Invoke(_peer, FindText, this, args); + } - // The actual invocation method that gets called on the peer's context. - static ITextRangeProvider? FindText(TextRangeProviderWrapper state, object[] args) - { - string text = (string)args[0]; - bool backward = (bool)args[1]; - bool ignoreCase = (bool)args[2]; + public object GetAttributeValue(int attribute) + { + // Note: If an attribute value is ever a range then we'll need to wrap/unwrap it appropriately here. + return ElementUtil.Invoke(_peer, static (state, attribute) => state.GetAttributeValue(attribute), _iface, attribute); + } - return WrapArgument(state._iface.FindText(text, backward, ignoreCase), state._peer); - } + public double[] GetBoundingRectangles() + { + return ElementUtil.Invoke(_peer, static (state) => state.GetBoundingRectangles(), _iface); + } - return ElementUtil.Invoke(_peer, FindText, this, args); - } + public IRawElementProviderSimple GetEnclosingElement() + { + return ElementUtil.Invoke(_peer, static (state) => state.GetEnclosingElement(), _iface); + } - public object GetAttributeValue(int attribute) - { - // Note: If an attribute value is ever a range then we'll need to wrap/unwrap it appropriately here. - return ElementUtil.Invoke(_peer, static (state, attribute) => state.GetAttributeValue(attribute), _iface, attribute); - } + public string GetText(int maxLength) + { + return ElementUtil.Invoke(_peer, static (state, maxLength) => state.GetText(maxLength), _iface, maxLength); + } - public double[] GetBoundingRectangles() - { - return ElementUtil.Invoke(_peer, static (state) => state.GetBoundingRectangles(), _iface); - } + public int Move(TextUnit unit, int count) + { + object[] args = [unit, count]; - public IRawElementProviderSimple GetEnclosingElement() + // The actual invocation method that gets called on the peer's context. + static int Move(ITextRangeProvider state, object[] args) { - return ElementUtil.Invoke(_peer, static (state) => state.GetEnclosingElement(), _iface); - } + TextUnit unit = (TextUnit)args[0]; + int count = (int)args[1]; - public string GetText(int maxLength) - { - return ElementUtil.Invoke(_peer, static (state, maxLength) => state.GetText(maxLength), _iface, maxLength); + return state.Move(unit, count); } - public int Move(TextUnit unit, int count) - { - object[] args = [unit, count]; + return ElementUtil.Invoke(_peer, Move, _iface, args); + } - // The actual invocation method that gets called on the peer's context. - static int Move(ITextRangeProvider state, object[] args) - { - TextUnit unit = (TextUnit)args[0]; - int count = (int)args[1]; + public int MoveEndpointByUnit(TextPatternRangeEndpoint endpoint, TextUnit unit, int count) + { + object[] args = [endpoint, unit, count]; - return state.Move(unit, count); - } + // The actual invocation method that gets called on the peer's context. + static int MoveEndpointByUnit(ITextRangeProvider state, object[] args) + { + TextPatternRangeEndpoint endpoint = (TextPatternRangeEndpoint)args[0]; + TextUnit unit = (TextUnit)args[1]; + int count = (int)args[2]; - return ElementUtil.Invoke(_peer, Move, _iface, args); + return state.MoveEndpointByUnit(endpoint, unit, count); } - public int MoveEndpointByUnit(TextPatternRangeEndpoint endpoint, TextUnit unit, int count) - { - object[] args = [endpoint, unit, count]; - - // The actual invocation method that gets called on the peer's context. - static int MoveEndpointByUnit(ITextRangeProvider state, object[] args) - { - TextPatternRangeEndpoint endpoint = (TextPatternRangeEndpoint)args[0]; - TextUnit unit = (TextUnit)args[1]; - int count = (int)args[2]; + return ElementUtil.Invoke(_peer, MoveEndpointByUnit, _iface, args); + } - return state.MoveEndpointByUnit(endpoint, unit, count); - } + public void MoveEndpointByRange(TextPatternRangeEndpoint endpoint, ITextRangeProvider targetRange, TextPatternRangeEndpoint targetEndpoint) + { + if (targetRange is not TextRangeProviderWrapper) + throw new ArgumentException(SR.Format(SR.TextRangeProvider_InvalidRangeProvider, nameof(targetRange))); - return ElementUtil.Invoke(_peer, MoveEndpointByUnit, _iface, args); - } + object[] args = [endpoint, targetRange, targetEndpoint]; - public void MoveEndpointByRange(TextPatternRangeEndpoint endpoint, ITextRangeProvider targetRange, TextPatternRangeEndpoint targetEndpoint) + // The actual invocation method that gets called on the peer's context. + static void MoveEndpointByRange(ITextRangeProvider state, object[] args) { - if (targetRange is not TextRangeProviderWrapper) - throw new ArgumentException(SR.Format(SR.TextRangeProvider_InvalidRangeProvider, nameof(targetRange))); + TextPatternRangeEndpoint endpoint = (TextPatternRangeEndpoint)args[0]; + ITextRangeProvider targetRange = (ITextRangeProvider)args[1]; + TextPatternRangeEndpoint targetEndpoint = (TextPatternRangeEndpoint)args[2]; - object[] args = [endpoint, targetRange, targetEndpoint]; + state.MoveEndpointByRange(endpoint, UnwrapArgument(targetRange), targetEndpoint); + } - // The actual invocation method that gets called on the peer's context. - static void MoveEndpointByRange(ITextRangeProvider state, object[] args) - { - TextPatternRangeEndpoint endpoint = (TextPatternRangeEndpoint)args[0]; - ITextRangeProvider targetRange = (ITextRangeProvider)args[1]; - TextPatternRangeEndpoint targetEndpoint = (TextPatternRangeEndpoint)args[2]; + ElementUtil.Invoke(_peer, MoveEndpointByRange, _iface, args); + } - state.MoveEndpointByRange(endpoint, UnwrapArgument(targetRange), targetEndpoint); - } + public void Select() + { + ElementUtil.Invoke(_peer, static (state) => state.Select(), _iface); + } - ElementUtil.Invoke(_peer, MoveEndpointByRange, _iface, args); - } + public void AddToSelection() + { + ElementUtil.Invoke(_peer, static (state) => state.AddToSelection(), _iface); + } - public void Select() - { - ElementUtil.Invoke(_peer, static (state) => state.Select(), _iface); - } + public void RemoveFromSelection() + { + ElementUtil.Invoke(_peer, static (state) => state.RemoveFromSelection(), _iface); + } - public void AddToSelection() - { - ElementUtil.Invoke(_peer, static (state) => state.AddToSelection(), _iface); - } + public void ScrollIntoView(bool alignToTop) + { + ElementUtil.Invoke(_peer, static (state, alignToTop) => state.ScrollIntoView(alignToTop), _iface, alignToTop); + } - public void RemoveFromSelection() - { - ElementUtil.Invoke(_peer, static (state) => state.RemoveFromSelection(), _iface); - } + public IRawElementProviderSimple[]? GetChildren() + { + return ElementUtil.Invoke(_peer, static (state) => state.GetChildren(), _iface); + } - public void ScrollIntoView(bool alignToTop) - { - ElementUtil.Invoke(_peer, static (state, alignToTop) => state.ScrollIntoView(alignToTop), _iface, alignToTop); - } + // Wrap arguments that are being returned, assuming they're not null or already wrapped. + [return: NotNullIfNotNull(nameof(argument))] + internal static ITextRangeProvider? WrapArgument(ITextRangeProvider? argument, AutomationPeer peer) + { + if (argument == null) + return null; - public IRawElementProviderSimple[]? GetChildren() - { - return ElementUtil.Invoke(_peer, static (state) => state.GetChildren(), _iface); - } + if (argument is TextRangeProviderWrapper) + return argument; - // Wrap arguments that are being returned, assuming they're not null or already wrapped. - [return: NotNullIfNotNull(nameof(argument))] - internal static ITextRangeProvider? WrapArgument(ITextRangeProvider? argument, AutomationPeer peer) - { - if (argument == null) - return null; + return new TextRangeProviderWrapper(peer, argument); + } - if (argument is TextRangeProviderWrapper) - return argument; + [return: NotNullIfNotNull(nameof(argument))] + internal static ITextRangeProvider[]? WrapArgument(ITextRangeProvider[]? argument, AutomationPeer peer) + { + if (argument == null) + return null; - return new TextRangeProviderWrapper(peer, argument); - } + if (argument is TextRangeProviderWrapper[]) + return argument; - [return: NotNullIfNotNull(nameof(argument))] - internal static ITextRangeProvider[]? WrapArgument(ITextRangeProvider[]? argument, AutomationPeer peer) + ITextRangeProvider[] outArray = new ITextRangeProvider[argument.Length]; + for (int i = 0; i < argument.Length; i++) { - if (argument == null) - return null; - - if (argument is TextRangeProviderWrapper[]) - return argument; - - ITextRangeProvider[] outArray = new ITextRangeProvider[argument.Length]; - for (int i = 0; i < argument.Length; i++) - { - outArray[i] = WrapArgument(argument[i], peer); - } - return outArray; + outArray[i] = WrapArgument(argument[i], peer); } + return outArray; + } - // Remove the wrapper from the argument if a wrapper exists - internal static ITextRangeProvider UnwrapArgument(ITextRangeProvider argument) - { - return argument is TextRangeProviderWrapper wrapper ? wrapper._iface : argument; - } + /// + /// Removes the wrapper from the argument if a wrapper exists, otherwise returns the argument as is. + /// + internal static ITextRangeProvider UnwrapArgument(ITextRangeProvider argument) + { + return argument is TextRangeProviderWrapper wrapper ? wrapper._iface : argument; } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs index 78ac89bc529..0578b6a5323 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs @@ -1,61 +1,43 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Toggle pattern provider wrapper for WCP -// -// - #nullable enable using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class ToggleProviderWrapper : MarshalByRefObject, IToggleProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class ToggleProviderWrapper : MarshalByRefObject, IToggleProvider + private readonly AutomationPeer _peer; + private readonly IToggleProvider _iface; + + private ToggleProviderWrapper(AutomationPeer peer, IToggleProvider iface) + { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + + _peer = peer; + _iface = iface; + } + + public void Toggle() + { + ElementUtil.Invoke(_peer, static (state) => state.Toggle(), _iface); + } + + public ToggleState ToggleState + { + get => ElementUtil.Invoke(_peer, static (state) => state.ToggleState, _iface); + } + + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) { - private readonly AutomationPeer _peer; - private readonly IToggleProvider _iface; - - private ToggleProviderWrapper(AutomationPeer peer, IToggleProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); - - _peer = peer; - _iface = iface; - } - - public void Toggle() - { - ElementUtil.Invoke(_peer, static (state) => state.Toggle(), _iface); - } - - public ToggleState ToggleState - { - get => ElementUtil.Invoke(_peer, static (state) => state.ToggleState, _iface); - } - - internal static object Wrap(AutomationPeer peer, object iface) - { - return new ToggleProviderWrapper(peer, (IToggleProvider)iface); - } + return new ToggleProviderWrapper(peer, (IToggleProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs index 44fb253c696..202676002f2 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs @@ -1,80 +1,62 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Transform pattern provider wrapper for WCP -// -// - #nullable enable using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class TransformProviderWrapper : MarshalByRefObject, ITransformProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class TransformProviderWrapper : MarshalByRefObject, ITransformProvider - { - private readonly AutomationPeer _peer; - private readonly ITransformProvider _iface; + private readonly AutomationPeer _peer; + private readonly ITransformProvider _iface; - private TransformProviderWrapper(AutomationPeer peer, ITransformProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); + private TransformProviderWrapper(AutomationPeer peer, ITransformProvider iface) + { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); - _peer = peer; - _iface = iface; - } + _peer = peer; + _iface = iface; + } - public void Move(double x, double y) - { - ElementUtil.Invoke(_peer, static (state, coordinates) => state.Move(coordinates[0], coordinates[1]), _iface, new double[] { x, y }); - } + public void Move(double x, double y) + { + ElementUtil.Invoke(_peer, static (state, coordinates) => state.Move(coordinates[0], coordinates[1]), _iface, new double[] { x, y }); + } - public void Resize(double width, double height) - { - ElementUtil.Invoke(_peer, static (state, dimensions) => state.Resize(dimensions[0], dimensions[1]), _iface, new double[] { width, height }); - } + public void Resize(double width, double height) + { + ElementUtil.Invoke(_peer, static (state, dimensions) => state.Resize(dimensions[0], dimensions[1]), _iface, new double[] { width, height }); + } - public void Rotate(double degrees) - { - ElementUtil.Invoke(_peer, static (state, degrees) => state.Rotate(degrees), _iface, degrees); - } + public void Rotate(double degrees) + { + ElementUtil.Invoke(_peer, static (state, degrees) => state.Rotate(degrees), _iface, degrees); + } - public bool CanMove - { - get => ElementUtil.Invoke(_peer, static (state) => state.CanMove, _iface); - } + public bool CanMove + { + get => ElementUtil.Invoke(_peer, static (state) => state.CanMove, _iface); + } - public bool CanResize - { - get => ElementUtil.Invoke(_peer, static (state) => state.CanResize, _iface); - } + public bool CanResize + { + get => ElementUtil.Invoke(_peer, static (state) => state.CanResize, _iface); + } - public bool CanRotate - { - get => ElementUtil.Invoke(_peer, static (state) => state.CanRotate, _iface); - } + public bool CanRotate + { + get => ElementUtil.Invoke(_peer, static (state) => state.CanRotate, _iface); + } - internal static object Wrap(AutomationPeer peer, object iface) - { - return new TransformProviderWrapper(peer, (ITransformProvider)iface); - } + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) + { + return new TransformProviderWrapper(peer, (ITransformProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs index 283d932a003..a947e1bda52 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs @@ -1,65 +1,47 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Value pattern provider wrapper for WCP -// -// - #nullable enable using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class ValueProviderWrapper : MarshalByRefObject, IValueProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class ValueProviderWrapper : MarshalByRefObject, IValueProvider - { - private readonly AutomationPeer _peer; - private readonly IValueProvider _iface; + private readonly AutomationPeer _peer; + private readonly IValueProvider _iface; - private ValueProviderWrapper(AutomationPeer peer, IValueProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); + private ValueProviderWrapper(AutomationPeer peer, IValueProvider iface) + { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); - _peer = peer; - _iface = iface; - } + _peer = peer; + _iface = iface; + } - public void SetValue(string val) - { - ElementUtil.Invoke(_peer, static (state, value) => state.SetValue(value), _iface, val); - } + public void SetValue(string val) + { + ElementUtil.Invoke(_peer, static (state, value) => state.SetValue(value), _iface, val); + } - public string Value - { - get => ElementUtil.Invoke(_peer, static (state) => state.Value, _iface); - } + public string Value + { + get => ElementUtil.Invoke(_peer, static (state) => state.Value, _iface); + } - public bool IsReadOnly - { - get => ElementUtil.Invoke(_peer, static (state) => state.IsReadOnly, _iface); - } + public bool IsReadOnly + { + get => ElementUtil.Invoke(_peer, static (state) => state.IsReadOnly, _iface); + } - internal static object Wrap(AutomationPeer peer, object iface) - { - return new ValueProviderWrapper(peer, (IValueProvider)iface); - } + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) + { + return new ValueProviderWrapper(peer, (IValueProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs index 1576eaae8dc..7131671fe53 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs @@ -1,55 +1,37 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Virtualized Item pattern provider wrapper for WPF -// -// - #nullable enable using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class VirtualizedItemProviderWrapper : MarshalByRefObject, IVirtualizedItemProvider { - // Automation/WPF Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WPF AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class VirtualizedItemProviderWrapper : MarshalByRefObject, IVirtualizedItemProvider + private readonly AutomationPeer _peer; + private readonly IVirtualizedItemProvider _iface; + + private VirtualizedItemProviderWrapper(AutomationPeer peer, IVirtualizedItemProvider iface) + { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + + _peer = peer; + _iface = iface; + } + + public void Realize() + { + ElementUtil.Invoke(_peer, static (state) => state.Realize(), _iface); + } + + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) { - private readonly AutomationPeer _peer; - private readonly IVirtualizedItemProvider _iface; - - private VirtualizedItemProviderWrapper(AutomationPeer peer, IVirtualizedItemProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); - - _peer = peer; - _iface = iface; - } - - public void Realize() - { - ElementUtil.Invoke(_peer, static (state) => state.Realize(), _iface); - } - - internal static object Wrap(AutomationPeer peer, object iface) - { - return new VirtualizedItemProviderWrapper(peer, (IVirtualizedItemProvider)iface); - } + return new VirtualizedItemProviderWrapper(peer, (IVirtualizedItemProvider)iface); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs index 7dbbed8c642..17b07d34c04 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs @@ -1,96 +1,78 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// -// -// Description: Window pattern provider wrapper for WCP -// -// - #nullable enable using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -namespace MS.Internal.Automation +namespace MS.Internal.Automation; + +internal sealed class WindowProviderWrapper : MarshalByRefObject, IWindowProvider { - // Automation/WCP Wrapper class: Implements that UIAutomation I...Provider - // interface, and calls through to a WCP AutomationPeer which implements the corresponding - // I...Provider inteface. Marshalls the call from the RPC thread onto the - // target AutomationPeer's context. - // - // Class has two major parts to it: - // * Implementation of the I...Provider, which uses Dispatcher.Invoke - // to call a private method (lives in second half of the class) via a delegate, - // if necessary, packages any params into an object param. Return type of Invoke - // must be cast from object to appropriate type. - // * private methods - one for each interface entry point - which get called back - // on the right context. These call through to the peer that's actually - // implenting the I...Provider version of the interface. - internal sealed class WindowProviderWrapper : MarshalByRefObject, IWindowProvider + private readonly AutomationPeer _peer; + private readonly IWindowProvider _iface; + + private WindowProviderWrapper(AutomationPeer peer, IWindowProvider iface) + { + Debug.Assert(peer is not null); + Debug.Assert(iface is not null); + + _peer = peer; + _iface = iface; + } + + public void SetVisualState(WindowVisualState state) + { + ElementUtil.Invoke(_peer, static (state, visualState) => state.SetVisualState(visualState), _iface, state); + } + + public void Close() + { + ElementUtil.Invoke(_peer, static (state) => state.Close(), _iface); + } + + public bool WaitForInputIdle(int milliseconds) + { + return ElementUtil.Invoke(_peer, static (state, milliseconds) => state.WaitForInputIdle(milliseconds), _iface, milliseconds); + } + + public bool Maximizable + { + get => ElementUtil.Invoke(_peer, static (state) => state.Maximizable, _iface); + } + + public bool Minimizable + { + get => ElementUtil.Invoke(_peer, static (state) => state.Minimizable, _iface); + } + + public bool IsModal + { + get => ElementUtil.Invoke(_peer, static (state) => state.IsModal, _iface); + } + + public WindowVisualState VisualState + { + get => ElementUtil.Invoke(_peer, static (state) => state.VisualState, _iface); + } + + public WindowInteractionState InteractionState + { + get => ElementUtil.Invoke(_peer, static (state) => state.InteractionState, _iface); + } + + public bool IsTopmost + { + get => ElementUtil.Invoke(_peer, static (state) => state.IsTopmost, _iface); + } + + /// + /// Creates a wrapper for the given and interface. + /// + internal static object Wrap(AutomationPeer peer, object iface) { - private readonly AutomationPeer _peer; - private readonly IWindowProvider _iface; - - private WindowProviderWrapper(AutomationPeer peer, IWindowProvider iface) - { - Debug.Assert(peer is not null); - Debug.Assert(iface is not null); - - _peer = peer; - _iface = iface; - } - - public void SetVisualState(WindowVisualState state) - { - ElementUtil.Invoke(_peer, static (state, visualState) => state.SetVisualState(visualState), _iface, state); - } - - public void Close() - { - ElementUtil.Invoke(_peer, static (state) => state.Close(), _iface); - } - - public bool WaitForInputIdle(int milliseconds) - { - return ElementUtil.Invoke(_peer, static (state, milliseconds) => state.WaitForInputIdle(milliseconds), _iface, milliseconds); - } - - public bool Maximizable - { - get => ElementUtil.Invoke(_peer, static (state) => state.Maximizable, _iface); - } - - public bool Minimizable - { - get => ElementUtil.Invoke(_peer, static (state) => state.Minimizable, _iface); - } - - public bool IsModal - { - get => ElementUtil.Invoke(_peer, static (state) => state.IsModal, _iface); - } - - public WindowVisualState VisualState - { - get => ElementUtil.Invoke(_peer, static (state) => state.VisualState, _iface); - } - - public WindowInteractionState InteractionState - { - get => ElementUtil.Invoke(_peer, static (state) => state.InteractionState, _iface); - } - - public bool IsTopmost - { - get => ElementUtil.Invoke(_peer, static (state) => state.IsTopmost, _iface); - } - - internal static object Wrap(AutomationPeer peer, object iface) - { - return new WindowProviderWrapper(peer, (IWindowProvider)iface); - } + return new WindowProviderWrapper(peer, (IWindowProvider)iface); } } From 6fc0efed729995af1d57457d666dc9d003c15f74 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 31 May 2025 19:08:18 +0200 Subject: [PATCH 43/54] Formatting changes ONLY - Add summary comments for proxies --- .../MS/internal/Automation/DockProviderWrapper.cs | 4 ++++ .../internal/Automation/ExpandCollapseProviderWrapper.cs | 4 ++++ .../MS/internal/Automation/GridItemProviderWrapper.cs | 4 ++++ .../MS/internal/Automation/GridProviderWrapper.cs | 4 ++++ .../MS/internal/Automation/InvokeProviderWrapper.cs | 4 ++++ .../internal/Automation/ItemContainerProviderWrapper.cs | 4 ++++ .../MS/internal/Automation/MultipleViewProviderWrapper.cs | 4 ++++ .../MS/internal/Automation/RangeValueProviderWrapper.cs | 4 ++++ .../MS/internal/Automation/ScrollItemProviderWrapper.cs | 4 ++++ .../MS/internal/Automation/ScrollProviderWrapper.cs | 4 ++++ .../internal/Automation/SelectionItemProviderWrapper.cs | 4 ++++ .../MS/internal/Automation/SelectionProviderWrapper.cs | 4 ++++ .../Automation/SynchronizedInputProviderWrapper.cs | 4 ++++ .../MS/internal/Automation/TableItemProviderWrapper.cs | 4 ++++ .../MS/internal/Automation/TableProviderWrapper.cs | 4 ++++ .../MS/internal/Automation/TextProviderWrapper.cs | 8 ++++++-- .../MS/internal/Automation/TextRangeProviderWrapper.cs | 8 ++++++-- .../MS/internal/Automation/ToggleProviderWrapper.cs | 4 ++++ .../MS/internal/Automation/TransformProviderWrapper.cs | 4 ++++ .../MS/internal/Automation/ValueProviderWrapper.cs | 4 ++++ .../internal/Automation/VirtualizedItemProviderWrapper.cs | 4 ++++ .../MS/internal/Automation/WindowProviderWrapper.cs | 4 ++++ 22 files changed, 92 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs index 265ba05885a..71666d4629e 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/DockProviderWrapper.cs @@ -9,6 +9,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class DockProviderWrapper : MarshalByRefObject, IDockProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs index e55235ad435..b4b4fe00ea4 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ExpandCollapseProviderWrapper.cs @@ -9,6 +9,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class ExpandCollapseProviderWrapper : MarshalByRefObject, IExpandCollapseProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs index 60630520979..158b462447d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridItemProviderWrapper.cs @@ -8,6 +8,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class GridItemProviderWrapper : MarshalByRefObject, IGridItemProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs index 279dd1ff196..1fbe1e8c274 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs @@ -8,6 +8,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class GridProviderWrapper : MarshalByRefObject, IGridProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs index 7ccf95c45ee..39e6e2d63ae 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/InvokeProviderWrapper.cs @@ -8,6 +8,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class InvokeProviderWrapper : MarshalByRefObject, IInvokeProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs index 37fe92593ef..bf88521ec58 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs @@ -8,6 +8,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class ItemContainerProviderWrapper : MarshalByRefObject, IItemContainerProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs index b8cb3614df6..247a2f84f33 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/MultipleViewProviderWrapper.cs @@ -8,6 +8,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class MultipleViewProviderWrapper : MarshalByRefObject, IMultipleViewProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs index e277e85a4df..353d2d9b3aa 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/RangeValueProviderWrapper.cs @@ -8,6 +8,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class RangeValueProviderWrapper : MarshalByRefObject, IRangeValueProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs index 87fa4bcb892..8c33fb1d14c 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollItemProviderWrapper.cs @@ -8,6 +8,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class ScrollItemProviderWrapper : MarshalByRefObject, IScrollItemProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs index 093e975c269..6fc355a0762 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ScrollProviderWrapper.cs @@ -9,6 +9,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class ScrollProviderWrapper : MarshalByRefObject, IScrollProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs index ccef048ba46..0bb01ba18ca 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionItemProviderWrapper.cs @@ -8,6 +8,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class SelectionItemProviderWrapper : MarshalByRefObject, ISelectionItemProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs index 812be89d4fe..3f136f4cb51 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SelectionProviderWrapper.cs @@ -8,6 +8,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class SelectionProviderWrapper : MarshalByRefObject, ISelectionProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs index 311e9486493..dbd0defeabd 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputProviderWrapper.cs @@ -9,6 +9,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class SynchronizedInputProviderWrapper : MarshalByRefObject, ISynchronizedInputProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs index d9401e988b8..b399e4cee06 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableItemProviderWrapper.cs @@ -8,6 +8,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class TableItemProviderWrapper : MarshalByRefObject, ITableItemProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs index c33c0b4e024..2c5226223b5 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs @@ -9,6 +9,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class TableProviderWrapper : MarshalByRefObject, ITableProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs index 3eb2f80b872..c673578a20f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextProviderWrapper.cs @@ -1,15 +1,19 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable + using System.Windows; using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows.Automation.Peers; -#nullable enable - namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class TextProviderWrapper : MarshalByRefObject, ITextProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs index 0125863ad2d..7b5aa5096b4 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TextRangeProviderWrapper.cs @@ -1,15 +1,19 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable + using System.Windows.Automation.Provider; using System.Windows.Automation.Text; using System.Windows.Automation.Peers; using System.Diagnostics.CodeAnalysis; -#nullable enable - namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class TextRangeProviderWrapper : MarshalByRefObject, ITextRangeProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs index 0578b6a5323..61908c41e17 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ToggleProviderWrapper.cs @@ -9,6 +9,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class ToggleProviderWrapper : MarshalByRefObject, IToggleProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs index 202676002f2..95a5f8240ea 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TransformProviderWrapper.cs @@ -8,6 +8,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class TransformProviderWrapper : MarshalByRefObject, ITransformProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs index a947e1bda52..fe2f2474565 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ValueProviderWrapper.cs @@ -8,6 +8,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class ValueProviderWrapper : MarshalByRefObject, IValueProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs index 7131671fe53..8c9b56678d5 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/VirtualizedItemProviderWrapper.cs @@ -8,6 +8,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class VirtualizedItemProviderWrapper : MarshalByRefObject, IVirtualizedItemProvider { private readonly AutomationPeer _peer; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs index 17b07d34c04..71feca97e3d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/WindowProviderWrapper.cs @@ -9,6 +9,10 @@ namespace MS.Internal.Automation; +/// +/// Wrapper class for the interface, calls through to the managed +/// that implements it. The calls are made on the peer's context to ensure that the correct synchronization context is used. +/// internal sealed class WindowProviderWrapper : MarshalByRefObject, IWindowProvider { private readonly AutomationPeer _peer; From 174bb73fd967c076f89a26651243cfee9a94a84e Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sun, 1 Jun 2025 00:09:20 +0200 Subject: [PATCH 44/54] Re-order new files in csproj --- src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj b/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj index 5a8cc5202fc..5d45a323eb8 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj @@ -244,7 +244,6 @@ - @@ -286,13 +285,14 @@ + + + - - From e48620f681b50eaf67d7e806e4cea6dbb5d94eda Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sun, 1 Jun 2025 12:00:16 +0200 Subject: [PATCH 45/54] Use internals over public to avoid confusion --- .../internal/Automation/ElementUtil.ActionInfo.cs | 9 ++++----- .../internal/Automation/ElementUtil.ReturnInfo.cs | 14 +++++++------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ActionInfo.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ActionInfo.cs index dc5661c9069..49d3f0e5abd 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ActionInfo.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ActionInfo.cs @@ -22,20 +22,19 @@ private readonly struct ActionInfo /// /// The exception that was thrown during the operation, if any. /// - public Exception? StoredException { get; init; } + internal Exception? StoredException { get; init; } /// /// Gets a value indicating whether the operation has been completed or timed out. /// - public bool HasCompleted { get; init; } + internal bool HasCompleted { get; init; } /// /// Creates a new instance of with the specified exception. /// /// The exception that was thrown during the operation. /// Returns a object. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ActionInfo FromException(Exception exception) + internal static ActionInfo FromException(Exception exception) { return new ActionInfo { @@ -48,7 +47,7 @@ public static ActionInfo FromException(Exception exception) /// Returns a singleton instance of signalizing successful completion of the operation. /// /// Returns a object. - public static ActionInfo Completed { get; } = new ActionInfo + internal static ActionInfo Completed { get; } = new ActionInfo { StoredException = null, HasCompleted = true diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ReturnInfo.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ReturnInfo.cs index 2446154095b..baa13b46f3c 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ReturnInfo.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.ReturnInfo.cs @@ -16,32 +16,31 @@ internal static partial class ElementUtil /// /// Wraps the return value and exception of an asynchronous operation. /// - /// + /// The type of the return value. [StructLayout(LayoutKind.Auto)] private readonly struct ReturnInfo { /// /// The exception that was thrown during the operation, if any. /// - public Exception? StoredException { get; init; } + internal Exception? StoredException { get; init; } /// /// Gets a value indicating whether the operation has been completed or timed out. /// - public bool HasCompleted { get; init; } + internal bool HasCompleted { get; init; } /// /// The return value of the operation, if it completed successfully. /// - public TReturn Value { get; init; } + internal TReturn Value { get; init; } /// /// Creates a new instance of with the specified exception. /// /// The exception that was thrown during the operation. /// Returns a object. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReturnInfo FromException(Exception exception) + internal static ReturnInfo FromException(Exception exception) { return new ReturnInfo { @@ -56,7 +55,8 @@ public static ReturnInfo FromException(Exception exception) /// /// The return value of the operation. /// Returns a object. - public static ReturnInfo FromResult(TReturn value) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ReturnInfo FromResult(TReturn value) { return new ReturnInfo { From 59ed9ae06f921bd122005b32bbbba1453c022a52 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sun, 1 Jun 2025 13:10:46 +0200 Subject: [PATCH 46/54] Add ExpandCollapse/RangeValue/ToggleProvider unit tests --- .../ExpandCollapseProviderWrapper.Tests.cs | 29 +++++++++++++++ .../RangeValueProviderWrapper.Tests.cs | 35 +++++++++++++++++++ .../Automation/ToggleProviderWrapper.Tests.cs | 29 +++++++++++++++ .../PresentationCore.Tests.csproj | 1 + 4 files changed, 94 insertions(+) create mode 100644 src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/ExpandCollapseProviderWrapper.Tests.cs create mode 100644 src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/RangeValueProviderWrapper.Tests.cs create mode 100644 src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/ToggleProviderWrapper.Tests.cs diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/ExpandCollapseProviderWrapper.Tests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/ExpandCollapseProviderWrapper.Tests.cs new file mode 100644 index 00000000000..0fece4f7d03 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/ExpandCollapseProviderWrapper.Tests.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Windows.Automation; +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace MS.Internal.Automation; + +public sealed class ExpandCollapseProviderWrapperTests +{ + [WpfFact] + public void ComboBoxAutomationPeer_Wrap_Properties_Methods_ReturnsExpected() + { + ComboBox comboBox = new(); + ComboBoxAutomationPeer? peer = comboBox.CreateAutomationPeer() as ComboBoxAutomationPeer; + + Assert.NotNull(peer); + + ExpandCollapseProviderWrapper? wrapper = peer.GetWrappedPattern(ExpandCollapsePatternIdentifiers.Pattern.Id) as ExpandCollapseProviderWrapper; + Assert.NotNull(wrapper); + + Assert.Equal(ExpandCollapseState.Collapsed, wrapper.ExpandCollapseState); + + // TODO: To check the state after Expand and Collapse methods, we'd require valid ComboBox in the visual tree. + wrapper.Expand(); + wrapper.Collapse(); + } +} diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/RangeValueProviderWrapper.Tests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/RangeValueProviderWrapper.Tests.cs new file mode 100644 index 00000000000..2170c632ec4 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/RangeValueProviderWrapper.Tests.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Windows.Automation; +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace MS.Internal.Automation; + +public sealed class RangleValueProviderWrapperTests +{ + [WpfFact] + public void SliderAutomationPeer_Wrap_Properties_Methods_ReturnsExpected() + { + Slider slider = new() { Minimum = 10, Maximum = 100, Value = 50, LargeChange = 25, SmallChange = 10 }; + SliderAutomationPeer? peer = slider.CreateAutomationPeer() as SliderAutomationPeer; + + Assert.NotNull(peer); + + RangeValueProviderWrapper? wrapper = peer.GetWrappedPattern(RangeValuePatternIdentifiers.Pattern.Id) as RangeValueProviderWrapper; + Assert.NotNull(wrapper); + + Assert.Equal(50, wrapper.Value); + Assert.Equal(10, wrapper.Minimum); + Assert.Equal(100, wrapper.Maximum); + Assert.Equal(25, wrapper.LargeChange); + Assert.Equal(10, wrapper.SmallChange); + + Assert.False(wrapper.IsReadOnly); + + // We currently do not require Slider to be a part of visual tree for RangeValuePattern to be available, so we can set value without any issues + wrapper.SetValue(75); + Assert.Equal(75, wrapper.Value); + } +} diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/ToggleProviderWrapper.Tests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/ToggleProviderWrapper.Tests.cs new file mode 100644 index 00000000000..d1756bbbb9d --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/ToggleProviderWrapper.Tests.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Windows.Automation; +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace MS.Internal.Automation; + +public sealed class ToggleProviderWrapperTests +{ + [WpfFact] + public void CheckBoxAutomationPeer_Wrap_Properties_Methods_ReturnsExpected() + { + CheckBox checkBox = new(); + CheckBoxAutomationPeer? peer = checkBox.CreateAutomationPeer() as CheckBoxAutomationPeer; + + Assert.NotNull(peer); + + ToggleProviderWrapper? wrapper = peer.GetWrappedPattern(TogglePatternIdentifiers.Pattern.Id) as ToggleProviderWrapper; + Assert.NotNull(wrapper); + + Assert.Equal(ToggleState.Off, wrapper.ToggleState); + + // We currently do not require CheckBox to be a part of visual tree for TogglePattern to be available, so we can toggle it without any issues + wrapper.Toggle(); + Assert.Equal(ToggleState.On, wrapper.ToggleState); + } +} diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/PresentationCore.Tests.csproj b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/PresentationCore.Tests.csproj index 7fb2e9b3d38..0dd04cb8984 100644 --- a/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/PresentationCore.Tests.csproj +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/PresentationCore.Tests.csproj @@ -20,6 +20,7 @@ + From f0c0cc89514e72fc9f77350e7de78a4fcd5ff734 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sun, 1 Jun 2025 13:18:47 +0200 Subject: [PATCH 47/54] Add ValueProviderWrapper tests --- .../Automation/ValueProviderWrapper.Tests.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/ValueProviderWrapper.Tests.cs diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/ValueProviderWrapper.Tests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/ValueProviderWrapper.Tests.cs new file mode 100644 index 00000000000..2e75cca5f2e --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/ValueProviderWrapper.Tests.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Windows.Automation; +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace MS.Internal.Automation; + +public sealed class ValueProviderWrapperTests +{ + [WpfFact] + public void TextBoxAutomationPeer_Wrap_Properties_Methods_ReturnsExpected() + { + TextBox textBox = new(); + TextBoxAutomationPeer? peer = textBox.CreateAutomationPeer() as TextBoxAutomationPeer; + + Assert.NotNull(peer); + + ValueProviderWrapper? wrapper = peer.GetWrappedPattern(ValuePatternIdentifiers.Pattern.Id) as ValueProviderWrapper; + Assert.NotNull(wrapper); + + // Default TextProperty value for TextBox is string.Empty + Assert.Equal(string.Empty, wrapper.Value); + Assert.False(wrapper.IsReadOnly); + + // We currently do not require TextBox to be a part of visual tree for ValuePattern to be available, so we can interact with it without any issues + wrapper.SetValue("New Value"); + Assert.Equal("New Value", wrapper.Value); + + // Set the TextBox to be read-only and verify the wrapper reflects that change + textBox.IsReadOnly = true; + Assert.True(wrapper.IsReadOnly); + } +} From 92151a5f0d23ac5efa9842960838a83127ed4f18 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sun, 1 Jun 2025 13:42:18 +0200 Subject: [PATCH 48/54] Fix nullability on FindItemByProperty --- .../ItemContainerProviderWrapper.cs | 12 +++++------ .../Provider/IItemContainerProvider.cs | 6 ++++-- .../Automation/Provider/ITextRangeProvider.cs | 20 ------------------- 3 files changed, 10 insertions(+), 28 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs index bf88521ec58..71e663310b9 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ItemContainerProviderWrapper.cs @@ -26,16 +26,16 @@ private ItemContainerProviderWrapper(AutomationPeer peer, IItemContainerProvider _iface = iface; } - public IRawElementProviderSimple FindItemByProperty(IRawElementProviderSimple startAfter, int propertyId, object value) + public IRawElementProviderSimple? FindItemByProperty(IRawElementProviderSimple? startAfter, int propertyId, object? value) { - object[] args = [startAfter, propertyId, value]; + object?[] args = [startAfter, propertyId, value]; // The actual invocation method that gets called on the peer's context. - static IRawElementProviderSimple FindItemByProperty(IItemContainerProvider state, object[] args) + static IRawElementProviderSimple? FindItemByProperty(IItemContainerProvider state, object?[] args) { - IRawElementProviderSimple startAfter = (IRawElementProviderSimple)args[0]; - int propertyId = (int)args[1]; - object value = args[2]; + IRawElementProviderSimple? startAfter = (IRawElementProviderSimple?)args[0]; + int propertyId = (int)args[1]!; + object? value = args[2]; return state.FindItemByProperty(startAfter, propertyId, value); } diff --git a/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationProvider/System/Windows/Automation/Provider/IItemContainerProvider.cs b/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationProvider/System/Windows/Automation/Provider/IItemContainerProvider.cs index 578297ea47b..b2dfe04687e 100644 --- a/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationProvider/System/Windows/Automation/Provider/IItemContainerProvider.cs +++ b/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationProvider/System/Windows/Automation/Provider/IItemContainerProvider.cs @@ -3,6 +3,8 @@ // Description: Item Container pattern provider interface +#nullable enable + using System.Runtime.InteropServices; namespace System.Windows.Automation.Provider @@ -10,7 +12,7 @@ namespace System.Windows.Automation.Provider /// /// /// Exposes a container control's ability to search over the items it contains. - /// This pattern must be implemented by containers which suppots virtualization and have + /// This pattern must be implemented by containers which supports virtualization and have /// no other means to find the virtualized element though it's orthogonal to virtualization /// and can be implemented by any containers which has items in it. /// @@ -53,7 +55,7 @@ public interface IItemContainerProvider /// corresponds to property for whose value it want to search over. /// value to be searched for, for specified property /// The first item which matches the searched criterion, if no item matches, it returns null - IRawElementProviderSimple FindItemByProperty(IRawElementProviderSimple startAfter, int propertyId, object value); + IRawElementProviderSimple? FindItemByProperty(IRawElementProviderSimple? startAfter, int propertyId, object? value); } } diff --git a/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationTypes/System/Windows/Automation/Provider/ITextRangeProvider.cs b/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationTypes/System/Windows/Automation/Provider/ITextRangeProvider.cs index bea04493644..4466a6617ec 100644 --- a/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationTypes/System/Windows/Automation/Provider/ITextRangeProvider.cs +++ b/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationTypes/System/Windows/Automation/Provider/ITextRangeProvider.cs @@ -22,14 +22,6 @@ internal interface ITextRangeProvider public interface ITextRangeProvider #endif { - //------------------------------------------------------ - // - // Public Methods - // - //------------------------------------------------------ - - #region Public Methods - /// /// Retrieves a new range covering an identical span of text. The new range can be manipulated independently from the original. /// @@ -187,17 +179,6 @@ public interface ITextRangeProvider /// true if the provider should be scrolled so the range is flush with the top of the viewport. /// false if the provider should be scrolled so the range is flush with the bottom. void ScrollIntoView([MarshalAs(UnmanagedType.Bool)] bool alignToTop); - - #endregion Public Methods - - - //------------------------------------------------------ - // - // Public Properties - // - //------------------------------------------------------ - - #region Public Properties /// /// Retrieves a collection of all of the children that fall within the range. @@ -208,6 +189,5 @@ public interface ITextRangeProvider /// this can return either null or an empty enumeration. IRawElementProviderSimple[]? GetChildren(); - #endregion Public Properties } } From 5db90be8681e62765f6eacdac9476cc502faade1 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sun, 1 Jun 2025 13:58:14 +0200 Subject: [PATCH 49/54] Fix IGridProvider nullability --- .../MS/internal/Automation/GridProviderWrapper.cs | 2 +- .../MS/internal/Automation/TableProviderWrapper.cs | 2 +- .../System/Windows/Automation/Provider/IGridProvider.cs | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs index 1fbe1e8c274..a058e34f8f3 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/GridProviderWrapper.cs @@ -26,7 +26,7 @@ private GridProviderWrapper(AutomationPeer peer, IGridProvider iface) _iface = iface; } - public IRawElementProviderSimple GetItem(int row, int column) + public IRawElementProviderSimple? GetItem(int row, int column) { return ElementUtil.Invoke(_peer, static (state, rowColumn) => state.GetItem(rowColumn[0], rowColumn[1]), _iface, new int[] { row, column }); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs index 2c5226223b5..6a0232e9205 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/TableProviderWrapper.cs @@ -27,7 +27,7 @@ private TableProviderWrapper(AutomationPeer peer, ITableProvider iface) _iface = iface; } - public IRawElementProviderSimple GetItem(int row, int column) + public IRawElementProviderSimple? GetItem(int row, int column) { return ElementUtil.Invoke(_peer, static (state, rowColumn) => state.GetItem(rowColumn[0], rowColumn[1]), _iface, new int[] { row, column }); } diff --git a/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationProvider/System/Windows/Automation/Provider/IGridProvider.cs b/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationProvider/System/Windows/Automation/Provider/IGridProvider.cs index 5d7adf75ecc..ce2cf681bef 100644 --- a/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationProvider/System/Windows/Automation/Provider/IGridProvider.cs +++ b/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationProvider/System/Windows/Automation/Provider/IGridProvider.cs @@ -1,8 +1,10 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // Description: Grid pattern provider interface +#nullable enable + using System.Runtime.InteropServices; namespace System.Windows.Automation.Provider @@ -24,7 +26,7 @@ public interface IGridProvider /// /// Row of cell to get /// Column of cell to get - IRawElementProviderSimple GetItem(int row, int column); + IRawElementProviderSimple? GetItem(int row, int column); /// /// number of rows in the grid From efcdfd3ca1d409803e61695a1671c0864f2af26a Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sun, 1 Jun 2025 14:02:08 +0200 Subject: [PATCH 50/54] Enable nullability on IMultipleViewProvider --- .../Automation/Provider/IMultipleViewProvider.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationProvider/System/Windows/Automation/Provider/IMultipleViewProvider.cs b/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationProvider/System/Windows/Automation/Provider/IMultipleViewProvider.cs index 3b5bfe73127..92a994583e8 100644 --- a/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationProvider/System/Windows/Automation/Provider/IMultipleViewProvider.cs +++ b/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationProvider/System/Windows/Automation/Provider/IMultipleViewProvider.cs @@ -1,8 +1,10 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // Description: Multiple View pattern provider interface +#nullable enable + using System.Runtime.InteropServices; namespace System.Windows.Automation.Provider @@ -30,20 +32,17 @@ public interface IMultipleViewProvider /// be the same across instances. /// /// Return a localized, human readable string in the application's current UI language. - string GetViewName( int viewId ); + string GetViewName(int viewId); /// /// Change the current view using an ID returned from SupportedViews property /// - void SetCurrentView( int viewId ); + void SetCurrentView(int viewId); /// The view ID corresponding to the control's current state. This ID is control-specific - int CurrentView - { - get; - } + int CurrentView { get; } /// Returns an array of ints representing the full set of views available in this control. - int [] GetSupportedViews(); + int[] GetSupportedViews(); } } From e24e4a559ad10c604e7f41e0a3658904209edd63 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sun, 1 Jun 2025 18:15:00 +0200 Subject: [PATCH 51/54] Add TransformProviderWrapper unit tests as well --- .../TransformProviderWrapper.Tests.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/TransformProviderWrapper.Tests.cs diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/TransformProviderWrapper.Tests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/TransformProviderWrapper.Tests.cs new file mode 100644 index 00000000000..a115793a96e --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/MS/Internal/Automation/TransformProviderWrapper.Tests.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Windows.Automation; +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace MS.Internal.Automation; + +public sealed class TransformProviderWrapperTests +{ + [WpfFact] + public void GridSplitterAutomationPeer_Wrap_Properties_Methods_ReturnsExpected() + { + GridSplitter gridSplitter = new(); + GridSplitterAutomationPeer? peer = gridSplitter.CreateAutomationPeer() as GridSplitterAutomationPeer; + + Assert.NotNull(peer); + + TransformProviderWrapper? wrapper = peer.GetWrappedPattern(TransformPatternIdentifiers.Pattern.Id) as TransformProviderWrapper; + Assert.NotNull(wrapper); + + Assert.True(wrapper.CanMove); + + // GridSplitter does not support resizing or rotating, so these properties should return false + Assert.False(wrapper.CanResize); + Assert.False(wrapper.CanRotate); + + // Attempting to call Move should not throw an exception + wrapper.Move(10, 10); + + // Unsupported operations should throw exceptions + Assert.Throws(() => wrapper.Resize(100, 100)); + Assert.Throws(() => wrapper.Rotate(45)); + } +} From d380b144c748693de42efd803dbefc60bf129808 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Mon, 2 Jun 2025 10:34:23 +0200 Subject: [PATCH 52/54] Final touches --- .../Windows/Threading/DispatcherOperation.Action.1.cs | 3 +++ .../System/Windows/Threading/DispatcherOperation.cs | 6 ++---- .../WindowsBase/System/Windows/Threading/DispatcherUtils.cs | 4 ++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.1.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.1.cs index a570d92c224..4f045728e76 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.1.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.1.cs @@ -5,6 +5,9 @@ namespace System.Windows.Threading; +/// +/// DispatcherOperation represents a delegate that has been posted to the queue. +/// internal sealed class DispatcherOperationAction : DispatcherOperationAction { private readonly TArg _arg; diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs index 07f8c2af709..4a925049179 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs @@ -10,8 +10,7 @@ namespace System.Windows.Threading { /// - /// DispatcherOperation represents a delegate that has been - /// posted to the Dispatcher queue. + /// DispatcherOperation represents a delegate that has been posted to the queue. /// public abstract partial class DispatcherOperation { @@ -127,8 +126,7 @@ public DispatcherOperationStatus Wait() /// public DispatcherOperationStatus Wait(TimeSpan timeout) { - if ((_status == DispatcherOperationStatus.Pending || _status == DispatcherOperationStatus.Executing) && - timeout.TotalMilliseconds != 0) + if ((_status is DispatcherOperationStatus.Pending or DispatcherOperationStatus.Executing) && timeout.TotalMilliseconds != 0) { if (_dispatcher.Thread == Thread.CurrentThread) { diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherUtils.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherUtils.cs index 6f2905bcac0..a55e3df81c7 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherUtils.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherUtils.cs @@ -7,6 +7,10 @@ namespace System.Windows.Threading; internal static class DispatcherUtils { + /// + /// Either returns the default for the given dispatcher, or creates a new one with the specified priority. + /// + /// The behaviour is modified based on several . [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static DispatcherSynchronizationContext GetOrCreateContext(Dispatcher dispatcher, DispatcherPriority priority) { From 140210e4a24c06f481122c56e8b95287d00d8993 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Mon, 2 Jun 2025 13:38:02 +0200 Subject: [PATCH 53/54] Fix ref to include abstract keyword, revert callvirt --- .../Threading/DispatcherOperation.Action.0.cs | 2 +- .../DispatcherOperation.TResult.0.cs | 5 ++ .../Windows/Threading/DispatcherOperation.cs | 12 +++- .../Threading/DispatcherOperationLegacy.cs | 7 +-- .../src/WindowsBase/ref/WindowsBase.cs | 2 +- .../Windows/Threading/DispatcherTests.cs | 57 +++++++++++++++++++ 6 files changed, 77 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.0.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.0.cs index e9e25a3a84b..b7e71d41535 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.0.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.Action.0.cs @@ -20,7 +20,7 @@ internal DispatcherOperationAction(Dispatcher dispatcher, DispatcherPriority pri { } - public sealed override object Result + private protected sealed override object OperationResult { get { diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.0.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.0.cs index b33940df111..25464a54bda 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.0.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.TResult.0.cs @@ -75,6 +75,11 @@ internal DispatcherOperation(Dispatcher dispatcher, DispatcherPriority priority, } } + private protected sealed override object OperationResult + { + get => Result; + } + internal sealed override void InvokeCompletions() { switch (_status) diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs index 4a925049179..be13a360ae2 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperation.cs @@ -238,7 +238,17 @@ internal long Id /// /// Returns the result of the operation if it has completed. /// - public virtual object Result { get; } + public object Result + { + // As we cannot change the property to be abstract in the base class due to callvirt, + // we use a private protected property to allow derived classes to implement it. + get => OperationResult; + } + + /// + /// Gets the underlying result of the operation as an object. + /// + private protected abstract object OperationResult { get; } /// /// An event that is raised when the operation is aborted or canceled. diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationLegacy.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationLegacy.cs index 6483d1a2b52..d179da613e0 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationLegacy.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/DispatcherOperationLegacy.cs @@ -27,12 +27,9 @@ internal DispatcherOperationLegacy(Dispatcher dispatcher, Delegate method, Dispa { } - public override object Result + private protected sealed override object OperationResult { - get - { - return _result; - } + get => _result; } internal override void InvokeCompletions() diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/ref/WindowsBase.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/ref/WindowsBase.cs index fa8fd5edbeb..6afc1bda418 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/ref/WindowsBase.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/ref/WindowsBase.cs @@ -1809,7 +1809,7 @@ protected DispatcherObject() { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public void VerifyAccess() { } } - public partial class DispatcherOperation + public abstract partial class DispatcherOperation { internal DispatcherOperation() { } public System.Windows.Threading.Dispatcher Dispatcher { get { throw null; } } diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs index 564217a6513..19947d6f287 100644 --- a/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/WindowsBase.Tests/System/Windows/Threading/DispatcherTests.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.Threading; +using System.Threading.Tasks; namespace System.Windows.Threading.Tests; @@ -134,6 +135,62 @@ public void BeginInvoke_InvokeDelegateObjectArray_Success() Assert.NotNull(operation.Task); } + [WpfFact] + public void BeginInvoke_SameThread_DispatcherOperation_Return_Result_Block_Success() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + + static object action() => 5; + DispatcherOperation operation = dispatcher.BeginInvoke(DispatcherPriority.Send, action); + + Assert.Equal(DispatcherOperationStatus.Completed, operation.Wait()); + Assert.Equal(5, (int)operation.Result); + } + + [WpfFact] + public async Task BeginInvoke_SameThread_DispatcherOperation_Result_Await_SuccessAsync() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + + static object action() => 5; + DispatcherOperation operation = dispatcher.BeginInvoke(DispatcherPriority.Send, action); + await operation; + + Assert.Equal(DispatcherOperationStatus.Completed, operation.Status); + Assert.Equal(5, (int)operation.Result); + } + + [WpfFact] + public void InvokeAsync_SameThread_DispatcherOperation_TReturn_Result_Block_Success() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + + static int action() => 5; + DispatcherOperation operation = dispatcher.InvokeAsync(action, DispatcherPriority.Send, CancellationToken.None); + DispatcherOperation parentOperation = operation; + + Assert.Equal(5, operation.Result); + Assert.Equal(5, (int)parentOperation.Result); + Assert.Equal(DispatcherOperationStatus.Completed, operation.Status); + Assert.Equal(DispatcherOperationStatus.Completed, parentOperation.Status); + } + + [WpfFact] + public async Task InvokeAsync_SameThread_DispatcherOperation_TReturn_Await_SuccessAsync() + { + Dispatcher dispatcher = Dispatcher.CurrentDispatcher; + + static int action() => 5; + DispatcherOperation operation = dispatcher.InvokeAsync(action, DispatcherPriority.Send, CancellationToken.None); + DispatcherOperation parentOperation = operation; + + Assert.Equal(5, await operation); + Assert.Equal(5, operation.Result); + Assert.Equal(5, (int)parentOperation.Result); + Assert.Equal(DispatcherOperationStatus.Completed, operation.Status); + Assert.Equal(DispatcherOperationStatus.Completed, parentOperation.Status); + } + [WpfFact] public void Invoke_SameThread_TReturn_Success() { From 3935c32303a681ad726374f5526bb348501ab0fc Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Mon, 2 Jun 2025 14:11:19 +0200 Subject: [PATCH 54/54] Fix baseline (again) --- .../ApiCompat/Baselines/WindowsBase-ref-Net48.baseline.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/WindowsBase-ref-Net48.baseline.txt b/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/WindowsBase-ref-Net48.baseline.txt index d49e8532aa1..2ccd82e68d1 100644 --- a/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/WindowsBase-ref-Net48.baseline.txt +++ b/src/Microsoft.DotNet.Wpf/ApiCompat/Baselines/WindowsBase-ref-Net48.baseline.txt @@ -32,6 +32,7 @@ CannotRemoveBaseTypeOrInterface : Type 'System.Windows.Markup.XmlnsCompatibleWit CannotRemoveBaseTypeOrInterface : Type 'System.Windows.Markup.XmlnsDefinitionAttribute' does not implement interface 'System.Runtime.InteropServices._Attribute' in the implementation but it does in the contract. CannotRemoveBaseTypeOrInterface : Type 'System.Windows.Markup.XmlnsPrefixAttribute' does not implement interface 'System.Runtime.InteropServices._Attribute' in the implementation but it does in the contract. CannotRemoveBaseTypeOrInterface : Type 'System.Windows.Media.DisableDpiAwarenessAttribute' does not implement interface 'System.Runtime.InteropServices._Attribute' in the implementation but it does in the contract. +CannotMakeTypeAbstract : Type 'System.Windows.Threading.DispatcherOperation' is abstract in the implementation but is not abstract in the contract. MembersMustExist : Member 'protected System.Object System.Windows.Threading.DispatcherOperation.InvokeDelegateCore()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'protected System.Object System.Windows.Threading.DispatcherOperation.InvokeDelegateCore()' does not exist in the implementation but it does exist in the contract. -Total Issues: 35 +Total Issues: 36