Skip to content

Commit 2a82242

Browse files
committed
Merge in 'release/3.1' changes
2 parents 11a61c9 + 4e51e00 commit 2a82242

File tree

6 files changed

+1355
-48
lines changed

6 files changed

+1355
-48
lines changed

src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/HwndMouseInputProvider.cs

+152-8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Windows.Media;
88
using System.Runtime.InteropServices;
99
using System.Diagnostics;
10+
using System.Reflection;
1011
using System.Security;
1112
using MS.Internal;
1213
using MS.Internal.Interop;
@@ -908,12 +909,12 @@ private void PossiblyDeactivate(IntPtr hwndCapture, bool stillActiveIfOverSelf)
908909
//Console.WriteLine("PossiblyDeactivate(" + hwndCapture + ")");
909910

910911
// We are now longer active ourselves, but it is possible that the
911-
// window the mouse is going to intereact with is in the same
912+
// window the mouse is going to interact with is in the same
912913
// Dispatcher as ourselves. If so, we don't want to deactivate the
913914
// mouse input stream because the other window hasn't activated it
914915
// yet, and it may result in the input stream "flickering" between
915-
// active/inactive/active. This is ugly, so we try to supress the
916-
// uneccesary transitions.
916+
// active/inactive/active. This is ugly, so we try to suppress the
917+
// unnecessary transitions.
917918
//
918919
IntPtr hwndToCheck = hwndCapture;
919920
if(hwndToCheck == IntPtr.Zero)
@@ -951,17 +952,16 @@ private void PossiblyDeactivate(IntPtr hwndCapture, bool stillActiveIfOverSelf)
951952
{
952953
// We need to check if the point is over the client or
953954
// non-client area. We only care about being over the
954-
// non-client area.
955+
// client area.
955956
try
956957
{
957-
NativeMethods.RECT rcClient = new NativeMethods.RECT();
958-
SafeNativeMethods.GetClientRect(new HandleRef(this,hwndToCheck), ref rcClient);
958+
NativeMethods.RECT rcClient = GetEffectiveClientRect(hwndToCheck);
959959
SafeNativeMethods.ScreenToClient(new HandleRef(this,hwndToCheck), ptCursor);
960960

961961
if(ptCursor.x < rcClient.left || ptCursor.x >= rcClient.right ||
962962
ptCursor.y < rcClient.top || ptCursor.y >= rcClient.bottom)
963963
{
964-
// We are not over the non-client area. We can bail out.
964+
// We are not over the client area. We can bail out.
965965
//Console.WriteLine(" No capture, mouse outside of client area.");
966966
//Console.WriteLine(" Client Area: ({0},{1})-({2},{3})", rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
967967
//Console.WriteLine(" Mouse: ({0},{1})", ptCursor.x, ptCursor.y);
@@ -1005,6 +1005,139 @@ private void PossiblyDeactivate(IntPtr hwndCapture, bool stillActiveIfOverSelf)
10051005
}
10061006
}
10071007

1008+
/// <summary>
1009+
/// Returns the effective client rect of the hwnd, for use by PossiblyDeactivate
1010+
/// when deciding whether to do the "don't deactivate mouse input stream"
1011+
/// optimization. Specifically:
1012+
/// o If the hwnd isn't ours, return Empty. We never want to do the
1013+
/// optimization in this case.
1014+
/// o If the hwnd has no custom chrome, return its native client rect.
1015+
/// o Otherwise, remove the invisible caption and resize-border areas
1016+
/// from the native client rect, and return the result.
1017+
/// The last case deactivates the mouse stream when the mouse enters the
1018+
/// "effective" non-client area of a custom-chromed window. Leaving it
1019+
/// active leads to mouse events intended only for DWM to be delivered
1020+
/// to our windows, running user's event handlers at inappropriate times,
1021+
/// which can cause more or less arbitrary damage.
1022+
///
1023+
/// For example, moving the mouse from one window into the caption area
1024+
/// of a chromed window, then press-and-drag in the caption area will
1025+
/// (correctly) cause DWM to move the window, but will also (incorrectly)
1026+
/// raise MouseMove events to both windows. [DDVSO 1245503]
1027+
/// </summary>
1028+
private NativeMethods.RECT GetEffectiveClientRect(IntPtr hwnd)
1029+
{
1030+
NativeMethods.RECT rcClient = new NativeMethods.RECT();
1031+
HwndSource hwndSource;
1032+
1033+
// if the hwnd isn't ours, return an empty rect
1034+
if (!IsOurWindowImpl(hwnd, out hwndSource))
1035+
{
1036+
return rcClient;
1037+
}
1038+
1039+
// if the hwnd has custom chrome, return the reduced client area
1040+
if (HasCustomChrome(hwndSource, ref rcClient))
1041+
{
1042+
return rcClient;
1043+
}
1044+
1045+
// otherwise, return the native client rect
1046+
SafeNativeMethods.GetClientRect(new HandleRef(this,hwnd), ref rcClient);
1047+
return rcClient;
1048+
}
1049+
1050+
/// <summary>
1051+
/// If the given hwndSource has custom chrome via WindowChrome, return
1052+
/// true and set rcClient to the effective client area.
1053+
/// </summary>
1054+
private bool HasCustomChrome(HwndSource hwndSource, ref NativeMethods.RECT rcClient)
1055+
{
1056+
if (!EnsureFrameworkAccessors(hwndSource))
1057+
{
1058+
return false;
1059+
}
1060+
1061+
// an hwnd has custom chrome if its root visual is a Window whose
1062+
// WindowChromeWorker property is set. We can't say it that way here,
1063+
// because those classes belong to PresentationFramework. But we
1064+
// can do the equivalent using dependency properties and reflection.
1065+
DependencyObject rootVisual = hwndSource.RootVisual;
1066+
1067+
DependencyObject windowChromeWorker = rootVisual?.GetValue(WindowChromeWorkerProperty) as DependencyObject;
1068+
if (windowChromeWorker == null)
1069+
{
1070+
return false;
1071+
}
1072+
1073+
// ask the worker for the effective client area
1074+
object[] args = new object[1] { rcClient };
1075+
if ((bool)GetEffectiveClientAreaMI.Invoke(windowChromeWorker, args))
1076+
{
1077+
// copy the answer back to our caller
1078+
rcClient = (NativeMethods.RECT)(args[0]);
1079+
return true;
1080+
}
1081+
1082+
return false;
1083+
}
1084+
1085+
// lazy initialization of static fields that have to be
1086+
// set by reflection into PresentationFramework
1087+
private bool EnsureFrameworkAccessors(HwndSource hwndSource)
1088+
{
1089+
// if we've already done the work, return
1090+
if (WindowChromeWorkerProperty != null)
1091+
{
1092+
return true;
1093+
}
1094+
1095+
// get a reference to PresentationFramework, either from our own
1096+
// HwndSource, or (as a fallback) from the target HwndSource
1097+
Assembly presentationFramework = GetPresentationFrameworkFromHwndSource(_source.Value);
1098+
if (presentationFramework == null)
1099+
{
1100+
presentationFramework = GetPresentationFrameworkFromHwndSource(hwndSource);
1101+
}
1102+
1103+
if (presentationFramework == null)
1104+
{
1105+
return false;
1106+
}
1107+
1108+
// reflect into PresentationFramework to the accessors we need
1109+
Type windowChromeWorker = presentationFramework.GetType("System.Windows.Shell.WindowChromeWorker");
1110+
FieldInfo fiWindowChromeWorkerProperty = windowChromeWorker?.GetField("WindowChromeWorkerProperty", BindingFlags.Static | BindingFlags.Public);
1111+
DependencyProperty windowChromeWorkerProperty = fiWindowChromeWorkerProperty?.GetValue(null) as DependencyProperty;
1112+
GetEffectiveClientAreaMI = windowChromeWorker?.GetMethod("GetEffectiveClientArea", BindingFlags.Instance | BindingFlags.NonPublic);
1113+
1114+
// if we got them all, set WindowChromeWorkerProperty to signal that the
1115+
// initialization succeeded. This method can run on multiple threads,
1116+
// but it sets the static members to the same value on each thread, so
1117+
// it doesn't need any locking.
1118+
if (windowChromeWorkerProperty != null && GetEffectiveClientAreaMI != null)
1119+
{
1120+
WindowChromeWorkerProperty = windowChromeWorkerProperty;
1121+
}
1122+
1123+
return (WindowChromeWorkerProperty != null);
1124+
}
1125+
1126+
// return the Assembly for PresentationFramework. Usually the root visual
1127+
// of the given HwndSource is an object whose type is (or derives from)
1128+
// a type in PF: Window, PopupRoot, etc.
1129+
private Assembly GetPresentationFrameworkFromHwndSource(HwndSource hwndSource)
1130+
{
1131+
DependencyObject rootVisual = hwndSource?.RootVisual;
1132+
1133+
Type type = rootVisual?.GetType();
1134+
while (type != null && type.Assembly.FullName != PresentationFrameworkAssemblyFullName)
1135+
{
1136+
type = type.BaseType;
1137+
}
1138+
1139+
return type?.Assembly;
1140+
}
10081141
private void StartTracking(IntPtr hwnd)
10091142
{
10101143
if(!_tracking && !_isDwmProcess)
@@ -1047,14 +1180,20 @@ private IntPtr MakeLPARAM(int high, int low)
10471180
}
10481181

10491182
private bool IsOurWindow(IntPtr hwnd)
1183+
{
1184+
HwndSource hwndSource;
1185+
return IsOurWindowImpl(hwnd, out hwndSource);
1186+
}
1187+
1188+
private bool IsOurWindowImpl(IntPtr hwnd, out HwndSource hwndSource)
10501189
{
10511190
bool isOurWindow = false;
1191+
hwndSource = null;
10521192

10531193
Debug.Assert(null != _source && null != _source.Value);
10541194

10551195
if(hwnd != IntPtr.Zero)
10561196
{
1057-
HwndSource hwndSource;
10581197
hwndSource = HwndSource.CriticalFromHwnd(hwnd);
10591198

10601199
if(hwndSource != null)
@@ -1341,6 +1480,11 @@ private void RecordMouseMove(int x, int y, int timestamp)
13411480

13421481
private NativeMethods.TRACKMOUSEEVENT _tme = new NativeMethods.TRACKMOUSEEVENT();
13431482

1483+
// accessors into PresentationFramework classes
1484+
const string PresentationFrameworkAssemblyFullName = "PresentationFramework, Version=" + BuildInfo.WCP_VERSION + ", Culture=neutral, PublicKeyToken=" + BuildInfo.WCP_PUBLIC_KEY_TOKEN;
1485+
private static DependencyProperty WindowChromeWorkerProperty;
1486+
private static MethodInfo GetEffectiveClientAreaMI;
1487+
13441488
// MITIGATION_SETCURSOR
13451489
//
13461490
// Windows can have a "context help" button in the title bar. When the user

src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/HwndSource.cs

+16
Original file line numberDiff line numberDiff line change
@@ -1565,6 +1565,22 @@ private IntPtr InputFilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lP
15651565
IntPtr result = IntPtr.Zero ;
15661566
WindowMessage message = (WindowMessage)msg;
15671567

1568+
if (message == WindowMessage.WM_DESTROY)
1569+
{
1570+
// shut down the stylus stack on WM_DESTROY. This is normally done
1571+
// in PublicHooksFilterMessage (see remarks there), but that won't
1572+
// get called if no public hooks have ever been installed. Do it
1573+
// here as a backup for that case.
1574+
// For maintenance, there are three workflows to consider:
1575+
// 1. The normal case - public hooks are present, none of them
1576+
// handle WM_DESTROY. DisposeStylusInputProvider gets
1577+
// called twice, but the second call is a no-op.
1578+
// 2. No public hooks present. Only this call happens.
1579+
// 3. Public hooks are present, one of them handles WM_DESTROY.
1580+
// Only the call in PublicHooksFilterMessage happens.
1581+
DisposeStylusInputProvider();
1582+
}
1583+
15681584
// NOTE (alexz): invoke _stylus.FilterMessage before _mouse.FilterMessage
15691585
// to give _stylus a chance to eat mouse message generated by stylus
15701586
if (!_isDisposed && _stylus != null && !handled)

src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Automation/Peers/ItemAutomationPeer.cs

+16
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,22 @@ private bool IsItemInAutomationTree()
148148
}
149149

150150

151+
///
152+
override internal bool IgnoreUpdatePeer()
153+
{
154+
// Ignore UpdatePeer if the we're no longer in the automation tree.
155+
// There's no need to update such a peer, as it no longer
156+
// participates in automation. And UpdatePeer actually throws exceptions
157+
// in some cases.
158+
159+
if (!IsItemInAutomationTree())
160+
{
161+
return true;
162+
}
163+
164+
return base.IgnoreUpdatePeer();
165+
}
166+
151167
override internal bool IsDataItemAutomationPeer()
152168
{
153169
return true;

0 commit comments

Comments
 (0)