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
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
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);
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: https://docs.microsoft.com/en-gb/windows/win32/hidpi
(original link (now redirects to above): 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.