Reputation: 6861
How can I get the size of the display in centimeters or inches?
This code does not always works correctly:
HDC hdc = CreateDC(_T("DISPLAY"),dd.DeviceName,NULL,NULL);
int width = GetDeviceCaps(hdc, HORZSIZE);
int height = GetDeviceCaps(hdc, VERTSIZE);
ReleaseDC(0, hdc)
Especially for multi-monitor configuration.
Update: I need to get the size just for ordinary monitors, which have a constant physical size.
Upvotes: 11
Views: 28015
Reputation: 159
I recently encountered the same issue and finally found a solution myself
if (QueryDisplayConfig(
QDC_ONLY_ACTIVE_PATHS,
&pathCount,
paths,
&modeCount,
modes,
NULL) == ERROR_SUCCESS)
{
// Use paths and modes
}
paths
and modes
contain all active display paths, which contain width / height (in pixel), refresh rate, etc.
DISPLAYCONFIG_TARGET_DEVICE_NAME targetName = {
.header = {
.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME,
.size = sizeof(targetName),
.adapterId = path->targetInfo.adapterId,
.id = path->targetInfo.id,
},
};
if(DisplayConfigGetDeviceInfo(&targetName.header) == ERROR_SUCCESS)
{
// Use targetName.monitorDevicePath
}
In my laptop, targetName.monitorDevicePath
is \\?\DISPLAY#PRL5000#3&21de92e8&0&UID0#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
. Notibly DISPLAY#PRL5000#3&21de92e8&0&UID0
#
with \
; pretend SYSTEM\CurrentControlSet\Enum
; append Device Parameters
wchar_t regPath[256] = L"SYSTEM\\CurrentControlSet\\Enum";
wchar_t* pRegPath = regPath + strlen("SYSTEM\\CurrentControlSet\\Enum");
wchar_t* pDevPath = targetName.monitorDevicePath + strlen("\\\\?");
while (*pDevPath && *pDevPath != L'{')
{
if (*pDevPath == L'#')
*pRegPath = L'\\';
else
*pRegPath = *pDevPath;
++pRegPath;
++pDevPath;
assert(pRegPath < regPath + sizeof(regPath) / sizeof(wchar_t) + strlen("Device Parameters"));
}
wcscpy(pRegPath, L"Device Parameters");
You get L"SYSTEM\CurrentControlSet\Enum\DISPLAY\PRL5000\3&21de92e8&0&UID0\Device Parameters"
uint8_t edidData[1024];
DWORD edidLength = sizeof(edidData);
if (RegGetValueW(HKEY_LOCAL_MACHINE, regPath, L"EDID", RRF_RT_REG_BINARY, NULL, edidData, &edidLength) == ERROR_SUCCESS &&
edidLength > 0 && edidLength % 128 == 0)
{
// Read physical size from EDID data
}
I use this function to read physical size from EDID
void ffEdidGetPhysicalSize(const uint8_t edid[128], uint32_t* width, uint32_t* height)
{
*width = (((uint32_t) edid[68] & 0xF0) << 4) + edid[66];
*height = (((uint32_t) edid[68] & 0x0F) << 8) + edid[67];
}
For multi-monitor machine, you may match an HMONITOR
to an DISPLAYCONFIG_PATH_INFO
:
DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName = {
.header = {
.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME,
.size = sizeof(sourceName),
.adapterId = path->sourceInfo.adapterId,
.id = path->sourceInfo.id,
},
};
if (DisplayConfigGetDeviceInfo(&sourceName.header) == ERROR_SUCCESS)
{
// Use sourceName.viewGdiDeviceName
}
szDevice
from a HMONITOR
MONITORINFOEXW monitorInfo;
GetMonitorInfoW(hMonitor, (MONITORINFO*) &monitorInfo);
// Use monitorInfo.szDevice
sourceName.viewGdiDeviceName
should contain the same string as monitorInfo.szDevice
, which is \\.\DISPLAY1
in my laptopFull code, which detects current resolution in pixel
, refresh rate
, DPI scaled resolution in pixel
, rotation in degree
, monitor brand name
, physical size in mm
, etc: https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/displayserver/displayserver_windows.c
Upvotes: 0
Reputation: 16109
Navigating the registry directly is not only unsupported, but actually fails for devices different than yours. (e.g., the one on which I tested your code).
Unlike what some here say, there is an official way of accessing the EDID key path: by use of the Setup API, and specifically SetupDiOpenDevRegKey.
There's some tedious setup involved - Sample code is here.
EDIT: multiple monitors are handled here.
Upvotes: 9
Reputation: 6861
I found another way. The physical size of the monitor are stored in the EDID, and Windows are almost always copies of its value in the registry. If you can parse EDID, you can read the width and height of the monitor in centimeters.
Update: Added code
BOOL GetMonitorDevice( TCHAR* adapterName, DISPLAY_DEVICE &ddMon )
{
DWORD devMon = 0;
while (EnumDisplayDevices(adapterName, devMon, &ddMon, 0))
{
if (ddMon.StateFlags & DISPLAY_DEVICE_ACTIVE &&
ddMon.StateFlags & DISPLAY_DEVICE_ATTACHED) // for ATI, Windows XP
break;
devMon++;
}
if (ddMon.DeviceString[0] == '\0')
{
EnumDisplayDevices(adapterName, 0, &ddMon, 0);
if (ddMon.DeviceString[0] == '\0')
_tcscpy_s(ddMon.DeviceString, _T("Default Monitor"));
}
return ddMon.DeviceID[0] != '\0';
}
BOOL GetMonitorSizeFromEDID(TCHAR* adapterName, DWORD& Width, DWORD& Height)
{
DISPLAY_DEVICE ddMon;
ZeroMemory(&ddMon, sizeof(ddMon));
ddMon.cb = sizeof(ddMon);
//read edid
bool result = false;
Width = 0;
Height = 0;
if (GetMonitorDevice(adapterName, ddMon))
{
TCHAR model[8];
TCHAR* s = _tcschr(ddMon.DeviceID, '\\') + 1;
size_t len = _tcschr(s, '\\') - s;
if (len >= _countof(model))
len = _countof(model) - 1;
_tcsncpy_s(model, s, len);
TCHAR *path = _tcschr(ddMon.DeviceID, '\\') + 1;
TCHAR str[MAX_PATH] = _T("SYSTEM\\CurrentControlSet\\Enum\\DISPLAY\\");
_tcsncat_s(str, path, _tcschr(path, '\\')-path);
path = _tcschr(path, '\\') + 1;
HKEY hKey;
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, str, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
DWORD i = 0;
DWORD size = MAX_PATH;
FILETIME ft;
while(RegEnumKeyEx(hKey, i, str, &size, NULL, NULL, NULL, &ft) == ERROR_SUCCESS)
{
HKEY hKey2;
if(RegOpenKeyEx(hKey, str, 0, KEY_READ, &hKey2) == ERROR_SUCCESS)
{
size = MAX_PATH;
if(RegQueryValueEx(hKey2, _T("Driver"), NULL, NULL, (LPBYTE)&str, &size) == ERROR_SUCCESS)
{
if (_tcscmp(str, path) == 0)
{
HKEY hKey3;
if(RegOpenKeyEx(hKey2, _T("Device Parameters"), 0, KEY_READ, &hKey3) == ERROR_SUCCESS)
{
BYTE EDID[256];
size = 256;
if(RegQueryValueEx(hKey3, _T("EDID"), NULL, NULL, (LPBYTE)&EDID, &size) == ERROR_SUCCESS)
{
DWORD p = 8;
TCHAR model2[9];
char byte1 = EDID[p];
char byte2 = EDID[p+1];
model2[0]=((byte1 & 0x7C) >> 2) + 64;
model2[1]=((byte1 & 3) << 3) + ((byte2 & 0xE0) >> 5) + 64;
model2[2]=(byte2 & 0x1F) + 64;
_stprintf(model2 + 3, _T("%X%X%X%X"), (EDID[p+3] & 0xf0) >> 4, EDID[p+3] & 0xf, (EDID[p+2] & 0xf0) >> 4, EDID[p+2] & 0x0f);
if (_tcscmp(model, model2) == 0)
{
Width = EDID[22];
Height = EDID[21];
result = true;
}
else
{
// EDID incorrect
}
}
RegCloseKey(hKey3);
}
}
}
RegCloseKey(hKey2);
}
i++;
}
RegCloseKey(hKey);
}
}
return result;
}
Upvotes: 20
Reputation: 6861
Windows Vista and upper support new function GetMonitorDisplayAreaSize() http://msdn.microsoft.com/en-us/library/ms775210%28VS.85%29.aspx
Update: It doesn't work properly
Upvotes: 4
Reputation: 23095
It is not possible to determine the exact physical size of a video device on windows as this depends on quite a lot of variables (e.g. active monitor profile, horizontal/vertical resolution, pixel size, etc.), some of which are not under the control of the computer.
Think for example of projector devices, where the physical size depends on the distance to the projection area which cannot be determined programmatically, as the video projector could be moved manually anytime.
Upvotes: 7
Reputation: 93555
You can't get the real exact size - you can get an approximation that depends on the DPI setting in windows, and the resolution of the screen, but you can't guarantee that this is the real size.
Especially in a multimonitor situation with different displays (say a 19" CRT and 24" LCD). Further, if the display is CRT then the measurement is the tube measurement, and not the display area.
When programs have needed this information exactly in the past, they've shown a gauge onscreen, and had the user hold a piece of paper up to the screen and measure the paper width with the gauge. Given the paper is 8.5" or A4 then you know the width, and you can use the number that they input to figure out the real DPI for a given display. You may need to have them do that for each monitor in a multimonitor setup.
-Adam
Upvotes: 5
Reputation: 300825
You can request LOGPIXELSX from GetDeviceCaps to get the DPI for the display, though it will generally return 96. See also this MSDN article on writing DPI-aware apps.
Upvotes: 1