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 4a81b30e231..842d2092e14 100644 --- a/src/System.Windows.Forms.Design.Editors/src/Misc/CommonUnsafeNativeMethods.cs +++ b/src/System.Windows.Forms.Design.Editors/src/Misc/CommonUnsafeNativeMethods.cs @@ -63,76 +63,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)] - internal static extern DpiAwarenessContext GetThreadDpiAwarenessContext(); - - [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] - 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 54d3da5e1dc..acc22ab217f 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Application.cs @@ -1242,7 +1242,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."); @@ -1257,7 +1257,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) { @@ -1289,7 +1289,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); @@ -2493,7 +2493,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) { @@ -2507,7 +2507,6 @@ internal ParkingWindow GetParkingWindow(DpiAwarenessContext context) { Debug.WriteLine(CoreSwitches.PerfTrack.Enabled, Environment.StackTrace); } #endif - using (DpiHelper.EnterDpiAwarenessScope(context)) { parkingWindow = new ParkingWindow(); } @@ -2522,7 +2521,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; @@ -2531,7 +2530,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, null)) { Debug.Assert(parkingWindows.Count == 1, "parkingWindows count can not be > 1 for legacy OS/target framework versions"); return parkingWindows[0]; @@ -2539,7 +2538,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 52379575fb8..bf81bcdf249 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs @@ -577,7 +577,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; } @@ -5617,7 +5617,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); @@ -5638,6 +5638,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, @@ -8096,7 +8101,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); } } @@ -11420,7 +11425,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/NativeMethods.cs b/src/System.Windows.Forms/src/System/Windows/Forms/NativeMethods.cs index e9f9e1eda78..c80b2b4fbe3 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/System/Windows/Forms/NativeWindow.cs b/src/System.Windows.Forms/src/System/Windows/Forms/NativeWindow.cs index 97708833de2..f71c424a1be 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/NativeWindow.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/NativeWindow.cs @@ -95,9 +95,9 @@ public class NativeWindow : MarshalByRefObject, IWin32Window { NativeWindow previousWindow; // doubly linked list of subclasses. NativeWindow nextWindow; WeakReference weakThisPtr; - private DpiAwarenessContext windowDpiAwarenessContext = DpiHelper.IsScalingRequirementMet ? - CommonUnsafeNativeMethods.TryGetThreadDpiAwarenessContext() : - DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED; + private DpiAwarenessContext? windowDpiAwarenessContext = DpiHelper.IsScalingRequirementMet ? + (DpiAwarenessContext?)DpiUnsafeNativeMethods.TryGetThreadDpiAwarenessContext() : + null; static NativeWindow() { EventHandler shutdownHandler = new EventHandler(OnShutdown); @@ -127,7 +127,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/System/Windows/Forms/WebBrowserBase.cs b/src/System.Windows.Forms/src/System/Windows/Forms/WebBrowserBase.cs index 62056016b80..f5b51801406 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/WebBrowserBase.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/WebBrowserBase.cs @@ -479,7 +479,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 8529c0e69aa..c9905bbe699 100644 --- a/src/System.Windows.Forms/src/misc/CommonUnsafeNativeMethods.cs +++ b/src/System.Windows.Forms/src/misc/CommonUnsafeNativeMethods.cs @@ -22,6 +22,7 @@ internal class CommonUnsafeNativeMethods [DllImport(ExternDll.Kernel32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Ansi)] public static extern IntPtr GetProcAddress(HandleRef hModule, string lpProcName); + [DllImport(ExternDll.Kernel32, SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr GetModuleHandle(string modName); @@ -69,85 +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)] - - internal static extern DpiAwarenessContext GetThreadDpiAwarenessContext(); - - [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] - - 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..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,25 +45,28 @@ 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 (!CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(awareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)) + if (!DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(awareness, null)) { - 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,47 @@ 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(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) || + 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 +132,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 660c7c880de..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; @@ -70,7 +71,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... @@ -105,10 +106,10 @@ 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 = CommonUnsafeNativeMethods.GetThreadDpiAwarenessContext(); - return CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + // 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 { @@ -341,47 +342,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,16 +410,20 @@ 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; } /// @@ -412,10 +431,15 @@ internal static HighDpiMode GetWinformsApplicationDpiAwareness() /// /// 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 +470,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 +498,26 @@ 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))); + } + 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; } - // 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,22 +528,16 @@ 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; } } 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 new file mode 100644 index 00000000000..4527320fcdb --- /dev/null +++ b/src/System.Windows.Forms/src/misc/DpiUnsafeNativeMethods.cs @@ -0,0 +1,125 @@ +// 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); + + 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. + /// + /// true/false + public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext? dpiContextA, DpiAwarenessContext? dpiContextB) + { + if(dpiContextA == null) + { + return (dpiContextB == null); // true if both null but not either + } + else if (dpiContextB == null) + { + return false; // because we know A is not null + } + + if (AreDpiAwarenessContextsEqualIsAvailable()) + { + return AreDpiAwarenessContextsEqual((DpiAwarenessContext)dpiContextA, (DpiAwarenessContext)dpiContextB); + } + + // legacy OS that does not have this API available. + 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 (GetThreadDpiAwarenessContextIsAvailable()) + { + return GetThreadDpiAwarenessContext(); + } + // legacy OS that does not have this API available. + return null; + } + + /// + /// 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 (SetThreadDpiAwarenessContextIsAvailable()) + { + if (dpiContext == null) + { + throw new ArgumentNullException(); + } + return SetThreadDpiAwarenessContext((DpiAwarenessContext)dpiContext); + } + // legacy OS that does not have this API available. + return null; + } + + /// + /// 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 (GetWindowDpiAwarenessContextIsAvailable()) + { + return GetWindowDpiAwarenessContext(hwnd); + } + // legacy OS that does not have this API available. + return null; + } + } +} diff --git a/src/System.Windows.Forms/tests/UnitTests/CommonUnsafenativeMethods.cs b/src/System.Windows.Forms/tests/UnitTests/CommonUnsafenativeMethods.cs deleted file mode 100644 index f3c33a15cd7..00000000000 --- a/src/System.Windows.Forms/tests/UnitTests/CommonUnsafenativeMethods.cs +++ /dev/null @@ -1,59 +0,0 @@ -// 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. - -using Xunit; -using WinForms.Common.Tests; - -namespace System.Windows.Forms.Tests -{ - public class CommonUnsafeNativeMethodsTests - { - - - /*/// - /// Data for the TryFindDpiAwarenessContextsEqual test - /// - public static TheoryData TryFindDpiAwarenessContextsEqualData => - CommonTestHelper.GetEnumTheoryData(); - - - Theory] - [MemberData(nameof(TryFindDpiAwarenessContextsEqualData))] - internal void CommonUnsafeNativeMathods_TryFindDpiAwarenessContextsEqual(DpiAwarenessContext item) - { - Assert.True(CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(item, item)); - } - - - [Fact] - public void CommonUnsafeNativeMethods_AreDpiAwarenessContextsEqualNotForUnspecified() - { - Assert.False(CommonUnsafeNativeMethods.AreDpiAwarenessContextsEqual(DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)); - } - - /// - /// Data for the TrySetThreadDpiAwarenessContextGetSet test - /// - public static TheoryData TrySetThreadDpiAwarenessContextGetSetData => - CommonTestHelper.GetEnumTheoryData(); - - [Theory] - [MemberData(nameof(TrySetThreadDpiAwarenessContextGetSetData))] - internal void CommonUnsafeNativeMethods_TrySetThreadDpiAwarenessContextGetSet(DpiAwarenessContext item) - { - if (item != DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) - { - CommonUnsafeNativeMethods.TrySetThreadDpiAwarenessContext(item); - - Assert.True(CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(item, CommonUnsafeNativeMethods.TryGetThreadDpiAwarenessContext())); - } - else - { - var ex = Assert.Throws(() => CommonUnsafeNativeMethods.TrySetThreadDpiAwarenessContext(DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)); - Assert.Equal(DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED.ToString(), ex.ParamName); - } - }*/ - - } -} diff --git a/src/System.Windows.Forms/tests/UnitTests/DpiUnsafeNativeMethodsTests.cs b/src/System.Windows.Forms/tests/UnitTests/DpiUnsafeNativeMethodsTests.cs new file mode 100644 index 00000000000..c2c83561314 --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/DpiUnsafeNativeMethodsTests.cs @@ -0,0 +1,63 @@ +// 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. + +using Xunit; +using WinForms.Common.Tests; + +namespace System.Windows.Forms.Tests +{ + public class DpiUnsafeNativeMethodsTests + { + /// + /// Data for the TryFindDpiAwarenessContextsEqual test + /// + public static TheoryData TryFindDpiAwarenessContextsEqualData => + CommonTestHelper.GetEnumTheoryData(); + + + [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] + internal void DpiUnsafeNativeMethods_TryFindDpiAwarenessContextsEqual_BothNull() + { + Assert.True(DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(null, null)); + } + + + /// + /// Data for the TrySetThreadDpiAwarenessContextGetSet test + /// + public static TheoryData TrySetThreadDpiAwarenessContextGetSetData => + CommonTestHelper.GetEnumTheoryData(); + + [Theory] + [MemberData(nameof(TrySetThreadDpiAwarenessContextGetSetData))] + internal void DpiUnsafeNativeMethods_TrySetThreadDpiAwarenessContextGetSet(DpiAwarenessContext item) + { + DpiUnsafeNativeMethods.TrySetThreadDpiAwarenessContext(item); + + Assert.True(DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(item, DpiUnsafeNativeMethods.TryGetThreadDpiAwarenessContext())); + } + + [Fact] + internal void DPiUnsafeNativeMethods_TrySetThreadDpiAwarenessContext_Null() + { + Assert.Throws(() => DpiUnsafeNativeMethods.TrySetThreadDpiAwarenessContext(null)); + } + + } +}