未验证 提交 3e5f11e4 编写于 作者: V Vatsan Madhavan 提交者: GitHub

Merge pull request #2100 from wpfcontrib/systemresources-hwnd-fixup

Prevent NULL HWND's from being parented under SystemResources listener windows
......@@ -976,23 +976,28 @@ private void BuildOrReparentWindow()
UnsafeNativeMethods.SetParent(_hwnd, new HandleRef(null,hwndParent));
}
}
else
else if (_hwnd.Handle != IntPtr.Zero)
{
// Reparent the window to notification-only window provided by SystemResources
// This keeps the child window around, but it is not visible. We can reparent the
// window later when a new parent is available
var hwnd = SystemResources.GetDpiAwarenessCompatibleNotificationWindow(_hwnd);
UnsafeNativeMethods.SetParent(_hwnd, new HandleRef(null, hwnd.Handle));
// ...But we have a potential problem: If the SystemResources listener window gets
// destroyed ahead of the call to HwndHost.OnDispatcherShutdown(), the HwndHost's window
// will be destroyed too, before the "logical" Dispose has had a chance to do proper
// shutdown. This turns out to be very significant for WebBrowser/ActiveXHost, which shuts
// down the hosted control through the COM interfaces, and the control destroys its
// window internally. Evidently, the WebOC fails to do full, proper cleanup if its
// window is destroyed unexpectedly.
// To avoid this situation, we make sure SystemResources responds to the Dispatcher
// shutdown event after this HwndHost.
SystemResources.DelayHwndShutdown();
Debug.Assert(hwnd != null);
Trace.WriteLineIf(hwnd == null, $"- Warning - Notification Window is null\n{new System.Diagnostics.StackTrace(true).ToString()}");
if (hwnd != null)
{
UnsafeNativeMethods.SetParent(_hwnd, new HandleRef(null, hwnd.Handle));
// ...But we have a potential problem: If the SystemResources listener window gets
// destroyed ahead of the call to HwndHost.OnDispatcherShutdown(), the HwndHost's window
// will be destroyed too, before the "logical" Dispose has had a chance to do proper
// shutdown. This turns out to be very significant for WebBrowser/ActiveXHost, which shuts
// down the hosted control through the COM interfaces, and the control destroys its
// window internally. Evidently, the WebOC fails to do full, proper cleanup if its
// window is destroyed unexpectedly.
// To avoid this situation, we make sure SystemResources responds to the Dispatcher
// shutdown event after this HwndHost.
SystemResources.DelayHwndShutdown();
}
}
}
finally
......
......@@ -1023,7 +1023,7 @@ internal static void OnThemeChanged()
/// This is the default HWND used to listen for theme-change messages.
///
/// When <see cref="IsPerMonitorDpiScalingActive"/> is true, additional notification windows are created
/// on-demand by <see cref="EnsureResourceChangeListener(DpiAwarenessContextValue)"/> or <see cref="EnsureResourceChangeListener(DpiUtil.HwndDpiInfo)"/>
/// on-demand by <see cref="EnsureResourceChangeListener(DpiUtil.HwndDpiInfo)"/>
/// as the need arises. For e.g., when <see cref="System.Windows.Interop.HwndHost"/> calls into <see cref="GetDpiAwarenessCompatibleNotificationWindow(HandleRef)"/>,
/// we would look for a notify-window that matches both (a) DPI Awareness Context and (b) DPI Scale factor of the foreign window from HwndHost to return. If none is found,
/// we would create one and add it to our list in <see cref="_hwndNotify"/> and return the newly created notify-window.
......@@ -1054,27 +1054,6 @@ private static void EnsureResourceChangeListener()
}
}
/// <summary>
/// Ensures that the notify-window corresponding to a given <paramref name="dpiContextValue"/> has been
/// created.
/// </summary>
/// <param name="dpiContextValue">DPI Awareness Context for which notify-window has to be ensured</param>
private static void EnsureResourceChangeListener(DpiAwarenessContextValue dpiContextValue)
{
EnsureResourceChangeListener();
// Test if _hwndNotify has a key that contains dpiContextValue - otherwise create and add a notify-window with
// this DPI Awareness Context
if (_hwndNotify.Keys.FirstOrDefault((hwndDpiContext) => hwndDpiContext.DpiAwarenessContextValue == dpiContextValue) == null)
{
var hwndDpiInfo = CreateResourceChangeListenerWindow(dpiContextValue);
if (!_dpiAwarenessContextAndDpis.Contains(hwndDpiInfo))
{
_dpiAwarenessContextAndDpis.Add(hwndDpiInfo);
}
}
}
/// <summary>
/// Ensures that a notify-window corresponding to a given HwndDpiInfo(=DpiAwarenessContextValue + DpiScale) has been
/// created.
......@@ -1085,9 +1064,16 @@ private static void EnsureResourceChangeListener(DpiAwarenessContextValue dpiCon
/// whose characteristics we are trying to match, we are guaranteed that the DPI Scale factor of the newly created HWND
/// would be identical to that of the reference HWND.
/// </remarks>
private static void EnsureResourceChangeListener(DpiUtil.HwndDpiInfo hwndDpiInfo)
private static bool EnsureResourceChangeListener(DpiUtil.HwndDpiInfo hwndDpiInfo)
{
EnsureResourceChangeListener();
// It's meaningless to ensure RCL for Invalid DACV
if (hwndDpiInfo.DpiAwarenessContextValue == DpiAwarenessContextValue.Invalid)
{
return false;
}
if (!_hwndNotify.ContainsKey(hwndDpiInfo))
{
var hwndDpiInfoKey =
......@@ -1095,13 +1081,15 @@ private static void EnsureResourceChangeListener(DpiUtil.HwndDpiInfo hwndDpiInfo
hwndDpiInfo.DpiAwarenessContextValue,
hwndDpiInfo.ContainingMonitorScreenRect.left,
hwndDpiInfo.ContainingMonitorScreenRect.top);
Debug.Assert(hwndDpiInfo == hwndDpiInfoKey);
if (!_dpiAwarenessContextAndDpis.Contains(hwndDpiInfo))
if (hwndDpiInfoKey == hwndDpiInfo && // If hwndDpiInfoKey != hwndDpiInfo, something is wrong, abort.
!_dpiAwarenessContextAndDpis.Contains(hwndDpiInfo))
{
_dpiAwarenessContextAndDpis.Add(hwndDpiInfo);
}
}
return _hwndNotify.ContainsKey(hwndDpiInfo);
}
/// <summary>
......@@ -1113,7 +1101,7 @@ private static void EnsureResourceChangeListener(DpiUtil.HwndDpiInfo hwndDpiInfo
/// <param name="y">y-coordinate position on the screen where the window is to be created</param>
/// <remarks>
/// Assumes that <see cref="_hwndNotify"/> and <see cref="_hwndNotifyHook"/> have been initialized. This method
/// must only be called by <see cref="EnsureResourceChangeListener"/> or <see cref="EnsureResourceChangeListener(DpiAwarenessContextValue)"/>
/// must only be called by <see cref="EnsureResourceChangeListener"/> or <see cref="EnsureResourceChangeListener(DpiUtil.HwndDpiInfo)"/>
/// </remarks>
/// <returns><see cref="DpiUtil.HwndDpiInfo"/> of the newly created <see cref="HwndWrapper"/>, which is also a new key added into <see cref="_hwndNotify"/></returns>
private static DpiUtil.HwndDpiInfo CreateResourceChangeListenerWindow(DpiAwarenessContextValue dpiContextValue, int x = 0, int y = 0, [System.Runtime.CompilerServices.CallerMemberName] string callerName = "")
......@@ -1652,8 +1640,12 @@ internal static HwndWrapper GetDpiAwarenessCompatibleNotificationWindow(HandleRe
DpiUtil.GetExtendedDpiInfoForWindow(hwnd.Handle, fallbackToNearestMonitorHeuristic: true) :
new DpiUtil.HwndDpiInfo(processDpiAwarenessContextValue, GetDpiScaleForUnawareOrSystemAwareContext(processDpiAwarenessContextValue));
EnsureResourceChangeListener(hwndDpiInfo);
return _hwndNotify[hwndDpiInfo].Value;
if (EnsureResourceChangeListener(hwndDpiInfo))
{
return _hwndNotify[hwndDpiInfo].Value;
}
return null;
}
/// <summary>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册