Windows Display Setting at 150% still shows 96 DPI

我们两清 提交于 2019-11-30 13:01:17

On Windows Vista and Windows 7, with DPIs above 120 (I think) and applications that it considers to be non-DPI aware, they switch into DPI Virtualization Mode.

This does exactly what you're seeing - the application thinks it's running in 96dpi while Windows blows it up and renders everything bigger (and blurrier).

For more details: http://msdn.microsoft.com/en-us/library/dd464660(VS.85).aspx#dpi_virtualization

The article explains how to disable it on a per-application basis.

I've just struggled the same problem, and though there are a lot of DPI-related questions on StackOverflow, I did not find all the answers in one place.

The answer to question a) is the easier one: Starting with Windows Vista, Windows supports two kinds of DPI-related resizing. If you click on "Set custom text size (DPI)" on Display settings, you can see that by default, 125% uses the Windows XP-compatible resizing, while 150% doesn't.


Question b) is a trickier one. If you search StackOverflow, usually you can find the following answer:

    using (Graphics screen = Graphics.FromHwnd(IntPtr.Zero))
    {
        IntPtr hdc = screen.GetHdc();
        int dpiX = GetDeviceCaps(hdc, DeviceCaps.LOGPIXELSX);
        screen.ReleaseHdc(hdc);
    }

However, it will return always 96, regardless of actual DPI settings, unless...
- You use Windows XP or the compatibility mode is checked in at DPI settings. Problem: you cannot enforce it at the users.
- DWM is turned off (you use Basic or Classic themes). Problem: same as above.
- You call SetProcessDPIAware function before using GetDeviceCaps. Problem: This function should be called once, before all other rendering. If you have an existing DPI-unaware app, changing the awareness will ruin the whole appearance. It cannot be turned off once you called the function.
- You call SetProcessDpiAwareness before and after using GetDeviceCaps. Problem: This function requires at least Windows 8.1

The real working solution

It seems that the GetDeviceCaps function is not fully documented at MSDN. At least I discovered that pinvoke.net mentions a few further options that can be obtained by the function. At the end I came out with the following solution:

public static int GetSystemDpi()
{
    using (Graphics screen = Graphics.FromHwnd(IntPtr.Zero))
    {
        IntPtr hdc = screen.GetHdc();

        int virtualWidth = GetDeviceCaps(hdc, DeviceCaps.HORZRES);
        int physicalWidth = GetDeviceCaps(hdc, DeviceCaps.DESKTOPHORZRES);
        screen.ReleaseHdc(hdc);

        return (int)(96f * physicalWidth / virtualWidth);
    }
}

And the required additional code:

private enum DeviceCaps
{
    /// <summary>
    /// Logical pixels inch in X
    /// </summary>
    LOGPIXELSX = 88,

    /// <summary>
    /// Horizontal width in pixels
    /// </summary>
    HORZRES = 8,

    /// <summary>
    /// Horizontal width of entire desktop in pixels
    /// </summary>
    DESKTOPHORZRES = 118
}

/// <summary>
/// Retrieves device-specific information for the specified device.
/// </summary>
/// <param name="hdc">A handle to the DC.</param>
/// <param name="nIndex">The item to be returned.</param>
[DllImport("gdi32.dll")]
private static extern int GetDeviceCaps(IntPtr hdc, DeviceCaps nIndex);
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!