Как получить правильный физический размер монитора?
Как узнать размер дисплея в сантиметрах или дюймах?
Этот код не всегда работает правильно:
HDC hdc = CreateDC(_T("DISPLAY"),dd.DeviceName,NULL,NULL);
int width = GetDeviceCaps(hdc, HORZSIZE);
int height = GetDeviceCaps(hdc, VERTSIZE);
ReleaseDC(0, hdc)
Специально для конфигурации с несколькими мониторами.
Обновление: мне нужно получить размер только для обычных мониторов, которые имеют постоянный физический размер.
Ответов (7)7
Я нашел другой способ. Физические размеры монитора хранятся в EDID, и Windows почти всегда копирует его значение в реестре. Если вы можете разобрать EDID, вы можете прочитать ширину и высоту монитора в сантиметрах.
Обновление: добавлен код
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;
}
Прямая навигация по реестру не только не поддерживается, но и не работает на устройствах, отличных от вашего. (например, тот, на котором я тестировал ваш код).
В отличие от того, что некоторые здесь говорят, там есть официальный способ доступа путь ключа EDID: путем использования API настройки, и , в частности SetupDiOpenDevRegKey .
Требуется некоторая утомительная настройка - пример кода здесь .
EDIT: несколько мониторов обрабатываются здесь .
Windows Vista и более поздние версии поддерживают новую функцию GetMonitorDisplayAreaSize () http://msdn.microsoft.com/en-us/library/ms775210%28VS.85%29.aspx
Обновление: он не работает должным образом
Невозможно определить точный физический размер видеоустройства в Windows, поскольку это зависит от довольно большого количества переменных (например, активного профиля монитора, разрешения по горизонтали / вертикали, размера пикселя и т. Д.), Некоторые из которых не находятся под управление компьютером.
Подумайте, например, о проекторах, физический размер которых зависит от расстояния до области проецирования, которое не может быть определено программно, поскольку видеопроектор можно перемещать вручную в любое время.
Вы не можете получить реальный точный размер - вы можете получить приблизительное значение, которое зависит от настройки DPI в Windows и разрешения экрана, но вы не можете гарантировать, что это реальный размер.
Особенно в ситуации с несколькими мониторами с разными дисплеями (скажем, 19 "CRT и 24" LCD). Кроме того, если дисплей представляет собой ЭЛТ, тогда измерение - это измерение трубки, а не область отображения.
Когда программы нуждались в этой информации точно в прошлом, они показывали датчик на экране и предлагали пользователю поднести лист бумаги к экрану и измерить ширину бумаги датчиком. Учитывая, что размер бумаги 8,5 дюймов или A4, вы знаете ширину и можете использовать введенное число для определения реального DPI для данного дисплея. Возможно, вам потребуется, чтобы они делали это для каждого монитора в многомониторной установке.
-Адам
Вы можете запросить LOGPIXELSX у GetDeviceCaps, чтобы получить DPI для дисплея, хотя обычно он возвращает 96. См. Также эту статью MSDN о написании приложений с поддержкой DPI .