Как получить правильный физический размер монитора?

Как я могу получить размер экрана в сантиметрах или дюймах?

этот код не всегда работает правильно:

HDC hdc = CreateDC(_T("DISPLAY"),dd.DeviceName,NULL,NULL);
int width = GetDeviceCaps(hdc, HORZSIZE);
int height = GetDeviceCaps(hdc, VERTSIZE);
ReleaseDC(0, hdc)

специально для конфигурации multi-монитора.

обновление: мне нужно получить размер только для обычных мониторов, которые имеют постоянные физические размеры.

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] == '')
    {
        EnumDisplayDevices(adapterName, 0, &ddMon, 0);
        if (ddMon.DeviceString[0] == '')
            _tcscpy_s(ddMon.DeviceString, _T("Default Monitor"));
    }
    return ddMon.DeviceID[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;
}

невозможно определить точный физический размер видеоустройства в windows, поскольку это зависит от довольно большого количества переменных (например, профиль активного монитора, горизонтальное / вертикальное разрешение, размер пикселя и т. д.), некоторые из которых не находятся под контролем компьютера.

подумайте, например, о проекторных устройствах, где физический размер зависит от расстояния до области проекции, которое не может быть определено программно, так как видеопроектор может быть перемещен вручную в любое время.


вы не можете получить реальный точный размер - вы можете получить приближение, которое зависит от настройки DPI в windows, и разрешение экрана, но вы не можете гарантировать, что это реальный размер.

особенно в ситуации мультимонитора с различными дисплеями (скажем, 19" CRT и 24" LCD). Кроме того, если дисплей ЭЛТ, то измерение является измерение трубки, а не область отображения.

когда программы нуждались в этой информации именно в прошлом, они показали датчик на экране, и пользователь держал лист бумаги до экрана и измерял ширину бумаги с помощью датчика. Учитывая, что бумага 8.5" или A4, вы знаете ширину, и вы можете использовать число, которое они вводят, чтобы выяснить реальный DPI для данного дисплея. Возможно, вам придется сделать это для каждого монитора в настройке мультимонитора.

Адам


навигация по реестру напрямую не только не поддерживается, но и фактически не работает для устройств, отличных от ваших. (например, тот, на котором я тестировал ваш код).

В отличие от того, что некоторые здесь говорят, там is официальный способ доступа к пути ключа EDID: с помощью API установки, и в частности SetupDiOpenDevRegKey.

есть некоторая сложность установки - пример кода здесь.


EDIT: несколько мониторы обрабатываются здесь.


Windows Vista и верхняя поддержка новой функции GetMonitorDisplayAreaSize() http://msdn.microsoft.com/en-us/library/ms775210%28VS.85%29.aspx

Update: он не работает должным образом


вы можете запросить LOGPIXELSX от разделе getdevicecaps чтобы получить DPI для дисплея, хотя он обычно возвращает 96. Смотрите также это MSDN статья о написании приложений с поддержкой DPI.


вы можете получить EDID из реестра.