如何获取 Windows 显示设置?

Posted

技术标签:

【中文标题】如何获取 Windows 显示设置?【英文标题】:How to get Windows Display settings? 【发布时间】:2011-08-24 01:29:37 【问题描述】:

Windows 7 中有显示设置(控制面板 -> 显示)。它允许更改屏幕上文本和其他项目的大小。 我需要获取此设置才能根据设置值打开/关闭我的 C# 应用程序中的某些功能。 这可能吗?

【问题讨论】:

您对哪个设置感兴趣?有很多。 如果您使用设置 - 控制面板 -> 显示打开此页面。只有一个设置可以更改屏幕上的文本和其他项目的大小。选项有“小”、“中”和“大” 【参考方案1】:

graphics.DpiX 和 DeviceCap.LOGPIXELSX 在 Surface Pro 上的所有缩放级别都返回 96。

相反,我设法以这种方式计算比例因子:

[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
public enum DeviceCap

    VERTRES = 10,
    DESKTOPVERTRES = 117,

    // http://pinvoke.net/default.aspx/gdi32/GetDeviceCaps.html
  


private float getScalingFactor()

    Graphics g = Graphics.FromHwnd(IntPtr.Zero);
    IntPtr desktop = g.GetHdc();
    int LogicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.VERTRES);
    int PhysicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.DESKTOPVERTRES); 

    float ScreenScalingFactor = (float)PhysicalScreenHeight / (float)LogicalScreenHeight;

    return ScreenScalingFactor; // 1.25 = 125%

【讨论】:

哇,我找了很久如何得到真正的比例因子,终于解决了我的问题! 当你快要哭泣放弃的那一刻,你却遇到了这样的事情。 请注意,您可能希望在我的系统上使用 both 此解决方案和标记为已接受的解决方案...,读取 g.DpiX 返回 120 但比例因子仍然是 1.0。 仅供参考,在戴尔 Precision 5510 笔记本电脑上,运行 Win 10 64 位,分辨率 3840x2160,显示比例设置为 200%(从右键单击桌面>>显示设置),LogicalScreenHeight 和 PhysicalScreenHeight 都在上面的代码返回相同的内容,2160。因此,在这种情况下,此代码无法获得所需的 200% 显示比例。 我使用类似的硬件观察到与@Nerdtron 相同的问题。不幸的是,这段代码不能在显示器上可靠地工作。【参考方案2】:

此设置是屏幕 DPI,即每英寸点数。

这样读:

float dpiX, dpiY;
Graphics graphics = this.CreateGraphics();
dpiX = graphics.DpiX;
dpiY = graphics.DpiY;

我认为目前 X 和 Y 值不可能不同。值 96 对应于 100% 字体缩放(更小),120 对应于 125% 缩放(中等),144 对应于 150% 缩放(更大)。但是,用户可以设置这些标准值以外的值。

请注意,除非您的应用程序被声明为支持 DPI,否则您观察到的值可能会受到 DPI 虚拟化的影响。

【讨论】:

我尝试使用不同的设置和图形的值运行此代码。DpiX 始终为 96。 DpiX 是 90?!我注销并重新登录,它从 96 变为 120。我使用的是新的 WinForms 应用程序。我想知道你是否在你的应用程序中做一些特别的事情。您是否尝试过一个全新的 WinForms 项目? 我尝试设置中等设置 - 它有效! DpiX 是 120,但对于更大的设置,它是 96... 好吧,我想我现在明白了。问题是您的应用程序未标记为 DPI 感知,因此它可能看起来很模糊。您应该在清单中将您的应用标记为 DPI 感知。 @ColonelPanic this 只是任何控件。 msdn.microsoft.com/en-us/library/…【参考方案3】:

我认为最简单的方法是使用GetDeviceCaps 函数。来自pinvoke.net:

[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int GetDeviceCaps(IntPtr hDC, int nIndex);

public enum DeviceCap

  /// <summary>
  /// Logical pixels inch in X
  /// </summary>
  LOGPIXELSX = 88,
  /// <summary>
  /// Logical pixels inch in Y
  /// </summary>
  LOGPIXELSY = 90

  // Other constants may be founded on pinvoke.net
      

及用法:

Graphics g = Graphics.FromHwnd(IntPtr.Zero);            
IntPtr desktop = g.GetHdc();

int Xdpi = GetDeviceCaps(desktop, (int)DeviceCap.LOGPIXELSX);
int Ydpi = GetDeviceCaps(desktop, (int)DeviceCap.LOGPIXELSY);    

在这种方法中,您无需将应用标记为支持 dpi。

【讨论】:

别忘了调用 ReleaseHdc @BitsEvolved 绝对。这解决了“我的”内存泄漏(在完全不同的上下文中)!非常感谢。 在 GetDeviceCaps 函数的 cmets 中,在您提供的链接中,它对 LOGPIXELSX 和 LOGPIXELSY 表示以下内容:“在具有多个显示监视器的系统中,此值对于所有监视器都是相同的。”需要注意的事情...【参考方案4】:

这就是您在 WPF 中可以做到的方式。返回值采用 WPF 的逻辑单位,等于 1/96 英寸。因此,如果您的屏幕 DPI 设置为 96,您将获得 1 的值。

Matrix m =
PresentationSource.FromVisual(Application.Current.MainWindow).CompositionTarget.TransformToDevice;
double dx = m.M11; // notice it's divided by 96 already
double dy = m.M22; // notice it's divided by 96 already

(source)

【讨论】:

谢谢,我认为这是wpf最简单的方法。【参考方案5】:

这是一个在 Windows 10 中运行良好的解决方案。不需要 DPI 意识或任何东西。

public static int GetWindowsScaling()

    return (int)(100 * Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth);

【讨论】:

有趣的是,有时 Screen.PrimaryScreen.Bounds.Width 不返回实际屏幕宽度并返回与 SystemParameters.PrimaryScreenWidth 相同的内容,我在 UI 自动化中遇到了这个问题。我不得不改用 Screen.PrimaryScreen.WorkingArea.Width。 我认为这是可行的,因为Screen.PrimaryScreen.Bounds 具有物理像素并且 SystemParameters.PrimaryScreenWidth 是合乎逻辑的吗?太糟糕了,我们无法确定其他屏幕的情况【参考方案6】:

使用 Farshid T 的答案作为基础适用于每个比例因子,但 125% 除外。我测试了大约 20 个不同的缩放因子,DPI 总是返回 96,除了设置为 125% 时,它返回的 DPI 为 120。120 / 96 = 1.25。不知道为什么会这样,但这段代码似乎适用于任何比例设置。

    [DllImport("gdi32.dll")]
    static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
    public enum DeviceCap
    
        VERTRES = 10,
        DESKTOPVERTRES = 117,
        LOGPIXELSY = 90,

        // http://pinvoke.net/default.aspx/gdi32/GetDeviceCaps.html

及用法:

        Graphics g = Graphics.FromHwnd(IntPtr.Zero);
        IntPtr desktop = g.GetHdc();
        int LogicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.VERTRES);
        int PhysicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.DESKTOPVERTRES);
        int logpixelsy = GetDeviceCaps(desktop, (int)DeviceCap.LOGPIXELSY);
        float screenScalingFactor = (float)PhysicalScreenHeight / (float)LogicalScreenHeight;
        float dpiScalingFactor = (float)logpixelsy / (float)96;

        if (screenScalingFactor > 1 ||
            dpiScalingFactor > 1)
        
            // do something nice for people who can't see very well...
        

【讨论】:

为了安全,你是不是也应该加:g.ReleaseHdc(desktop);【参考方案7】:

这是一个非常古老的问题,但是从 Windows 8.1 开始,人们可以使用各种其他功能,例如 GetDpiForWindow

在 C# 中:

[DllImport("user32.dll")]
static extern int GetDpiForWindow(IntPtr hWnd);

public float GetDisplayScaleFactor(IntPtr windowHandle)

    try
    
        return GetDpiForWindow(windowHandle) / 96f;
    
    catch
    
        // Or fallback to GDI solutions above
        return 1;
    

为了在 Windows 10 周年纪念日上正常工作,您需要将 app.manifest 添加到您的 C# 项目:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
          manifestVersion="1.0">
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <!-- The combination of below two tags have the following effect : 
      1) Per-Monitor for >= Windows 10 Anniversary Update
      2) System < Windows 10 Anniversary Update -->
      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
    </windowsSettings>
  </application>

</assembly>

【讨论】:

您说这应该适用于 Windows 8.1 及更高版本,但在您引用的 Microsoft 文档中它说“要求:Windows 10,版本 1607”。 哦,是的,不错。我想我将此与 GetDpiForMonitor api 调用混淆了。 docs.microsoft.com/en-us/windows/win32/api/shellscalingapi/…【参考方案8】:

我在控制台应用程序中使用这种方式:

float dpiX, dpiY;
using (Graphics graphics = Graphics.FromHwnd(IntPtr.Zero))

    dpiX = graphics.DpiX;
    dpiY = graphics.DpiY;

【讨论】:

【参考方案9】:

如果是 WPF,请使用以下 sn-p,

PresentationSource source = PresentationSource.FromVisual(this);

        double dpiX, dpiY;
        if (source != null)
        
            dpiX = 96.0 * source.CompositionTarget.TransformToDevice.M11;
            dpiY = 96.0 * source.CompositionTarget.TransformToDevice.M22;
        

【讨论】:

【参考方案10】:

Ric Gaudet 答案的更完整版本:

[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int GetDeviceCaps(IntPtr hDC, int nIndex);

public enum DeviceCap

    VERTRES = 10,
    DESKTOPVERTRES = 117


static double GetWindowsScreenScalingFactor(bool percentage = true)

    //Create Graphics object from the current windows handle
    Graphics GraphicsObject = Graphics.FromHwnd(IntPtr.Zero);
    //Get Handle to the device context associated with this Graphics object
    IntPtr DeviceContextHandle = GraphicsObject.GetHdc();
    //Call GetDeviceCaps with the Handle to retrieve the Screen Height
    int LogicalScreenHeight = GetDeviceCaps(DeviceContextHandle, (int)DeviceCap.VERTRES);
    int PhysicalScreenHeight = GetDeviceCaps(DeviceContextHandle, (int)DeviceCap.DESKTOPVERTRES);
    //Divide the Screen Heights to get the scaling factor and round it to two decimals
    double ScreenScalingFactor = Math.Round((double)PhysicalScreenHeight / (double)LogicalScreenHeight, 2);
    //If requested as percentage - convert it
    if (percentage)
    
        ScreenScalingFactor *= 100.0;
    
    //Release the Handle and Dispose of the GraphicsObject object
    GraphicsObject.ReleaseHdc(DeviceContextHandle);
    GraphicsObject.Dispose();
    //Return the Scaling Factor
    return ScreenScalingFactor;

【讨论】:

【参考方案11】:

我认为这应该为您提供您正在寻找的信息:

http://www.pinvoke.net/default.aspx/user32.getsystemmetrics

http://pinvoke.net/default.aspx/Enums.SystemMetric

编辑 - 哦,抱歉,现在似乎有一种更简单的方法可以在没有 pinvoke 的情况下获取此信息,

http://msdn.microsoft.com/en-us/library/system.windows.forms.systeminformation.aspx

【讨论】:

很遗憾,系统信息不反映 dpi 比例。此外,图形上下文的 dpiX/dpiY 属性似乎为 150% 返回 96(至少在表面 pro 2 上)。所以你无法区分 100% 和 150% 缩放。有人知道正确的方法吗?

以上是关于如何获取 Windows 显示设置?的主要内容,如果未能解决你的问题,请参考以下文章

c#如何创建/设置windows窗体用户控件属性获取简单消息弹窗

如何以编程方式获取 Windows 10 显示语言?

QSslCertificate 如何获取 Windows 中显示的公钥

如何获取和设置通用 Windows 应用程序的音量级别

Windows窗体 - 如何获取列表框的垂直滚动条?

如何在 Windows XP 上获取已连接显示器的类型?