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?
-
1Which setting are you interested in? There are many. – David Heffernan May 12 '11 at 11:46
-
if you open this page with a setting - Control Panel -> Display. There will be only one setting which offers to change the size of the text and other items on the screen. The options are "Smaller", "Medium" and "Larger" – newmember May 12 '11 at 11:50
11 Answers
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%
}

- 2,073
- 1
- 19
- 16
-
13Wow, I have searched for a long time how to get the true scaling factor, and this finally solved my problems! – andreas Jan 17 '15 at 01:40
-
I second this. I am on a Dell XPS 15 and finding with a native resolution of 3200x1800 than I could not work out the scaling factor to usefully use values from things like GetWindowPlacement etc. For example, when using a console app the scaling is 2, and when using LINQPad it's 1. This was the answer finally!!! Thank you – joshcomley Feb 07 '15 at 15:54
-
18That moment when you're just about to cry and give up and then you come across something like this. – The Muffin Man Apr 21 '15 at 22:06
-
7Please note that you will likely want to use *both* this solution and the solution marked as accepted... on my system, reading g.DpiX returns 120 but the scaling factor is still 1.0. – Todd Myhre Jul 21 '15 at 15:41
-
This is useful when performing html to pdf conversion with wkhtmltopdf on Windows, where the zoom factor has to be set according to this scaling factor to get consistent output on machines with varying display DPI. – Michael Petito Jul 06 '16 at 15:31
-
9FYI, on a Dell Precision 5510 laptop, running Win 10 64-bit, resolution 3840x2160, display scale set to 200% (from right click desktop >> Display Settings), both the LogicalScreenHeight and PhysicalScreenHeight in the above code return the same thing, 2160. So this code doesn't work to get the desired 200% display scale in this scenario. – Nerdtron Jan 28 '17 at 16:44
-
7I observed the same issue as @Nerdtron using similar hardware. This code does not work reliably across monitors, unfortunately. – OfficeAddinDev Mar 08 '17 at 20:38
-
3This method returns a scaling factor of 1.0 on my monitor using Windows 7 and a DPI Scale of 150%, so it does not work reliably like others said. – inexcitus Feb 14 '18 at 12:36
-
2I found that using the following for the 'LogicalScreenHeight' gave me correct results: var LogicalScreenHeight = SystemParameters.WorkArea.Height; – S. Rasmussen Jul 01 '20 at 17:41
-
I have Windows 10 Pro version 2004 (build 19041.508) and this method worked perfectly for me. My TV has the option "Change the size of text, apps, and other items" set to 250% within Display settings and the above code returns 2.5. – rhyek Sep 19 '20 at 15:39
-
4Thanks a lot for this idea. Maybe you could add a `g.ReleaseHdc()`and a `g.Dispose()` to your example? – martinstoeckli Oct 31 '20 at 11:33
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.

- 601,492
- 42
- 1,072
- 1,490
-
9I tried to run this code with the different settings and the value of the graphics.DpiX is always 96. – newmember May 12 '11 at 12:22
-
@newmember When you change the setting, Windows tells you that you need to log off before the setting takes effect. You really do need to do this! – David Heffernan May 12 '11 at 12:26
-
Yes, I did log off and log in again - as a result I have the sizes of everything are changed but the DpiX is 90! – newmember May 12 '11 at 12:30
-
1DpiX is 90?! I logged off and on and and it changed from 96 to 120. I was using a fresh WinForms app. I wonder if you are doing something special in your app. Did you try in a brand new WinForms project? – David Heffernan May 12 '11 at 12:34
-
I've just created a fresh WinForms app with the only two lines of code: Graphics graphics = CreateGraphics(); MessageBox.Show(graphics.DpiX.ToString()); //And it shows 96 in the smaller and in the larger modes. – newmember May 12 '11 at 12:42
-
-
Sorry, I've made a mistake in the previos comment - DpiX is 96 ofcourse. – newmember May 12 '11 at 12:44
-
1I tried to set medium setting and - it works! DpiX is 120, but for the larger setting it is 96... – newmember May 12 '11 at 12:52
-
11Right, I think I get it now. The problem is that your app is not marked as DPI aware and so it probably appears all fuzzy. You should mark your app as DPI aware in the manifest. – David Heffernan May 12 '11 at 12:54
-
3@ColonelPanic `this` is just any control. http://msdn.microsoft.com/en-us/library/system.windows.forms.control.creategraphics.aspx – David Heffernan Feb 13 '13 at 15:31
-
@DavidHeffernan I think it should be `using (Graphics g = this.CreateGraphics()) {...}` – KindDragon Apr 11 '17 at 12:30
-
Isn't this backwards, though? Shouldn't *more* DPI result in a *smaller* image, while a larger image would have fewer DPI? – Kyle Delaney Sep 25 '20 at 21:17
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)

- 4,190
- 2
- 48
- 63
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.

- 6,227
- 5
- 41
- 69
-
16
-
1@BitsEvolved absolutely. This solves "my" memory leak (within a completely different context)! Thanks a lot. – Wolf Apr 17 '15 at 09:54
-
3In the comments for GetDeviceCaps function, in the link you provided, it says the following for LOGPIXELSX and LOGPIXELSY: "In a system with multiple display monitors, this value is the same for all monitors." Something to be aware of ... – RenniePet Nov 25 '18 at 00:54
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);
}

- 11,616
- 6
- 39
- 66
-
Interesting thing that sometimes Screen.PrimaryScreen.Bounds.Width does not return actual screen width and returns the same as SystemParameters.PrimaryScreenWidth, I've got this issue working with UI Automation. And I had to use Screen.PrimaryScreen.WorkingArea.Width instead. – sarh Sep 25 '20 at 20:01
-
I assume this works because `Screen.PrimaryScreen.Bounds` have the physical pixels and SystemParameters.PrimaryScreenWidth is logical? Too bad we cannot determine it for the other screens – Garr Godfrey Nov 03 '21 at 23:57
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...
}

- 898
- 6
- 16
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>

- 1,012
- 10
- 19
-
You say this should work for Windows 8.1 and later, but in the Microsoft document you reference it says "Requirements: Windows 10, version 1607". – RenniePet Nov 25 '18 at 01:06
-
Oh yes indeed, good catch. I guess I confused this with the GetDpiForMonitor api call. https://learn.microsoft.com/en-us/windows/win32/api/shellscalingapi/nf-shellscalingapi-getdpiformonitor – Ziriax Sep 29 '19 at 21:28
I use this way in my console application:
float dpiX, dpiY;
using (Graphics graphics = Graphics.FromHwnd(IntPtr.Zero))
{
dpiX = graphics.DpiX;
dpiY = graphics.DpiY;
}

- 2,400
- 2
- 29
- 32
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;
}

- 1,385
- 2
- 17
- 31
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;
}

- 224
- 1
- 6
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

- 17,642
- 8
- 59
- 87
-
Unfortunately, systeminformation does not reflect the dpi scale. Also it seems that the dpiX/dpiY property of the graphics context returns 96 for 150% (at least on a surface pro 2). So you can't distinguish between 100% and 150% zoom. Does anybody know a proper method? – Zuppa Dec 13 '13 at 13:30