C# 在 Windows 中为每个监视器获取 DPI 缩放

C# Getting DPI Scaling for each Monitor in Windows

我正在使用 WPF 应用程序中的代码,该应用程序需要计算 Windows 中每个监视器的 DPI 缩放大小。我能够计算出主屏幕的 DPI,但出于某种原因,我无法计算出如何获得其他显示器的比例 - 其他所有 return 与主显示器的 DPI 相同。

有一些代码可以执行此操作,请耐心等待。第一组代码处理基于 HWND 获取 DPI。该代码获取活动监视器,然后检索 DPI 设置并将数字与 96 DPI(通常为 100%)的比率进行比较。

public static decimal GetDpiRatio(Window window)
{
    var dpi = WindowUtilities.GetDpi(window, DpiType.Effective);
    decimal ratio = 1;
    if (dpi > 96)
        ratio = (decimal)dpi / 96M;

    return ratio;
}
public static decimal GetDpiRatio(IntPtr hwnd)
{            
    var dpi = GetDpi(hwnd, DpiType.Effective);            
    decimal ratio = 1;
    if (dpi > 96)
        ratio = (decimal)dpi / 96M;

    //Debug.WriteLine($"Scale: {factor} {ratio}");
    return ratio;
}

public static uint GetDpi(IntPtr hwnd, DpiType dpiType)
{            
    var screen = Screen.FromHandle(hwnd);            
    var pnt = new Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
    var mon = MonitorFromPoint(pnt, 2 /*MONITOR_DEFAULTTONEAREST*/);

    Debug.WriteLine("monitor handle: " + mon);
    try
    {
        uint dpiX, dpiY;
        GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
        return dpiX;
    }
    catch
    {
        // fallback for Windows 7 and older - not 100% reliable
        Graphics graphics = Graphics.FromHwnd(hwnd);
        float dpiXX = graphics.DpiX;                
        return Convert.ToUInt32(dpiXX);
    }
}


public static uint GetDpi(Window window, DpiType dpiType)
{
    var hwnd = new WindowInteropHelper(window).Handle;
    return GetDpi(hwnd, dpiType);
}     

[DllImport("User32.dll")]
private static extern IntPtr MonitorFromPoint([In]System.Drawing.Point pt, [In]uint dwFlags);

[DllImport("Shcore.dll")]
private static extern IntPtr GetDpiForMonitor([In]IntPtr hmonitor, [In]DpiType dpiType, [Out]out uint dpiX, [Out]out uint dpiY);        


public enum DpiType
{
    Effective = 0,
    Angular = 1,
    Raw = 2,
}

此代码用作屏幕捕获解决方案的一部分,其中应该覆盖 window 用户鼠标悬停的位置。我捕获鼠标位置并基于它获得像素位置,然后在那里创建 WPF window。在这里,我必须应用 DPI 比率以使 Window 呈现在正确的位置和大小。

只要 DPI 相同,这在主显示器或多显示器上都可以正常工作。

问题是对 GetDpiForMonitor() 的调用总是 return 主监视器 DPI,即使传递给它的 HMONITOR 值不同。

DPI 意识

这是一个 WPF 应用程序,因此该应用程序是 DPI 感知的,但 WPF 在系统 DPI 感知中运行,而不是在每个监视器 DPI 感知中运行。为此,我在启动时连接了 static App() 代码以明确设置为每个显示器 DPI:

    try
    {
        // for this to work make sure [assembly:dpiawareness
        PROCESS_DPI_AWARENESS awareness;
        GetProcessDpiAwareness(Process.GetCurrentProcess().Handle, out awareness);
        var result = SetProcessDpiAwareness(PROCESS_DPI_AWARENESS.Process_Per_Monitor_DPI_Aware);
        GetProcessDpiAwareness(Process.GetCurrentProcess().Handle, out awareness);
}

[DllImport("SHCore.dll", SetLastError = true)]
public static extern bool SetProcessDpiAwareness(PROCESS_DPI_AWARENESS awareness);

[DllImport("SHCore.dll", SetLastError = true)]
public static extern void GetProcessDpiAwareness(IntPtr hprocess, out PROCESS_DPI_AWARENESS awareness);

public enum PROCESS_DPI_AWARENESS
{
    Process_DPI_Unaware = 0,
    Process_System_DPI_Aware = 1,
    Process_Per_Monitor_DPI_Aware = 2
}

// and in assemblyinfo
[assembly: DisableDpiAwareness]

我看到 DPI 设置更改为 Process_Per_Monitor_DPI_Aware 但这似乎对行为也没有影响。我仍然看到 DPI 结果 returned 与主显示器相同。

有一个大型解决方案中的测试,允许在这里玩这个: https://github.com/RickStrahl/MarkdownMonster/blob/master/Tests/ScreenCaptureAddin.Test/DpiDetectionTests.cs 如果有人有兴趣检查一下。

知道如何可靠地获得系统上所有显示器的 DPI 缩放级别(以及为什么没有系统 API 甚至没有 WMI 设置)?

自 .NET Framework 4.6.2 起,WPF 支持每个监视器的 DPI。 GitHub:http://github.com/Microsoft/WPF-Samples/tree/master/PerMonitorDPI 提供了更多信息和示例。

您可能还想查看 VisualTreeHelper.GetDpi 方法。

我一直在努力解决类似的问题(辅助显示器屏幕边界似乎按与主显示器上设置的相同比例因子缩放),我发现一些文档似乎至少解释了这是预期的行为:

DPI Awareness Mode - System
Windows Version Introduced - Vista
Application's view of DPI - All displays have the same DPI (the DPI of the primary display at the time the Windows session was started)
Behavior on DPI change - Bitmap-stretching (blurry)

这是从High DPI desktop application development on Windows

中的第一个table中提取的

这是我发现的第一份文档,它至少明确说明当应用程序处于系统 DPI 感知下时,代码将报告所有 windows 共享相同的缩放比例。