Windows Display Setting at 150% still shows 96 DPI

后端 未结 2 1207
南笙
南笙 2020-12-30 09:31

On my laptop running Win7, when I set the display setting to 125%, the DPI shows up as 120 (using both graphics.DpiX and GetDeviceCaps) as expected. However, with the displ

2条回答
  •  佛祖请我去吃肉
    2020-12-30 10:04

    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.

    Custom DPI Settings


    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
    {
        /// 
        /// Logical pixels inch in X
        /// 
        LOGPIXELSX = 88,
    
        /// 
        /// Horizontal width in pixels
        /// 
        HORZRES = 8,
    
        /// 
        /// Horizontal width of entire desktop in pixels
        /// 
        DESKTOPHORZRES = 118
    }
    
    /// 
    /// Retrieves device-specific information for the specified device.
    /// 
    /// A handle to the DC.
    /// The item to be returned.
    [DllImport("gdi32.dll")]
    private static extern int GetDeviceCaps(IntPtr hdc, DeviceCaps nIndex);
    

提交回复
热议问题