From c59877cd12908a443f5db7f2904533617c700520 Mon Sep 17 00:00:00 2001 From: Zachary Danz Date: Tue, 22 Jan 2019 14:03:48 -0800 Subject: [PATCH 1/6] move dpi related common unsafes to DpiCommonUnsafeNatives, privatize externs, use Trys instead, add context to park window from control, helper for WebBrowserBase, see if context is below (there should be a better way); todo: refactor out unspecified context --- .../src/Misc/CommonUnsafeNativeMethods.cs | 73 ------------ .../src/Misc/DpiHelper.cs | 4 +- .../src/Misc/DpiUnsafeNativeMethods.cs | 108 ++++++++++++++++++ .../src/System/Windows/Forms/Application.cs | 4 +- .../src/System/Windows/Forms/Control.cs | 11 +- .../src/System/Windows/Forms/NativeWindow.cs | 2 +- .../System/Windows/Forms/WebBrowserBase.cs | 2 +- .../src/misc/CommonUnsafeNativeMethods.cs | 82 +------------ .../src/misc/DpiHelper.DpiAwarenessContext.cs | 56 +++++++-- .../src/misc/DpiHelper.cs | 6 +- .../src/misc/DpiUnsafeNativeMethods.cs | 108 ++++++++++++++++++ .../UnitTests/CommonUnsafenativeMethods.cs | 18 +-- 12 files changed, 291 insertions(+), 183 deletions(-) create mode 100644 src/System.Windows.Forms.Design.Editors/src/Misc/DpiUnsafeNativeMethods.cs create mode 100644 src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs diff --git a/src/System.Windows.Forms.Design.Editors/src/Misc/CommonUnsafeNativeMethods.cs b/src/System.Windows.Forms.Design.Editors/src/Misc/CommonUnsafeNativeMethods.cs index a3400363ad4..e1b0b1f80e0 100644 --- a/src/System.Windows.Forms.Design.Editors/src/Misc/CommonUnsafeNativeMethods.cs +++ b/src/System.Windows.Forms.Design.Editors/src/Misc/CommonUnsafeNativeMethods.cs @@ -69,78 +69,5 @@ public static IntPtr LoadLibraryFromSystemPathIfAvailable(string libraryName) } #endregion - - #region PInvoke DpiRelated - // This section could go to Nativemethods.cs or Safenativemethods.cs but we have separate copies of them in each library (System.winforms, System.Design and System.Drawing). - // Keeping them here will reduce duplicating code but may have to take care of security warnings (if any). - // These APIs are available starting Windows 10, version 1607 only. - [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] - [ResourceExposure(ResourceScope.None)] - internal static extern DpiAwarenessContext GetThreadDpiAwarenessContext(); - - [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] - [ResourceExposure(ResourceScope.None)] - internal static extern DpiAwarenessContext SetThreadDpiAwarenessContext(DpiAwarenessContext dpiContext); - - [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool AreDpiAwarenessContextsEqual(DpiAwarenessContext dpiContextA, DpiAwarenessContext dpiContextB); - - /// - /// Tries to compare two DPIawareness context values. Return true if they were equal. - /// Return false when they are not equal or underlying OS does not support this API. - /// - /// true/false - public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext dpiContextA, DpiAwarenessContext dpiContextB) - { - if (ApiHelper.IsApiAvailable(ExternDll.User32, "AreDpiAwarenessContextsEqual")) - { - return AreDpiAwarenessContextsEqual(dpiContextA, dpiContextB); - } - - return false; - } - - /// - /// Tries to get thread dpi awareness context - /// - /// returns thread dpi awareness context if API is available in this version of OS. otherwise, return IntPtr.Zero. - public static DpiAwarenessContext TryGetThreadDpiAwarenessContext() - { - if (ApiHelper.IsApiAvailable(ExternDll.User32, "GetThreadDpiAwarenessContext")) - { - return GetThreadDpiAwarenessContext(); - } - else - { - // legacy OS that does not have this API available. - return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; - } - } - - /// - /// Tries to set thread dpi awareness context - /// - /// returns old thread dpi awareness context if API is available in this version of OS. otherwise, return IntPtr.Zero. - public static DpiAwarenessContext TrySetThreadDpiAwarenessContext(DpiAwarenessContext dpiCOntext) - { - if (ApiHelper.IsApiAvailable(ExternDll.User32, "SetThreadDpiAwarenessContext")) - { - return SetThreadDpiAwarenessContext(dpiCOntext); - } - else - { - // legacy OS that does not have this API available. - return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; - } - } -/* - // Dpi awareness context values. Matching windows values. - public static readonly DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_UNAWARE = (-1); - public static readonly DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = (-2); - public static readonly DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = (-3); - public static readonly DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = (-4); - public static readonly DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_UNSPECIFIED = (0);*/ - #endregion } } diff --git a/src/System.Windows.Forms.Design.Editors/src/Misc/DpiHelper.cs b/src/System.Windows.Forms.Design.Editors/src/Misc/DpiHelper.cs index cbbf420dae9..20315b6aa92 100644 --- a/src/System.Windows.Forms.Design.Editors/src/Misc/DpiHelper.cs +++ b/src/System.Windows.Forms.Design.Editors/src/Misc/DpiHelper.cs @@ -290,8 +290,8 @@ internal static bool EnableDpiChangedMessageHandling { // We can't cache this value because different top level windows can have different DPI awareness context // for mixed mode applications. - DpiAwarenessContext dpiAwareness = CommonUnsafeNativeMethods.GetThreadDpiAwarenessContext(); - return CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + DpiAwarenessContext dpiAwareness = DpiUnsafeNativeMethods.TryGetThreadDpiAwarenessContext(); + return DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); } else { diff --git a/src/System.Windows.Forms.Design.Editors/src/Misc/DpiUnsafeNativeMethods.cs b/src/System.Windows.Forms.Design.Editors/src/Misc/DpiUnsafeNativeMethods.cs new file mode 100644 index 00000000000..42efcf70719 --- /dev/null +++ b/src/System.Windows.Forms.Design.Editors/src/Misc/DpiUnsafeNativeMethods.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms +{ + using Runtime.InteropServices; + using Runtime.Versioning; + using System; + + internal class DpiUnsafeNativeMethods + { + + // This section could go to Nativemethods.cs or Safenativemethods.cs but we have separate copies of them in each library (System.winforms, System.Design and System.Drawing). + // Keeping them here will reduce duplicating code but may have to take care of security warnings (if any). + // These APIs are available starting Windows 10, version 1607 only. + + [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + internal static extern DpiAwarenessContext GetThreadDpiAwarenessContext(); + + [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + internal static extern DpiAwarenessContext SetThreadDpiAwarenessContext(DpiAwarenessContext dpiContext); + + [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool AreDpiAwarenessContextsEqual(DpiAwarenessContext dpiContextA, DpiAwarenessContext dpiContextB); + + [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + internal static extern DpiAwarenessContext GetWindowDpiAwarenessContext(IntPtr hwnd); + + /// + /// Tries to compare two DPIawareness context values. Return true if they were equal. + /// Return false when they are not equal or underlying OS does not support this API. + /// + /// true/false + public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext dpiContextA, DpiAwarenessContext dpiContextB) + { + if(dpiContextA == DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED && dpiContextB == DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) + { + return true; + } + if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.AreDpiAwarenessContextsEqual))) + { + return AreDpiAwarenessContextsEqual(dpiContextA, dpiContextB); + } + + return false; + } + + /// + /// Tries to get thread dpi awareness context + /// + /// returns thread dpi awareness context if API is available in this version of OS. otherwise, return IntPtr.Zero. + public static DpiAwarenessContext TryGetThreadDpiAwarenessContext() + { + if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.GetThreadDpiAwarenessContext))) + { + return GetThreadDpiAwarenessContext(); + } + else + { + // legacy OS that does not have this API available. + return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; + } + } + + /// + /// Tries to set thread dpi awareness context + /// + /// returns old thread dpi awareness context if API is available in this version of OS. otherwise, return IntPtr.Zero. + public static DpiAwarenessContext TrySetThreadDpiAwarenessContext(DpiAwarenessContext dpiContext) + { + if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.SetThreadDpiAwarenessContext))) + { + if (dpiContext == DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) + { + throw new ArgumentException(nameof(dpiContext), dpiContext.ToString()); + } + return SetThreadDpiAwarenessContext(dpiContext); + } + else + { + // legacy OS that does not have this API available. + return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; + } + } + + /// + /// Tries to get window dpi awareness context + /// + /// returns window dpi awareness context if API is available in this version of OS. otherwise, return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED. + public static DpiAwarenessContext TryGetWindowDpiAwarenessContext(IntPtr hwnd) + { + if (ApiHelper.IsApiAvailable(ExternDll.User32, "GetWindowDpiAwarenessContext")) + { + return GetWindowDpiAwarenessContext(hwnd); + } + else + { + // legacy OS that does not have this API available. + return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; + } + } + } +} \ No newline at end of file diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs index fa32147d775..6e303d688fe 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs @@ -2629,7 +2629,7 @@ internal ParkingWindow GetParkingWindowForContext(DpiAwarenessContext context) { // Legacy OS/target framework scenario where ControlDpiContext is set to DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_UNSPECIFIED // because of 'ThreadContextDpiAwareness' API unavailability or this feature is not enabled. - if (!DpiHelper.IsScalingRequirementMet || CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(context, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)) { + if (!DpiHelper.IsScalingRequirementMet || DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(context, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)) { Debug.Assert(parkingWindows.Count == 1, "parkingWindows count can not be > 1 for legacy OS/target framework versions"); return parkingWindows[0]; @@ -2637,7 +2637,7 @@ internal ParkingWindow GetParkingWindowForContext(DpiAwarenessContext context) { // Supported OS scenario. foreach (var p in parkingWindows) { - if (CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(p.DpiAwarenessContext, context)) { + if (DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(p.DpiAwarenessContext, context)) { return p; } } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs index 3cf29da5687..9732fe85502 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs @@ -5682,7 +5682,7 @@ protected virtual void CreateHandle() { // if (cp.Parent == IntPtr.Zero && (cp.Style & NativeMethods.WS_CHILD) != 0) { Debug.Assert((cp.ExStyle & NativeMethods.WS_EX_MDICHILD) == 0, "Can't put MDI child forms on the parking form"); - Application.ParkHandle(cp); + Application.ParkHandle(cp, window.DpiAwarenessContext); } window.CreateHandle(cp); @@ -5703,6 +5703,11 @@ protected virtual void CreateHandle() { } } + protected void ApplicationParkHandleWithWindowContext(HandleRef handle) + { + Application.ParkHandle(handle, window.DpiAwarenessContext); + } + /// /// /// Forces the creation of the control. This includes the creation of the handle, @@ -8226,7 +8231,7 @@ internal virtual void OnParentHandleRecreating() { // use SetParent directly so as to not raise ParentChanged events if (IsHandleCreated) { - Application.ParkHandle(new HandleRef(this, this.Handle)); + Application.ParkHandle(new HandleRef(this, this.Handle), window.DpiAwarenessContext); } } @@ -11622,7 +11627,7 @@ private void SetParentHandle(IntPtr value) { } if (!GetTopLevel()) { if (value == IntPtr.Zero) { - Application.ParkHandle(new HandleRef(window, Handle)); + Application.ParkHandle(new HandleRef(window, Handle), window.DpiAwarenessContext); UpdateRoot(); } else { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/NativeWindow.cs b/src/System.Windows.Forms/src/System/Windows/Forms/NativeWindow.cs index 80c8f7922fa..e1cb95b67d7 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/NativeWindow.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/NativeWindow.cs @@ -102,7 +102,7 @@ public class NativeWindow : MarshalByRefObject, IWin32Window { NativeWindow nextWindow; WeakReference weakThisPtr; private DpiAwarenessContext windowDpiAwarenessContext = DpiHelper.IsScalingRequirementMet ? - CommonUnsafeNativeMethods.TryGetThreadDpiAwarenessContext() : + DpiUnsafeNativeMethods.TryGetThreadDpiAwarenessContext() : DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; static NativeWindow() { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/WebBrowserBase.cs b/src/System.Windows.Forms/src/System/Windows/Forms/WebBrowserBase.cs index 62cb36b22ed..0b27cce03c2 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/WebBrowserBase.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/WebBrowserBase.cs @@ -488,7 +488,7 @@ protected override void WndProc(ref Message m) { if (this.ActiveXState >= WebBrowserHelper.AXState.InPlaceActive) { IntPtr hwndInPlaceObject; if (NativeMethods.Succeeded(this.AXInPlaceObject.GetWindow(out hwndInPlaceObject))) { - Application.ParkHandle(new HandleRef(this.AXInPlaceObject, hwndInPlaceObject)); + this.ApplicationParkHandleWithWindowContext(new HandleRef(this.AXInPlaceObject, hwndInPlaceObject)); } } diff --git a/src/System.Windows.Forms/src/misc/CommonUnsafeNativeMethods.cs b/src/System.Windows.Forms/src/misc/CommonUnsafeNativeMethods.cs index 1d629eaa717..6d71943d9ca 100644 --- a/src/System.Windows.Forms/src/misc/CommonUnsafeNativeMethods.cs +++ b/src/System.Windows.Forms/src/misc/CommonUnsafeNativeMethods.cs @@ -23,6 +23,7 @@ internal class CommonUnsafeNativeMethods [DllImport(ExternDll.Kernel32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Ansi)] [ResourceExposure(ResourceScope.Process)] public static extern IntPtr GetProcAddress(HandleRef hModule, string lpProcName); + [DllImport(ExternDll.Kernel32, SetLastError = true, CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.Process)] public static extern IntPtr GetModuleHandle(string modName); @@ -69,86 +70,5 @@ public static IntPtr LoadLibraryFromSystemPathIfAvailable(string libraryName) } #endregion - - #region PInvoke DpiRelated - // This section could go to Nativemethods.cs or Safenativemethods.cs but we have separate copies of them in each library (System.winforms, System.Design and System.Drawing). - // Keeping them here will reduce duplicating code but may have to take care of security warnings (if any). - // These APIs are available starting Windows 10, version 1607 only. - [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] - [ResourceExposure(ResourceScope.None)] - internal static extern DpiAwarenessContext GetThreadDpiAwarenessContext(); - - [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] - [ResourceExposure(ResourceScope.None)] - internal static extern DpiAwarenessContext SetThreadDpiAwarenessContext(DpiAwarenessContext dpiContext); - - [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool AreDpiAwarenessContextsEqual(DpiAwarenessContext dpiContextA, DpiAwarenessContext dpiContextB); - - /// - /// Tries to compare two DPIawareness context values. Return true if they were equal. - /// Return false when they are not equal or underlying OS does not support this API. - /// - /// true/false - public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext dpiContextA, DpiAwarenessContext dpiContextB) - { - if(dpiContextA == DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED && dpiContextB == DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) - { - return true; - } - if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(CommonUnsafeNativeMethods.AreDpiAwarenessContextsEqual))) - { - return AreDpiAwarenessContextsEqual(dpiContextA, dpiContextB); - } - - return false; - } - - /// - /// Tries to get thread dpi awareness context - /// - /// returns thread dpi awareness context if API is available in this version of OS. otherwise, return IntPtr.Zero. - public static DpiAwarenessContext TryGetThreadDpiAwarenessContext() - { - if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(CommonUnsafeNativeMethods.GetThreadDpiAwarenessContext))) - { - return GetThreadDpiAwarenessContext(); - } - else - { - // legacy OS that does not have this API available. - return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; - } - } - - /// - /// Tries to set thread dpi awareness context - /// - /// returns old thread dpi awareness context if API is available in this version of OS. otherwise, return IntPtr.Zero. - public static DpiAwarenessContext TrySetThreadDpiAwarenessContext(DpiAwarenessContext dpiContext) - { - if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(CommonUnsafeNativeMethods.SetThreadDpiAwarenessContext))) - { - if (dpiContext == DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) - { - throw new ArgumentException(nameof(dpiContext), dpiContext.ToString()); - } - return SetThreadDpiAwarenessContext(dpiContext); - } - else - { - // legacy OS that does not have this API available. - return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; - } - } -/* - // Dpi awareness context values. Matching windows values. - public static readonly DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_UNAWARE = (-1); - public static readonly DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = (-2); - public static readonly DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = (-3); - public static readonly DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = (-4); - public static readonly DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_UNSPECIFIED = (0);*/ - #endregion } } diff --git a/src/System.Windows.Forms/src/misc/DpiHelper.DpiAwarenessContext.cs b/src/System.Windows.Forms/src/misc/DpiHelper.DpiAwarenessContext.cs index 164bdc05712..c9da11abf65 100644 --- a/src/System.Windows.Forms/src/misc/DpiHelper.DpiAwarenessContext.cs +++ b/src/System.Windows.Forms/src/misc/DpiHelper.DpiAwarenessContext.cs @@ -54,16 +54,19 @@ public DpiAwarenessScope(DpiAwarenessContext awareness) { try { - if (!CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(awareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)) + if (!DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(awareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)) { - originalAwareness = CommonUnsafeNativeMethods.GetThreadDpiAwarenessContext(); + originalAwareness = DpiUnsafeNativeMethods.TryGetThreadDpiAwarenessContext(); - // If current process dpiawareness is SYSTEM_UNAWARE or SYSTEM_AWARE (must be equal to awareness), calling this method will be a no-op. - if (!CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(originalAwareness, awareness) && - !CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(originalAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNAWARE)) + // If desired awareness is not equal to current awareness + if (!DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(originalAwareness, awareness)) { - originalAwareness = CommonUnsafeNativeMethods.SetThreadDpiAwarenessContext(awareness); - dpiAwarenessScopeIsSet = true; + // Unaware is allowed to open Unaware only; System Aware may open System and Unaware; etc... + if (CanContextAParentContextB(originalAwareness, awareness)) + { + originalAwareness = DpiUnsafeNativeMethods.TrySetThreadDpiAwarenessContext(awareness); + dpiAwarenessScopeIsSet = true; + } } } } @@ -73,6 +76,43 @@ public DpiAwarenessScope(DpiAwarenessContext awareness) } } + /// + /// Returns whether or not a window in context A should be allowed to open a window in context B + /// Unaware windows may only open children in unaware mode; System Aware windows may open children in System Aware and Unaware; + /// PMV1 windows may open children in PMV1, System Aware, and Unaware; finally PMV2 windows may open children in PMV2, PMV1, System Aware, and Unaware + /// + /// + /// + /// + /// If a new member of the enumeration is added, this code will also have to change + /// It would be better if there was a way to convert from the bit-masked dpi awareness to our own enumeration and compare the magnitude + private bool CanContextAParentContextB(DpiAwarenessContext dpiAwarenessContextA, DpiAwarenessContext dpiAwarenessContextB) + { + if (DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextA, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) + { + return DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextB, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) || + DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextB, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE) || + DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextB, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE) || + DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextB, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNAWARE); + } + else if (DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextA, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) + { + return DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextB, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE) || + DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextB, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE) || + DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextB, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNAWARE); + } + else if (DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextA, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE)) + { + return DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextB, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE) || + DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextB, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNAWARE); + } + else if (DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextA, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNAWARE)) + { + return DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextB, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNAWARE); + } + return false; + } + /// /// Dispose object and resources /// @@ -88,7 +128,7 @@ private void ResetDpiAwarenessContextChanges() { if (dpiAwarenessScopeIsSet) { - CommonUnsafeNativeMethods.TrySetThreadDpiAwarenessContext(originalAwareness); + DpiUnsafeNativeMethods.TrySetThreadDpiAwarenessContext(originalAwareness); dpiAwarenessScopeIsSet = false; } } diff --git a/src/System.Windows.Forms/src/misc/DpiHelper.cs b/src/System.Windows.Forms/src/misc/DpiHelper.cs index 49f72191a90..9ffd6788afa 100644 --- a/src/System.Windows.Forms/src/misc/DpiHelper.cs +++ b/src/System.Windows.Forms/src/misc/DpiHelper.cs @@ -70,7 +70,7 @@ internal static void InitializeDpiHelperForWinforms() Initialize(); // We are in Windows 10/1603 or greater when this API is present. - if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(CommonUnsafeNativeMethods.GetThreadDpiAwarenessContext))) + if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(SafeNativeMethods.GetProcessDpiAwareness))) { // We are on Windows 10/1603 or greater all right, but we could still be DpiUnaware or SystemAware, so let's find that out... @@ -107,8 +107,8 @@ internal static bool IsPerMonitorV2Awareness { // We can't cache this value because different top level windows can have different DPI awareness context // for mixed mode applications. - DpiAwarenessContext dpiAwareness = CommonUnsafeNativeMethods.GetThreadDpiAwarenessContext(); - return CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + DpiAwarenessContext dpiAwareness = DpiUnsafeNativeMethods.TryGetThreadDpiAwarenessContext(); + return DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); } else { diff --git a/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs b/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs new file mode 100644 index 00000000000..2f9ad6d9e1f --- /dev/null +++ b/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms +{ + using Runtime.InteropServices; + using Runtime.Versioning; + using System; + + internal class DpiUnsafeNativeMethods + { + + // This section could go to Nativemethods.cs or Safenativemethods.cs but we have separate copies of them in each library (System.winforms, System.Design and System.Drawing). + // Keeping them here will reduce duplicating code but may have to take care of security warnings (if any). + // These APIs are available starting Windows 10, version 1607 only. + + [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern DpiAwarenessContext GetThreadDpiAwarenessContext(); + + [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern DpiAwarenessContext SetThreadDpiAwarenessContext(DpiAwarenessContext dpiContext); + + [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool AreDpiAwarenessContextsEqual(DpiAwarenessContext dpiContextA, DpiAwarenessContext dpiContextB); + + [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + private static extern DpiAwarenessContext GetWindowDpiAwarenessContext(IntPtr hwnd); + + /// + /// Tries to compare two DPIawareness context values. Return true if they were equal. + /// Return false when they are not equal or underlying OS does not support this API. + /// + /// true/false + public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext dpiContextA, DpiAwarenessContext dpiContextB) + { + if(dpiContextA == DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED && dpiContextB == DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) + { + return true; + } + if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.AreDpiAwarenessContextsEqual))) + { + return AreDpiAwarenessContextsEqual(dpiContextA, dpiContextB); + } + + return false; + } + + /// + /// Tries to get thread dpi awareness context + /// + /// returns thread dpi awareness context if API is available in this version of OS. otherwise, return IntPtr.Zero. + public static DpiAwarenessContext TryGetThreadDpiAwarenessContext() + { + if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.GetThreadDpiAwarenessContext))) + { + return GetThreadDpiAwarenessContext(); + } + else + { + // legacy OS that does not have this API available. + return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; + } + } + + /// + /// Tries to set thread dpi awareness context + /// + /// returns old thread dpi awareness context if API is available in this version of OS. otherwise, return IntPtr.Zero. + public static DpiAwarenessContext TrySetThreadDpiAwarenessContext(DpiAwarenessContext dpiContext) + { + if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.SetThreadDpiAwarenessContext))) + { + if (dpiContext == DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) + { + throw new ArgumentException(nameof(dpiContext), dpiContext.ToString()); + } + return SetThreadDpiAwarenessContext(dpiContext); + } + else + { + // legacy OS that does not have this API available. + return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; + } + } + + /// + /// Tries to get window dpi awareness context + /// + /// returns window dpi awareness context if API is available in this version of OS. otherwise, return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED. + public static DpiAwarenessContext TryGetWindowDpiAwarenessContext(IntPtr hwnd) + { + if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.GetWindowDpiAwarenessContext))) + { + return GetWindowDpiAwarenessContext(hwnd); + } + else + { + // legacy OS that does not have this API available. + return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; + } + } + } +} \ No newline at end of file diff --git a/src/System.Windows.Forms/tests/UnitTests/CommonUnsafenativeMethods.cs b/src/System.Windows.Forms/tests/UnitTests/CommonUnsafenativeMethods.cs index f3c33a15cd7..c95105523a0 100644 --- a/src/System.Windows.Forms/tests/UnitTests/CommonUnsafenativeMethods.cs +++ b/src/System.Windows.Forms/tests/UnitTests/CommonUnsafenativeMethods.cs @@ -7,7 +7,7 @@ namespace System.Windows.Forms.Tests { - public class CommonUnsafeNativeMethodsTests + public class DpiUnsafeNativeMethodsTests { @@ -20,16 +20,16 @@ public class CommonUnsafeNativeMethodsTests Theory] [MemberData(nameof(TryFindDpiAwarenessContextsEqualData))] - internal void CommonUnsafeNativeMathods_TryFindDpiAwarenessContextsEqual(DpiAwarenessContext item) + internal void DpiUnsafeNativeMethods_TryFindDpiAwarenessContextsEqual(DpiAwarenessContext item) { - Assert.True(CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(item, item)); + Assert.True(DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(item, item)); } [Fact] - public void CommonUnsafeNativeMethods_AreDpiAwarenessContextsEqualNotForUnspecified() + public void DpiUnsafeNativeMethods_AreDpiAwarenessContextsEqualNotForUnspecified() { - Assert.False(CommonUnsafeNativeMethods.AreDpiAwarenessContextsEqual(DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)); + Assert.False(DpiUnsafeNativeMethods.AreDpiAwarenessContextsEqual(DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)); } /// @@ -40,17 +40,17 @@ public void CommonUnsafeNativeMethods_AreDpiAwarenessContextsEqualNotForUnspecif [Theory] [MemberData(nameof(TrySetThreadDpiAwarenessContextGetSetData))] - internal void CommonUnsafeNativeMethods_TrySetThreadDpiAwarenessContextGetSet(DpiAwarenessContext item) + internal void DpiUnsafeNativeMethods_TrySetThreadDpiAwarenessContextGetSet(DpiAwarenessContext item) { if (item != DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) { - CommonUnsafeNativeMethods.TrySetThreadDpiAwarenessContext(item); + DpiUnsafeNativeMethods.TrySetThreadDpiAwarenessContext(item); - Assert.True(CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(item, CommonUnsafeNativeMethods.TryGetThreadDpiAwarenessContext())); + Assert.True(DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(item, DpiUnsafeNativeMethods.TryGetThreadDpiAwarenessContext())); } else { - var ex = Assert.Throws(() => CommonUnsafeNativeMethods.TrySetThreadDpiAwarenessContext(DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)); + var ex = Assert.Throws(() => DpiUnsafeNativeMethods.TrySetThreadDpiAwarenessContext(DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)); Assert.Equal(DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED.ToString(), ex.ParamName); } }*/ From 1f15d6126d465a6414dbd2e334527315f2c935d2 Mon Sep 17 00:00:00 2001 From: Zachary Danz Date: Wed, 30 Jan 2019 10:10:18 -0800 Subject: [PATCH 2/6] nullable dpiAwarenessContext --- .../src/System/Windows/Forms/Application.cs | 17 ++++------- .../src/System/Windows/Forms/Control.cs | 2 +- .../src/System/Windows/Forms/NativeWindow.cs | 8 ++--- .../src/misc/DpiHelper.DpiAwarenessContext.cs | 16 ++++++---- .../src/misc/DpiHelper.cs | 7 ++--- .../src/misc/DpiUnsafeNativeMethods.cs | 29 +++++++++++-------- 6 files changed, 41 insertions(+), 38 deletions(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs index 6e303d688fe..36b2bb7e43f 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs @@ -1305,7 +1305,7 @@ private static void RaiseThreadExit() { /// "Parks" the given HWND to a temporary HWND. This allows WS_CHILD windows to /// be parked. /// - internal static void ParkHandle(HandleRef handle, DpiAwarenessContext dpiAwarenessContext = DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) { + internal static void ParkHandle(HandleRef handle, DpiAwarenessContext? dpiAwarenessContext = null) { Debug.Assert(UnsafeNativeMethods.IsWindow(handle), "Handle being parked is not a valid window handle"); Debug.Assert(((int)UnsafeNativeMethods.GetWindowLong(handle, NativeMethods.GWL_STYLE) & NativeMethods.WS_CHILD) != 0, "Only WS_CHILD windows should be parked."); @@ -1320,7 +1320,7 @@ internal static void ParkHandle(HandleRef handle, DpiAwarenessContext dpiAwarene /// /// create params for control handle /// dpi awareness - internal static void ParkHandle(CreateParams cp, DpiAwarenessContext dpiAwarenessContext = DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) { + internal static void ParkHandle(CreateParams cp, DpiAwarenessContext? dpiAwarenessContext = null) { ThreadContext cxt = ThreadContext.FromCurrent(); if (cxt != null) { @@ -1352,7 +1352,7 @@ public static void OnThreadException(Exception t) { /// "Unparks" the given HWND to a temporary HWND. This allows WS_CHILD windows to /// be parked. /// - internal static void UnparkHandle(HandleRef handle, DpiAwarenessContext context) { + internal static void UnparkHandle(HandleRef handle, DpiAwarenessContext? context) { ThreadContext cxt = GetContextForHandle(handle); if (cxt != null) { cxt.GetParkingWindow(context).UnparkHandle(handle); @@ -2581,7 +2581,7 @@ internal bool CustomThreadExceptionHandlerAttached { /// if it needs to. /// /// - internal ParkingWindow GetParkingWindow(DpiAwarenessContext context) { + internal ParkingWindow GetParkingWindow(DpiAwarenessContext? context) { // Locking 'this' here is ok since this is an internal class. lock(this) { @@ -2595,11 +2595,6 @@ internal ParkingWindow GetParkingWindow(DpiAwarenessContext context) { Debug.WriteLine(CoreSwitches.PerfTrack.Enabled, Environment.StackTrace); } #endif - - - - - IntSecurity.ManipulateWndProcAndHandles.Assert(); try { using (DpiHelper.EnterDpiAwarenessScope(context)) { @@ -2620,7 +2615,7 @@ internal ParkingWindow GetParkingWindow(DpiAwarenessContext context) { /// Returns parking window that matches dpi awareness context. return null if not found. /// /// return matching parking window from list. returns null if not found - internal ParkingWindow GetParkingWindowForContext(DpiAwarenessContext context) { + internal ParkingWindow GetParkingWindowForContext(DpiAwarenessContext? context) { if (parkingWindows.Count == 0) { return null; @@ -2629,7 +2624,7 @@ internal ParkingWindow GetParkingWindowForContext(DpiAwarenessContext context) { // Legacy OS/target framework scenario where ControlDpiContext is set to DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_UNSPECIFIED // because of 'ThreadContextDpiAwareness' API unavailability or this feature is not enabled. - if (!DpiHelper.IsScalingRequirementMet || DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(context, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)) { + if (!DpiHelper.IsScalingRequirementMet || DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(context, null)) { Debug.Assert(parkingWindows.Count == 1, "parkingWindows count can not be > 1 for legacy OS/target framework versions"); return parkingWindows[0]; diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs index 9732fe85502..9485eb7ef90 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs @@ -589,7 +589,7 @@ public Control( Control parent, string text, int left, int top, int width, int h /// /// gets or sets control Dpi awareness context value. /// - internal DpiAwarenessContext DpiAwarenessContext { + internal DpiAwarenessContext? DpiAwarenessContext { get { return window.DpiAwarenessContext; } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/NativeWindow.cs b/src/System.Windows.Forms/src/System/Windows/Forms/NativeWindow.cs index e1cb95b67d7..2481cd24cbc 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/NativeWindow.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/NativeWindow.cs @@ -101,9 +101,9 @@ public class NativeWindow : MarshalByRefObject, IWin32Window { NativeWindow previousWindow; // doubly linked list of subclasses. NativeWindow nextWindow; WeakReference weakThisPtr; - private DpiAwarenessContext windowDpiAwarenessContext = DpiHelper.IsScalingRequirementMet ? - DpiUnsafeNativeMethods.TryGetThreadDpiAwarenessContext() : - DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; + private DpiAwarenessContext? windowDpiAwarenessContext = DpiHelper.IsScalingRequirementMet ? + (DpiAwarenessContext?)DpiUnsafeNativeMethods.TryGetThreadDpiAwarenessContext() : + null; static NativeWindow() { EventHandler shutdownHandler = new EventHandler(OnShutdown); @@ -133,7 +133,7 @@ public NativeWindow() { /// /// Cache window DpiContext awareness information that helps to create handle with right context at the later time. /// - internal DpiAwarenessContext DpiAwarenessContext { + internal DpiAwarenessContext? DpiAwarenessContext { get { return windowDpiAwarenessContext; } diff --git a/src/System.Windows.Forms/src/misc/DpiHelper.DpiAwarenessContext.cs b/src/System.Windows.Forms/src/misc/DpiHelper.DpiAwarenessContext.cs index c9da11abf65..fb44571b645 100644 --- a/src/System.Windows.Forms/src/misc/DpiHelper.DpiAwarenessContext.cs +++ b/src/System.Windows.Forms/src/misc/DpiHelper.DpiAwarenessContext.cs @@ -17,7 +17,7 @@ internal static partial class DpiHelper /// The new DPI awareness for the current thread /// An object that, when disposed, will reset the current thread's /// DPI awareness to the value it had when the object was created. - public static IDisposable EnterDpiAwarenessScope(DpiAwarenessContext awareness) + public static IDisposable EnterDpiAwarenessScope(DpiAwarenessContext? awareness) { return new DpiAwarenessScope(awareness); } @@ -45,16 +45,16 @@ public static T CreateInstanceInSystemAwareContext(Func createInstance) private class DpiAwarenessScope : IDisposable { private bool dpiAwarenessScopeIsSet = false; - private DpiAwarenessContext originalAwareness = DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; + private DpiAwarenessContext? originalAwareness = null; /// /// Enters given Dpi awareness scope /// - public DpiAwarenessScope(DpiAwarenessContext awareness) + public DpiAwarenessScope(DpiAwarenessContext? awareness) { try { - if (!DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(awareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)) + if (!DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(awareness, null)) { originalAwareness = DpiUnsafeNativeMethods.TryGetThreadDpiAwarenessContext(); @@ -86,9 +86,13 @@ public DpiAwarenessScope(DpiAwarenessContext awareness) /// /// If a new member of the enumeration is added, this code will also have to change /// It would be better if there was a way to convert from the bit-masked dpi awareness to our own enumeration and compare the magnitude - private bool CanContextAParentContextB(DpiAwarenessContext dpiAwarenessContextA, DpiAwarenessContext dpiAwarenessContextB) + private bool CanContextAParentContextB(DpiAwarenessContext? dpiAwarenessContextA, DpiAwarenessContext? dpiAwarenessContextB) { - if (DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextA, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) + if(dpiAwarenessContextA == null || dpiAwarenessContextB == null) + { + return false; // null indicates unspecified, which Windows will not understand + } + else if (DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextA, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) { return DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextB, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) || DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwarenessContextB, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE) || diff --git a/src/System.Windows.Forms/src/misc/DpiHelper.cs b/src/System.Windows.Forms/src/misc/DpiHelper.cs index 9ffd6788afa..4c58b7236de 100644 --- a/src/System.Windows.Forms/src/misc/DpiHelper.cs +++ b/src/System.Windows.Forms/src/misc/DpiHelper.cs @@ -105,9 +105,9 @@ internal static bool IsPerMonitorV2Awareness InitializeDpiHelperForWinforms(); if (doesNeedQueryForPerMonitorV2Awareness) { - // We can't cache this value because different top level windows can have different DPI awareness context - // for mixed mode applications. - DpiAwarenessContext dpiAwareness = DpiUnsafeNativeMethods.TryGetThreadDpiAwarenessContext(); + // We can't cache this value because different top level windows can + // have different DPI awareness context for mixed mode applications. + DpiAwarenessContext? dpiAwareness = DpiUnsafeNativeMethods.TryGetThreadDpiAwarenessContext(); return DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); } else @@ -340,7 +340,6 @@ public static void ScaleButtonImageLogicalToDevice(Button button) internal enum DpiAwarenessContext { - DPI_AWARENESS_CONTEXT_UNSPECIFIED = 0, DPI_AWARENESS_CONTEXT_UNAWARE = -1, DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = -2, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = -3, diff --git a/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs b/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs index 2f9ad6d9e1f..f0de6653b46 100644 --- a/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs +++ b/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs @@ -36,15 +36,20 @@ internal class DpiUnsafeNativeMethods /// Return false when they are not equal or underlying OS does not support this API. /// /// true/false - public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext dpiContextA, DpiAwarenessContext dpiContextB) + public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext? dpiContextA, DpiAwarenessContext? dpiContextB) { - if(dpiContextA == DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED && dpiContextB == DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) + if(dpiContextA == null) { - return true; + return (dpiContextB == null); // true if both null but not either } + else if (dpiContextB == null) + { + return false; // because we know A is not null + } + if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.AreDpiAwarenessContextsEqual))) { - return AreDpiAwarenessContextsEqual(dpiContextA, dpiContextB); + return AreDpiAwarenessContextsEqual((DpiAwarenessContext)dpiContextA, (DpiAwarenessContext)dpiContextB); } return false; @@ -54,7 +59,7 @@ public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext dpiConte /// Tries to get thread dpi awareness context /// /// returns thread dpi awareness context if API is available in this version of OS. otherwise, return IntPtr.Zero. - public static DpiAwarenessContext TryGetThreadDpiAwarenessContext() + public static DpiAwarenessContext? TryGetThreadDpiAwarenessContext() { if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.GetThreadDpiAwarenessContext))) { @@ -63,7 +68,7 @@ public static DpiAwarenessContext TryGetThreadDpiAwarenessContext() else { // legacy OS that does not have this API available. - return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; + return null; } } @@ -71,20 +76,20 @@ public static DpiAwarenessContext TryGetThreadDpiAwarenessContext() /// Tries to set thread dpi awareness context /// /// returns old thread dpi awareness context if API is available in this version of OS. otherwise, return IntPtr.Zero. - public static DpiAwarenessContext TrySetThreadDpiAwarenessContext(DpiAwarenessContext dpiContext) + public static DpiAwarenessContext? TrySetThreadDpiAwarenessContext(DpiAwarenessContext? dpiContext) { if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.SetThreadDpiAwarenessContext))) { - if (dpiContext == DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) + if (dpiContext == null) { throw new ArgumentException(nameof(dpiContext), dpiContext.ToString()); } - return SetThreadDpiAwarenessContext(dpiContext); + return SetThreadDpiAwarenessContext((DpiAwarenessContext)dpiContext); } else { // legacy OS that does not have this API available. - return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; + return null; } } @@ -92,7 +97,7 @@ public static DpiAwarenessContext TrySetThreadDpiAwarenessContext(DpiAwarenessCo /// Tries to get window dpi awareness context /// /// returns window dpi awareness context if API is available in this version of OS. otherwise, return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED. - public static DpiAwarenessContext TryGetWindowDpiAwarenessContext(IntPtr hwnd) + public static DpiAwarenessContext? TryGetWindowDpiAwarenessContext(IntPtr hwnd) { if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.GetWindowDpiAwarenessContext))) { @@ -101,7 +106,7 @@ public static DpiAwarenessContext TryGetWindowDpiAwarenessContext(IntPtr hwnd) else { // legacy OS that does not have this API available. - return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; + return null; } } } From a2880b3e13d846bcf23c677e475010a719bdb110 Mon Sep 17 00:00:00 2001 From: Zachary Danz Date: Wed, 30 Jan 2019 10:20:04 -0800 Subject: [PATCH 3/6] fix exception thrown, fix tests --- .../src/misc/DpiUnsafeNativeMethods.cs | 2 +- ...hods.cs => DpiUnsafeNativeMethodsTests.cs} | 44 ++++++++++--------- 2 files changed, 25 insertions(+), 21 deletions(-) rename src/System.Windows.Forms/tests/UnitTests/{CommonUnsafenativeMethods.cs => DpiUnsafeNativeMethodsTests.cs} (55%) diff --git a/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs b/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs index f0de6653b46..6d52ca6cbc5 100644 --- a/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs +++ b/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs @@ -82,7 +82,7 @@ public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext? dpiCont { if (dpiContext == null) { - throw new ArgumentException(nameof(dpiContext), dpiContext.ToString()); + throw new ArgumentNullException(); } return SetThreadDpiAwarenessContext((DpiAwarenessContext)dpiContext); } diff --git a/src/System.Windows.Forms/tests/UnitTests/CommonUnsafenativeMethods.cs b/src/System.Windows.Forms/tests/UnitTests/DpiUnsafeNativeMethodsTests.cs similarity index 55% rename from src/System.Windows.Forms/tests/UnitTests/CommonUnsafenativeMethods.cs rename to src/System.Windows.Forms/tests/UnitTests/DpiUnsafeNativeMethodsTests.cs index c95105523a0..c2c83561314 100644 --- a/src/System.Windows.Forms/tests/UnitTests/CommonUnsafenativeMethods.cs +++ b/src/System.Windows.Forms/tests/UnitTests/DpiUnsafeNativeMethodsTests.cs @@ -9,28 +9,34 @@ namespace System.Windows.Forms.Tests { public class DpiUnsafeNativeMethodsTests { - - - /*/// + /// /// Data for the TryFindDpiAwarenessContextsEqual test /// public static TheoryData TryFindDpiAwarenessContextsEqualData => CommonTestHelper.GetEnumTheoryData(); - Theory] + [Theory] [MemberData(nameof(TryFindDpiAwarenessContextsEqualData))] internal void DpiUnsafeNativeMethods_TryFindDpiAwarenessContextsEqual(DpiAwarenessContext item) { Assert.True(DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(item, item)); } - + + [Theory] + [MemberData(nameof(TryFindDpiAwarenessContextsEqualData))] + internal void DpiUnsafeNativeMethods_TryFindDpiAwarenessContextsEqual_CompareToNull(DpiAwarenessContext item) + { + Assert.False(DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(item, null)); + Assert.False(DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(null, item)); + } [Fact] - public void DpiUnsafeNativeMethods_AreDpiAwarenessContextsEqualNotForUnspecified() - { - Assert.False(DpiUnsafeNativeMethods.AreDpiAwarenessContextsEqual(DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)); + internal void DpiUnsafeNativeMethods_TryFindDpiAwarenessContextsEqual_BothNull() + { + Assert.True(DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(null, null)); } + /// /// Data for the TrySetThreadDpiAwarenessContextGetSet test @@ -42,18 +48,16 @@ public void DpiUnsafeNativeMethods_AreDpiAwarenessContextsEqualNotForUnspecified [MemberData(nameof(TrySetThreadDpiAwarenessContextGetSetData))] internal void DpiUnsafeNativeMethods_TrySetThreadDpiAwarenessContextGetSet(DpiAwarenessContext item) { - if (item != DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) - { - DpiUnsafeNativeMethods.TrySetThreadDpiAwarenessContext(item); - - Assert.True(DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(item, DpiUnsafeNativeMethods.TryGetThreadDpiAwarenessContext())); - } - else - { - var ex = Assert.Throws(() => DpiUnsafeNativeMethods.TrySetThreadDpiAwarenessContext(DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)); - Assert.Equal(DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED.ToString(), ex.ParamName); - } - }*/ + DpiUnsafeNativeMethods.TrySetThreadDpiAwarenessContext(item); + + Assert.True(DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(item, DpiUnsafeNativeMethods.TryGetThreadDpiAwarenessContext())); + } + + [Fact] + internal void DPiUnsafeNativeMethods_TrySetThreadDpiAwarenessContext_Null() + { + Assert.Throws(() => DpiUnsafeNativeMethods.TrySetThreadDpiAwarenessContext(null)); + } } } From cd699e52bd3894eddb0f20b6365f7664738e0a2d Mon Sep 17 00:00:00 2001 From: Zachary Danz Date: Thu, 31 Jan 2019 16:11:01 -0800 Subject: [PATCH 4/6] reorganize dpihelper methods into submethods, add nullable --- .../src/misc/DpiHelper.cs | 108 ++++++++++++------ .../src/misc/DpiUnsafeNativeMethods.cs | 28 ++++- 2 files changed, 97 insertions(+), 39 deletions(-) diff --git a/src/System.Windows.Forms/src/misc/DpiHelper.cs b/src/System.Windows.Forms/src/misc/DpiHelper.cs index a78b93411a5..c6b26fcbd9f 100644 --- a/src/System.Windows.Forms/src/misc/DpiHelper.cs +++ b/src/System.Windows.Forms/src/misc/DpiHelper.cs @@ -341,47 +341,61 @@ public static void ScaleButtonImageLogicalToDevice(Button button) /// internal static bool FirstParkingWindowCreated {get; set;} + /// /// Gets the DPI awareness. /// /// The thread's/process' current HighDpi mode internal static HighDpiMode GetWinformsApplicationDpiAwareness() + { + return GetAppDpiAwarenessFromThread() ?? + GetAppDpiAwarenessFromProcess() ?? + GetAppDpiAwarenessFromIsDpiAware() ?? + HighDpiMode.DpiUnaware; + } + + // For Windows 10 RS2 and above + private static HighDpiMode? GetAppDpiAwarenessFromThread() { - // For Windows 10 RS2 and above - if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(CommonUnsafeNativeMethods.GetThreadDpiAwarenessContext))) + if (DpiUnsafeNativeMethods.GetThreadDpiAwarenessContextIsAvailable()) { - DpiAwarenessContext dpiAwareness = CommonUnsafeNativeMethods.GetThreadDpiAwarenessContext(); + DpiAwarenessContext? dpiAwareness = DpiUnsafeNativeMethods.TryGetThreadDpiAwarenessContext(); - if (CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE)) + if (DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNAWARE)) { - return HighDpiMode.SystemAware; + return HighDpiMode.DpiUnaware; } - if (CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNAWARE)) + if (DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_SYSTEM_AWARE)) { - return HighDpiMode.DpiUnaware; + return HighDpiMode.SystemAware; } - if (CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) + if (DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) { return HighDpiMode.PerMonitorV2; } - if (CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) + if (DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) { return HighDpiMode.PerMonitor; } - if (CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED)) + if (DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED)) { return HighDpiMode.DpiUnawareGdiScaled; } } - // For operating systems windows 8.1 to Windows 10 redstone 1 version. - else if (ApiHelper.IsApiAvailable(ExternDll.ShCore, nameof(SafeNativeMethods.GetProcessDpiAwareness))) + return null; + } + + // For operating systems windows 8.1 to Windows 10 redstone 1 version. + private static HighDpiMode? GetAppDpiAwarenessFromProcess() + { + if (ApiHelper.IsApiAvailable(ExternDll.ShCore, nameof(SafeNativeMethods.GetProcessDpiAwareness))) { - CAPS.PROCESS_DPI_AWARENESS processDpiAwareness; + CAPS.PROCESS_DPI_AWARENESS processDpiAwareness = CAPS.PROCESS_DPI_AWARENESS.PROCESS_DPI_UNAWARE; SafeNativeMethods.GetProcessDpiAwareness(IntPtr.Zero, out processDpiAwareness); switch (processDpiAwareness) @@ -395,27 +409,36 @@ internal static HighDpiMode GetWinformsApplicationDpiAwareness() } } - // For operating systems windows 7 to windows 8 - else if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(SafeNativeMethods.IsProcessDPIAware))) + return null; + } + + // For operating systems windows 7 to windows 8 + private static HighDpiMode? GetAppDpiAwarenessFromIsDpiAware() + { + if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(SafeNativeMethods.IsProcessDPIAware))) { return SafeNativeMethods.IsProcessDPIAware() ? - HighDpiMode.SystemAware : - HighDpiMode.DpiUnaware; + HighDpiMode.SystemAware : + HighDpiMode.DpiUnaware; } - // We should never get here, except someone ported this with force to < Windows 7. - return HighDpiMode.DpiUnaware; + return null; } - /// + /// /// Sets the DPI awareness. If not available on the current OS, it falls back to the next possible. /// /// true/false - If the process DPI awareness is successfully set, returns true. Otherwise false. internal static bool SetWinformsApplicationDpiAwareness(HighDpiMode highDpiMode) - { - NativeMethods.PROCESS_DPI_AWARENESS dpiFlag = NativeMethods.PROCESS_DPI_AWARENESS.PROCESS_DPI_UNINITIALIZED; + { + return SetAppDpiAwarenessWithAwarenessContext(highDpiMode) || + SetAppDpiAwarenessWithAwareness(highDpiMode) || + SetAppDpiAwarenessWithAware(highDpiMode); + } - // For Windows 10 RS2 and above + // For Windows 10 RS2 and above + private static bool SetAppDpiAwarenessWithAwarenessContext(HighDpiMode highDpiMode) + { if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(SafeNativeMethods.SetProcessDpiAwarenessContext))) { int rs2AndAboveDpiFlag; @@ -446,9 +469,16 @@ internal static bool SetWinformsApplicationDpiAwareness(HighDpiMode highDpiMode) return SafeNativeMethods.SetProcessDpiAwarenessContext(rs2AndAboveDpiFlag); } - // For operating systems Windows 8.1 to Windows 10 RS1 version. - else if (ApiHelper.IsApiAvailable(ExternDll.ShCore, nameof(SafeNativeMethods.SetProcessDpiAwareness))) + return false; + } + + // For operating systems Windows 8.1 to Windows 10 RS1 version. + private static bool SetAppDpiAwarenessWithAwareness(HighDpiMode highDpiMode) + { + if (ApiHelper.IsApiAvailable(ExternDll.ShCore, nameof(SafeNativeMethods.SetProcessDpiAwareness))) { + NativeMethods.PROCESS_DPI_AWARENESS dpiFlag = NativeMethods.PROCESS_DPI_AWARENESS.PROCESS_DPI_UNINITIALIZED; + switch (highDpiMode) { case HighDpiMode.DpiUnaware: @@ -467,11 +497,24 @@ internal static bool SetWinformsApplicationDpiAwareness(HighDpiMode highDpiMode) break; } - return SafeNativeMethods.SetProcessDpiAwareness(dpiFlag) != NativeMethods.S_OK; + int hResult = SafeNativeMethods.SetProcessDpiAwareness(dpiFlag); + if (hResult == NativeMethods.E_INVALIDARG) + { + throw new ArgumentException(string.Format("{0} is invalid for {1}", + dpiFlag.ToString(), + nameof(SafeNativeMethods.SetProcessDpiAwareness))); + } + + return hResult == NativeMethods.S_OK; } - // For operating systems windows 7 to windows 8 - else if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(SafeNativeMethods.SetProcessDPIAware))) + return false; + } + + // For operating systems windows 7 to windows 8 + private static bool SetAppDpiAwarenessWithAware(HighDpiMode highDpiMode) + { + if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(SafeNativeMethods.SetProcessDPIAware))) { switch (highDpiMode) { @@ -482,15 +525,10 @@ internal static bool SetWinformsApplicationDpiAwareness(HighDpiMode highDpiMode) case HighDpiMode.SystemAware: case HighDpiMode.PerMonitor: case HighDpiMode.PerMonitorV2: - dpiFlag = NativeMethods.PROCESS_DPI_AWARENESS.PROCESS_SYSTEM_DPI_AWARE; - break; - } - - if (dpiFlag == NativeMethods.PROCESS_DPI_AWARENESS.PROCESS_SYSTEM_DPI_AWARE) - { - return SafeNativeMethods.SetProcessDPIAware(); + return SafeNativeMethods.SetProcessDPIAware(); } } + return false; } } diff --git a/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs b/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs index 6d52ca6cbc5..ac0784e8396 100644 --- a/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs +++ b/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs @@ -31,6 +31,26 @@ internal class DpiUnsafeNativeMethods [ResourceExposure(ResourceScope.None)] private static extern DpiAwarenessContext GetWindowDpiAwarenessContext(IntPtr hwnd); + public static bool GetThreadDpiAwarenessContextIsAvailable() + { + return ApiHelper.IsApiAvailable(ExternDll.User32, nameof(GetThreadDpiAwarenessContext)); + } + + public static bool SetThreadDpiAwarenessContextIsAvailable() + { + return ApiHelper.IsApiAvailable(ExternDll.User32, nameof(SetThreadDpiAwarenessContext)); + } + + public static bool AreDpiAwarenessContextsEqualIsAvailable() + { + return ApiHelper.IsApiAvailable(ExternDll.User32, nameof(AreDpiAwarenessContextsEqual)); + } + + public static bool GetWindowDpiAwarenessContextIsAvailable() + { + return ApiHelper.IsApiAvailable(ExternDll.User32, nameof(GetWindowDpiAwarenessContext)); + } + /// /// Tries to compare two DPIawareness context values. Return true if they were equal. /// Return false when they are not equal or underlying OS does not support this API. @@ -47,7 +67,7 @@ public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext? dpiCont return false; // because we know A is not null } - if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.AreDpiAwarenessContextsEqual))) + if (AreDpiAwarenessContextsEqualIsAvailable()) { return AreDpiAwarenessContextsEqual((DpiAwarenessContext)dpiContextA, (DpiAwarenessContext)dpiContextB); } @@ -61,7 +81,7 @@ public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext? dpiCont /// returns thread dpi awareness context if API is available in this version of OS. otherwise, return IntPtr.Zero. public static DpiAwarenessContext? TryGetThreadDpiAwarenessContext() { - if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.GetThreadDpiAwarenessContext))) + if (GetThreadDpiAwarenessContextIsAvailable()) { return GetThreadDpiAwarenessContext(); } @@ -78,7 +98,7 @@ public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext? dpiCont /// returns old thread dpi awareness context if API is available in this version of OS. otherwise, return IntPtr.Zero. public static DpiAwarenessContext? TrySetThreadDpiAwarenessContext(DpiAwarenessContext? dpiContext) { - if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.SetThreadDpiAwarenessContext))) + if (SetThreadDpiAwarenessContextIsAvailable()) { if (dpiContext == null) { @@ -99,7 +119,7 @@ public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext? dpiCont /// returns window dpi awareness context if API is available in this version of OS. otherwise, return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED. public static DpiAwarenessContext? TryGetWindowDpiAwarenessContext(IntPtr hwnd) { - if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.GetWindowDpiAwarenessContext))) + if (GetWindowDpiAwarenessContextIsAvailable()) { return GetWindowDpiAwarenessContext(hwnd); } From b3889dd53777ece5e4f046835ac04eab0b44e329 Mon Sep 17 00:00:00 2001 From: Zachary Danz Date: Thu, 31 Jan 2019 16:21:20 -0800 Subject: [PATCH 5/6] access denied exception --- .../src/System/Windows/Forms/NativeMethods.cs | 1 + src/System.Windows.Forms/src/misc/DpiHelper.cs | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/NativeMethods.cs b/src/System.Windows.Forms/src/System/Windows/Forms/NativeMethods.cs index 242e781acae..0e73c7a3b22 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/NativeMethods.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/NativeMethods.cs @@ -459,6 +459,7 @@ public const int public const int E_NOTIMPL = unchecked((int)0x80004001), E_OUTOFMEMORY = unchecked((int)0x8007000E), E_INVALIDARG = unchecked((int)0x80070057), + E_ACCESSDENIED = unchecked((int)0x80070005), E_NOINTERFACE = unchecked((int)0x80004002), E_POINTER = unchecked((int)0x80004003), E_FAIL = unchecked((int)0x80004005), diff --git a/src/System.Windows.Forms/src/misc/DpiHelper.cs b/src/System.Windows.Forms/src/misc/DpiHelper.cs index c6b26fcbd9f..304bb90286e 100644 --- a/src/System.Windows.Forms/src/misc/DpiHelper.cs +++ b/src/System.Windows.Forms/src/misc/DpiHelper.cs @@ -5,6 +5,7 @@ using System.Drawing; using System.Drawing.Drawing2D; using System.Runtime.InteropServices; +using System.Diagnostics; #if WINFORMS_NAMESPACE using CAPS = System.Windows.Forms.NativeMethods; @@ -425,7 +426,7 @@ internal static HighDpiMode GetWinformsApplicationDpiAwareness() return null; } - /// + /// /// Sets the DPI awareness. If not available on the current OS, it falls back to the next possible. /// /// true/false - If the process DPI awareness is successfully set, returns true. Otherwise false. @@ -504,7 +505,9 @@ private static bool SetAppDpiAwarenessWithAwareness(HighDpiMode highDpiMode) dpiFlag.ToString(), nameof(SafeNativeMethods.SetProcessDpiAwareness))); } - + Debug.Assert(hResult != NativeMethods.E_ACCESSDENIED, + "The DPI awareness is already set, either by calling this API previously or through the application (.exe) manifest."); + return hResult == NativeMethods.S_OK; } From c191d6c2d284757a2c8a1830d3a2621728595099 Mon Sep 17 00:00:00 2001 From: Zachary Danz Date: Thu, 14 Feb 2019 15:05:08 -0800 Subject: [PATCH 6/6] respond to pr feedback: redundant elses --- .../src/misc/DpiUnsafeNativeMethods.cs | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs b/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs index ac0784e8396..4527320fcdb 100644 --- a/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs +++ b/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs @@ -71,7 +71,8 @@ public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext? dpiCont { return AreDpiAwarenessContextsEqual((DpiAwarenessContext)dpiContextA, (DpiAwarenessContext)dpiContextB); } - + + // legacy OS that does not have this API available. return false; } @@ -85,11 +86,8 @@ public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext? dpiCont { return GetThreadDpiAwarenessContext(); } - else - { - // legacy OS that does not have this API available. - return null; - } + // legacy OS that does not have this API available. + return null; } /// @@ -106,11 +104,8 @@ public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext? dpiCont } return SetThreadDpiAwarenessContext((DpiAwarenessContext)dpiContext); } - else - { - // legacy OS that does not have this API available. - return null; - } + // legacy OS that does not have this API available. + return null; } /// @@ -123,11 +118,8 @@ public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext? dpiCont { return GetWindowDpiAwarenessContext(hwnd); } - else - { - // legacy OS that does not have this API available. - return null; - } + // legacy OS that does not have this API available. + return null; } } -} \ No newline at end of file +}