C# 获取 Windows 中每个显示器的 DPI 缩放比例

2024-01-29

我正在使用 WPF 应用程序中的代码,该应用程序需要计算 Windows 中每个显示器的 DPI 缩放大小。我能够计算出主屏幕的 DPI,但由于某种原因,我无法计算出如何获取其他显示器的比例 - 其他显示器都返回与主显示器相同的 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,
}

此代码用作屏幕捕获解决方案的一部分,其中应该在用户鼠标所在的窗口上覆盖。我捕获鼠标位置,并根据该位置获取像素位置,然后在那里创建 WPF 窗口。在这里,我必须应用 DPI 比率,以使窗口在正确的位置和大小上渲染。

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

问题是调用GetDpiForMonitor()始终返回主显示器 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 结果与主显示器相同。

在一个较大的解决方案中有一个测试,允许在这里使用它:https://github.com/RickStrahl/MarkdownMonster/blob/master/Tests/ScreenCaptureAddin.Test/DpiDetectionTests.cs 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 http://github.com/Microsoft/WPF-Samples/tree/master/PerMonitorDPI.

您可能还想查看VisualTreeHelper.GetDpi https://msdn.microsoft.com/en-us/library/system.windows.media.visualtreehelper.getdpi%28v=vs.110%29.aspx method.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C# 获取 Windows 中每个显示器的 DPI 缩放比例 的相关文章

随机推荐