How to get Windows Display settings?

C#WindowsDpi

C# Problem Overview


There is setting for Display in Windows 7 (Control Panel -> Display). It allows to change the size of the text and other items on the screen. I need to get this setting to be able to switch on/switch off some functionality in my C# application based on the setting value. Is that possible?

C# Solutions


Solution 1 - C#

Both graphics.DpiX and DeviceCap.LOGPIXELSX return 96 on Surface Pro in all scaling levels.

Instead, I managed to calculate the scaling factor this way:

[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%
}

Solution 2 - C#

This setting is the screen DPI, or dots per inch.

Read it like so:

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

I don't think it's possible at the moment for the X and Y values to be different. A value of 96 corresponds to 100% font scaling (smaller), 120 corresponds to 125% scaling (medium) and 144 corresponds to 150% scaling (larger). However, users are able to set values other than these standard ones.

Do be aware that unless your application is declared to be DPI aware, then the values you observe may be subject to DPI virtualization.

Solution 3 - C#

The most easier way in my opinion is to use GetDeviceCaps function. From 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
}      

And usage:

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

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

In this approach you have no need to mark your app as dpi aware.

Solution 4 - C#

This is how you can do it in WPF. The return value is in WPF's logical units, which are equal to 1/96th of an inch. So if your screen DPI is set to 96, you will get a value of 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)

Solution 5 - C#

Here's a solution that works nicely in Windows 10. There's no need for DPI awareness or anything.

public static int GetWindowsScaling()
{
	return (int)(100 * Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth);
}

Solution 6 - C#

Using Farshid T's answer as a base works in every scaling factor, except for 125%. I tested about 20 different scaling factors, and the DPI always returns as 96, except for when set at 125%, which returns a DPI of 120. 120 / 96 = 1.25. Not sure why this is the case, but this code seems to work for any scale setting.

    [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

and usage:

        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...
        }

Solution 7 - C#

This is a very old question, but since Windows 8.1, one can use various other functions, like GetDpiForWindow

In 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;
    }
}

For this to work correctly on Windows 10 anniversary, you need to add an app.manifest to your C# project:

<?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>

Solution 8 - C#

I use this way in my console application:

float dpiX, dpiY;
using (Graphics graphics = Graphics.FromHwnd(IntPtr.Zero))
{
    dpiX = graphics.DpiX;
    dpiY = graphics.DpiY;
}

Solution 9 - C#

In case of WPF use the following snippet,

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;
        }

Solution 10 - C#

A more complete version of Ric Gaudet's answer:

[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;
}

Solution 11 - C#

I think this should provide you with the information you are looking for:

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

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

Edit - oh sorry it looks like there is an easier way to get this information now without a pinvoke,

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionnewmemberView Question on Stackoverflow
Solution 1 - C#Farshid TView Answer on Stackoverflow
Solution 2 - C#David HeffernanView Answer on Stackoverflow
Solution 3 - C#Anton SemenovView Answer on Stackoverflow
Solution 4 - C#Eternal21View Answer on Stackoverflow
Solution 5 - C#Kyle DelaneyView Answer on Stackoverflow
Solution 6 - C#Ric GaudetView Answer on Stackoverflow
Solution 7 - C#ZiriaxView Answer on Stackoverflow
Solution 8 - C#honzakuzel1989View Answer on Stackoverflow
Solution 9 - C#Anees DeenView Answer on Stackoverflow
Solution 10 - C#Ultimate LukiView Answer on Stackoverflow
Solution 11 - C#asawyerView Answer on Stackoverflow