File: MS\Internal\DpiUtil\DpiUtil.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj (PresentationCore)
// 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 MS.Internal
{
    using MS.Utility;
    using System;
    using System.Security;
    using System.Windows;
    using System.Windows.Interop;
 
    using PROCESS_DPI_AWARENESS = MS.Win32.NativeMethods.PROCESS_DPI_AWARENESS;
 
    /// <summary>
    /// DPI related utilities
    /// </summary>
    internal static partial class DpiUtil
    {
        /// <summary>
        /// This is the default PPI value used in WPF and Windows
        /// </summary>
        internal const double DefaultPixelsPerInch = 96.0d;
 
        /// <summary>
        /// Gets the DPI awareness context handle from and HWND
        /// </summary>
        /// <param name="hWnd">Handle to the window</param>
        /// <returns>The awareness context</returns>
        /// <remarks>
        ///     .. Attempts to get the value directly from the HWND, failing which
        ///     .. attempts to get it from the PROCESS_DPI_AWARENESS
        ///             value associated with the process of the HWND, failing which
        ///     .. gets the value from the PROCESS_DPI_AWARENESS of
        ///             the currently executing process
        /// </remarks>
        internal static DpiAwarenessContextHandle GetDpiAwarenessContext(IntPtr hWnd)
        {
            return DpiAwarenessContextHelper.GetDpiAwarenessContext(hWnd);
        }
 
        /// <summary>
        /// Gets the PROCESS_DPI_AWARENESS enum value of the process associated
        /// with a window
        /// </summary>
        /// <param name="hWnd">Handle to the window being queried</param>
        /// <returns>The PROCESS_DPI_AWARNESS value</returns>
        /// <remarks>
        /// If the process associated with the HWND cannot be queried for its
        /// PROCESS_DPI_AWARENESS value, then the value is obtained from the
        /// current process' DPI awareness information.
        /// </remarks>
        internal static PROCESS_DPI_AWARENESS GetProcessDpiAwareness(IntPtr hWnd)
        {
            return ProcessDpiAwarenessHelper.GetProcessDpiAwareness(hWnd);
        }
 
        /// <summary>
        /// Equivalent to <see cref="GetProcessDpiAwareness(IntPtr)"/>
        /// </summary>
        /// <param name="hWnd">Handle to the window being queried</param>
        /// <returns>The <see cref="DpiAwarenessContextValue"/> enum corresponding to the PROCESS_DPI_AWARENESS value of the <paramref name="hWnd"/></returns>
        /// <remarks>See remarks for <see cref="GetProcessDpiAwareness(IntPtr)"/></remarks>
        internal static DpiAwarenessContextValue GetProcessDpiAwarenessContextValue(IntPtr hWnd)
        {
            var dpiAwarenessContext = ProcessDpiAwarenessHelper.GetProcessDpiAwareness(hWnd);
            return (DpiAwarenessContextValue)DpiAwarenessContextHelper.GetProcessDpiAwarenessContext(dpiAwarenessContext);
        }
 
 
        /// <summary>
        /// Gets the PROCESS_DPI_AWARENESS enum value of the current process
        /// </summary>
        /// <returns>PROCESS_DPI_AWARENESS value of the current process</returns>
        /// <remarks>
        /// The only values returned by this method are PROCESS_SYSTEM_DPI_AWARE or
        /// PROCESS_DPI_UNAWARE
        /// </remarks>
        internal static PROCESS_DPI_AWARENESS GetLegacyProcessDpiAwareness()
        {
            return ProcessDpiAwarenessHelper.GetLegacyProcessDpiAwareness();
        }
 
        /// <summary>
        /// Gets the System DPI
        /// </summary>
        /// <returns>The system DPI</returns>
        /// <remarks>
        /// If the system DPI cannot be obtained directly, then
        /// it falls back to obtaining the system DPI indirectly
        /// by querying through the desktop.
        /// 
        /// If querying via the desktop fails for some reason, then
        /// it returns null.
        /// </remarks>
        internal static DpiScale2 GetSystemDpi()
        {
            return SystemDpiHelper.GetSystemDpi();
        }
 
        /// <summary>
        /// Gets the System DPI from the values stored in the UIElement static cache
        /// </summary>
        /// <returns>The system DPI value</returns>
        internal static DpiScale2 GetSystemDpiFromUIElementCache()
        {
            return SystemDpiHelper.GetSystemDpiFromUIElementCache();
        }
 
        /// <summary>
        /// Updates the UIElement static cache containing System DPI value
        /// </summary>
        /// <param name="systemDpiScale">Updated system DPI scale value</param>
        internal static void UpdateUIElementCacheForSystemDpi(DpiScale2 systemDpiScale)
        {
            SystemDpiHelper.UpdateUIElementCacheForSystemDpi(systemDpiScale);
        }
 
        /// <summary>
        /// Gets a window's DPI scale factor
        /// </summary>
        /// <param name="hWnd">Handle to the window</param>
        /// <param name="fallbackToNearestMonitorHeuristic">
        /// When true, falls back to obtaining the DPI of the monitor nearest to the window if unable to obtain
        /// the DPI of the window directly.
        /// When false, there is no fallback attempted and returns null on failure to obtain the window's DPI directly.
        /// </param>
        /// <returns>The window's DPI</returns>
        internal static DpiScale2 GetWindowDpi(IntPtr hWnd, bool fallbackToNearestMonitorHeuristic)
        {
            return WindowDpiScaleHelper.GetWindowDpi(hWnd, fallbackToNearestMonitorHeuristic);
        }
 
        /// <summary>
        /// Obtains extended DPI related information about an HWND - specifically, the <see cref="DpiAwarenessContextValue"/>,
        /// <see cref="DpiScale2"/>, and the screen-rectangle of the monitor where the <paramref name="hWnd"/> resides.
        /// </summary>
        /// <param name="hWnd">The handle to the HWND</param>
        /// <param name="fallbackToNearestMonitorHeuristic">The semantics of this parameter are identical to that in <see cref="GetWindowDpi(IntPtr, bool)"/></param>
        /// <returns>Extended DPI information</returns>
        internal static HwndDpiInfo GetExtendedDpiInfoForWindow(IntPtr hWnd, bool fallbackToNearestMonitorHeuristic)
        {
            return new HwndDpiInfo(hWnd, fallbackToNearestMonitorHeuristic);
        }
 
        /// <summary>
        /// Obtains extended DPI related information about an HWND - specifically, the <see cref="DpiAwarenessContextValue"/>,
        /// <see cref="DpiScale2"/>, and the screen-rectangle of the monitor where the <paramref name="hWnd"/> resides.
        /// </summary>
        /// <param name="hWnd">The handle to the HWND</param>
        /// <returns>Extended DPI information</returns>
        /// <remarks>If the window DPI could not be obtained directly, then it uses the DPI of the nearest
        /// monitor as a fall-back value</remarks>
        internal static HwndDpiInfo GetExtendedDpiInfoForWindow(IntPtr hWnd)
        {
            return GetExtendedDpiInfoForWindow(hWnd, true);
        }
 
        /// <summary>
        /// Helper to modify the DPI_AWARENESS_CONTEXT of the current thread.
        /// </summary>
        /// <param name="dpiAwarenessContext">DPI_AWARENESS_CONTEXT to set the thread to</param>
        /// <returns>
        /// An <see cref="IDisposable"/> that defines a scope during which the DPI_AWARENESS_CONTEXT of the current thread is
        /// modified to the requested value.
        /// </returns>
        internal static IDisposable WithDpiAwarenessContext(DpiAwarenessContextValue dpiAwarenessContext)
        {
            return new DpiAwarenessScope(dpiAwarenessContext);
        }
 
        /// <summary>
        /// flag1 and flag2 correspond to MonitorDpiScaleIndex1 and MonitorDpiScaleIndex2 of VisualFlags respectively
        /// UpdateDpiScales sets flag1 and flag2 based on the correct index of the DPI in the static DPI array stored in
        /// UIElement class and inserts an entry into the array if necessary.
        /// </summary>
        internal static DpiFlags UpdateDpiScalesAndGetIndex(double pixelsPerInchX, double pixelsPerInchY)
        {
            lock (UIElement.DpiLock)
            {
                bool dpiScaleFlag1, dpiScaleFlag2;
                int index = 0;
                int sizeOfList = UIElement.DpiScaleXValues.Count;
                for (index = 0; index < sizeOfList; index++)
                {
                    // We have found a match.
                    if (UIElement.DpiScaleXValues[index] == pixelsPerInchX / DpiUtil.DefaultPixelsPerInch &&
                        UIElement.DpiScaleYValues[index] == pixelsPerInchY / DpiUtil.DefaultPixelsPerInch)
                    {
                        break;
                    }
                }
 
                // Didn't find a match, add to the end of the list
                if (index == sizeOfList)
                {
                    UIElement.DpiScaleXValues.Add(pixelsPerInchX / DpiUtil.DefaultPixelsPerInch);
                    UIElement.DpiScaleYValues.Add(pixelsPerInchY / DpiUtil.DefaultPixelsPerInch);
                }
 
                // Since we only have 2 bits to spare in VisualFlags, we use the first 3 possible
                // values to directly map the index to the array. If the index is >= 3, we set both flags
                // to true, and store the actual index in an UncommonField on the Visual
 
                if (index < 3)
                {
                    dpiScaleFlag1 = (index & 0x1) != 0;
                    dpiScaleFlag2 = (index & 0x2) != 0;
                }
                else
                {
                    dpiScaleFlag1 = dpiScaleFlag2 = true;
                }
 
                return new DpiFlags(dpiScaleFlag1, dpiScaleFlag2, index);
            }
        }
    }
}