25

I would like to get the actual screen dpi/ppi, not the dpi setting used for font in C++.

I tried with the following codes:

Version 1, reports 72 dpi, which is wrong.

SetProcessDPIAware(); //true
HDC screen = GetDC(NULL);
double hSize = GetDeviceCaps(screen, HORZSIZE);
double vSize = GetDeviceCaps(screen, VERTSIZE);
double hRes = GetDeviceCaps(screen, HORZRES);
double vRes = GetDeviceCaps(screen, VERTRES);
double hPixelsPerInch = hRes / hSize * 25.4;
double vPixelsPerInch = vRes / vSize * 25.4;
ReleaseDC(NULL, screen);
return (hPixelsPerInch + vPixelsPerInch) * 0.5;

Version 2, reports 96 dpi, which is the Windows dpi setting for font, but not the actual screen dpi.

SetProcessDPIAware(); //true
HDC screen = GetDC(NULL);
double hPixelsPerInch = GetDeviceCaps(screen,LOGPIXELSX);
double vPixelsPerInch = GetDeviceCaps(screen,LOGPIXELSY);
ReleaseDC(NULL, screen);
return (hPixelsPerInch + vPixelsPerInch) * 0.5;
Andy Li
  • 5,894
  • 6
  • 37
  • 47
  • 3
    FYI [EDID allows to get a perfect solution](http://stackoverflow.com/questions/577736/how-to-obtain-the-correct-physical-size-of-the-monitor) – Brian Cannard Jan 16 '15 at 19:08
  • @BrianHaak: As noted in this question and the one you linked to, the EDID is not always available, and sometimes the information in the EDID is wrong. – Adrian McCarthy Nov 19 '18 at 21:55

4 Answers4

16

I'm honestly confused by the answers here.

Microsoft has a GetDpiForMonitor method:

https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx

And monitors DO expose their physical dimensions to tools. You can read your monitors width and height, in centimeters, using the HWiNFO64 tool. So if they're getting it (DDI?), it stands to reason that you can access that information yourself.

Even a different Stack Overflow post mentions using WmiMonitorBasicDisplayParams to get the data.

How to get monitor size

So the top post is flat-out, 100%, wrong.

Community
  • 1
  • 1
Katastic Voyage
  • 965
  • 9
  • 17
  • 4
    GetDpiForMonitor gives you the dots per logical inch, not physical inch. Some monitors expose their physical dimensions through EDID. Unfortunately, that is not universally available; many monitors don't support it. The Remarks section of the documentation for WmiMonitorBasicDisplayParams points out that the actual values may not be available. Also, a display could be a projector, and there's no way for a projector to know the throw distance, so it can't compute the physical dimensions even if it knows the zoom setting. There is no reliable way to get the actual DPI in all cases. Sorry. – Adrian McCarthy Feb 02 '17 at 22:19
  • 2
    `GetDpiForMonitor` called with `MDT_RAW_DPI` will indeed return the actual physical DPI (if it's available obviously). On my machine, `MDT_EFFECTIVE_DPI` = (144, 144), `MDT_RAW_DPI` = (158, 160). – diapir Sep 27 '18 at 16:11
  • 1
    Not wrong at the time, just outdated. I think those APIs you're referring to were added in Windows 8.1 and 10 – O'Rooney Mar 25 '20 at 03:08
15

What you're asking for is, unfortunately, not possible in the general case.

Windows doesn't know the physical screen size. Windows might know that your screen has 1024x768 pixels, but it doesn't know how big the screen actually is. You might pull the cable out of your old 13" screen and connect it to a 19" monitor without changing the resolution. The DPI would be different, but Windows won't notice that you changed monitors.

You can get the true physical dimensions and DPI for a printer (assuming the driver isn't lying), but not for a screen. At least not reliably.

UPDATED

As others have pointed out, there are standards for two-way communication between newer monitors and the OS (EDID), that might make this information available for some devices. But I haven't yet found a monitor that provides this information.

Even if EDID were universally available, it's still not solvable in the general case, as the display could be a video projector, where the DPI would depend on the zoom, the focus, the lens type, and the throw distance. A projector is extremely unlikely to know the throw distance, so there's no way for it to report the actual DPI.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • 5
    Upvoting for correct answer. Windows does not know the physical dimensions of your monitor. All it knows is that it's `1680x1050`. It doesn't know if that's a 22" monitor (90dpi), a 21" monitor (94dpi), a 20" monitor (99dpi), or a 4" handheld (495dpi). Fortunately as a Windows developer you don't have to *care* about the monitor's physical resolution. – Ian Boyd Jan 05 '14 at 19:47
  • 5
    @Ian Boyd `Fortunately as a Windows developer you don't have to care about the monitor's physical resolution.` With the coming of 4K monitors that's proving to not be the case. People are finding that Windows + 4K isn't terribly useable as most programs don't handle the DPI increase very well. Even scaling things up with Windows doesn't work well across the board. I hope things get worked out, as someone eager to get a 4K monitor. – leetNightshade Aug 15 '14 at 22:23
  • 7
    @leetNightshade: Ian is right, Windows developers don't care about the _physical_ DPI. They must care about the _logical_ DPI. Unfortunately, Windows let too many developers get away with ignoring it for too long, so now that there's been a big step up in resolution, we're all suffering. – Adrian McCarthy Aug 15 '14 at 23:25
  • 3
    @leetNightshade As a Windows developer you should have been doing since the 1980s what web developers are learning today. On the web you should be specifying sizes of images, buttons, text boxes, etc in `em` or `en`. That way you do not care about the monitor's resolution. Windows has used an equivalent of `em` for decades: the **dialog unit** (based on the width and height of the average character in the font you're using). – Ian Boyd Aug 16 '14 at 15:07
  • 2
    [EDID with driver's query from USERMODE](http://stackoverflow.com/questions/577736/how-to-obtain-the-correct-physical-size-of-the-monitor) – Brian Cannard Jan 16 '15 at 19:07
  • @avesys: EDID is not universally available. For example, on the machine I'm using right now, it's all zeros. And if you're display device is a projector, then the screen size is variable depending on throw distance and zoom settings. – Adrian McCarthy Jan 16 '15 at 20:44
  • 1
    It's completely false that you can't get the physical dimensions of a monitor in Windows. See my post. – Katastic Voyage Feb 02 '17 at 06:09
  • 2
    It's 2017, knowing the physical dimensions of a typical screen (i.e. non-beamer) is important information for many UI/UX designers. What good is knowing the logical dpi if you want your icon to be a certain physical size? – jiggunjer Aug 18 '17 at 08:15
  • 1
    @jiggunjer: The number of cases where you'd want to make something a physical size seems vanishingly small. The important thing to know is what size text can the user can comfortably read. Virtually all UI layout should be based on a virtual metric rather than physical size. – Adrian McCarthy Sep 08 '17 at 18:55
  • 1
    Windows DOES know the physical dimensions of a screen. On my laptop's 4K screen (15.6") it shows 100% as suggested scaling value, on my 4K 32" monitor it shows 150% as suggested scaling value. The resolution in both cases is exactly 3840x2160. On still another monitor (friend's) with 3840x2160 resolution and 24" size the recommended scaling factor is 200%. Windows considers among other things (angle subtended at eye?) the size of panel while making the suggestion. Size of Panel plays a MAJOR role. Almost all monitors (not projectors) I have seen report physical size via EDID. Downvoting. – Sahil Singh Feb 15 '19 at 07:42
  • "Almost all" != "all" I have a couple monitors that don't have EDID information and one that has incorrect EDID information. If you're writing code that depends on (correct) EDID information, you're going to have to have some sort of fallback for the cases when it's not available or wrong. – Adrian McCarthy Feb 15 '19 at 18:08
  • If this is not possible, how can hwInfo provide me exact measurements for my monitor. And I am using WinXp. – user13947194 Sep 19 '22 at 02:05
  • user13947194: I said it's not possible _in the general case_. That doesn't mean it's always impossible. Today, more monitors provide correct EDID information than 10 years ago when I wrote this answer. There are many cases where it _is_ possible, and probably even most cases. But if you're writing software that has to run on any display out there, you should have a fallback plan in case the EDID information is missing or wrong. – Adrian McCarthy Sep 20 '22 at 18:53
1

Getting DPI information is found to produce exact value using the below method.

ID2D1Factory* m_pDirect2dFactory;
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory);
FLOAT dpiX, dpiY;
m_pDirect2dFactory->GetDesktopDpi( &dpiX, &dpiY );
MGR
  • 313
  • 3
  • 14
  • 6
    -1. The question is asking for the actual _physical_ DPI. This D2D interface (which is available only on Windows 7 and up) is simply providing the same logical DPI that you get from `GetDeviceCaps(hdcScreen, LOGPIXELSX)`. As I said in my answer, there is no way to get exactly what Andy Li is asking for, since Windows doesn't know the true dimensions of each monitor. Also note that you could attach two different monitors with different physical DPIs, and these methods give only one answer for the "desktop". – Adrian McCarthy Nov 20 '13 at 13:59
0

I think what you're after is:

GetDeviceCaps(hdcScreen, LOGPIXELSX); GetDeviceCaps(hdcScreen, LOGPIXELSY);

James Bedford
  • 28,702
  • 8
  • 57
  • 64
  • That would be useful for a printer DC. For a monitor, it returns the number of pixels per logical inch, which is unlikely to match the physical DPI. – Adrian McCarthy Jan 03 '20 at 13:30