如何获取显示器的正确物理尺寸?

11

如何获取显示器的大小(厘米或英寸)?

以下代码并不总是有效:

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

特别是针对多显示器配置。
更新:我需要获取普通显示器的尺寸,这些显示器具有恒定的物理尺寸。
7个回答

20

我发现了另一种方法。显示器的物理尺寸存储在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;
}

4
稍微多缩进一点会更好,这样我就看不到在我30英寸的电脑屏幕上整行的内容了。 - devoured elysium
6
另外,值得注意的是,并非所有的显示器都提供EDID信息。 - Earlz
1
如果EDID不存在,你需要做一些合理的事情。(在我的机器上没有)你还必须考虑到像投影仪这样的东西,其中图像的大小取决于投射距离、变焦设置等因素。 - Adrian McCarthy
3
@KingDragon:我认为应该给予一些信任。https://ofekshilon.com/2014/06/19/reading-specific-monitor-dimensions/ - Ofek Shilon
1
我还没有测试运行这段代码,但我认为它在我的电脑上不会起作用。它只会返回字节22和21,这并不能给出我的屏幕尺寸。有效的方法是使用ofekshilon.com提供的链接(请参见上面的评论),该链接使用字节66、67和68,并以毫米为单位给出尺寸。在我的屏幕上,我正确地得到了w=480mm和h=270mm。 - youen
显示剩余6条评论

9
直接浏览注册表不仅不受支持,而且对于与您的设备不同的设备实际上会失败。(例如,我测试您的代码所用的设备)。
与某些人在这里说的不同,访问EDID密钥路径确实有一种官方方法:通过使用Setup API,特别是SetupDiOpenDevRegKey
其中涉及到一些繁琐的设置-示例代码在此处

编辑:多个显示器的处理在这里


你的代码无法处理多个显示器,因为GetsizeForDevID会忽略所使用的设备,它总是检索最后一个设备的大小。第一个设备的大小总是被忽略。 - Samuel
1
警告:并非所有显示器都可用EDID。 - Adrian McCarthy

7

在Windows上,无法确定视频设备的确切物理尺寸,因为这取决于许多变量(例如活动监视器配置文件、水平/垂直分辨率、像素大小等),其中一些不受计算机控制。

例如,考虑投影仪设备,在这种情况下,物理尺寸取决于到投影区域的距离,这无法通过编程确定,因为视频投影仪随时可以手动移动。


8
+1,你得想一想知道物理尺寸而不是实际分辨率有什么用处。 - user7116
1
六个字母的变量,每当你想要以“实际大小”显示某些内容时,比如一个文本页面,在打印预览情境下,你都会想要知道其物理尺寸。 - Rob Kennedy
纠正了我的英语错误。感谢你的提示。 - Kosi2801
5
水平/垂直分辨率和像素大小如何影响显示器的物理尺寸?我经常更改分辨率,但从未注意到显示器的大小会变化。这对投影仪等设备是正确的,但并不意味着获取物理尺寸总是不可能。只是对于投影仪等设备是不可能的。 - jalf
1
在CRT显示器上,更改分辨率和像素大小可能会影响使用管子来显示图像的程度,从而有效地改变显示的大小。 CRT图像往往会随着管子的老化而略微缩小,这就是为什么许多显示方案会进行超扫描的原因。 - Adrian McCarthy
显示剩余2条评论

5
您无法得到真正的确切尺寸 - 您可以获得一个近似值,该近似值取决于Windows中的DPI设置和屏幕的分辨率,但是您不能保证这是真实尺寸。特别是在具有不同显示器的多监视器情况下(例如19英寸CRT和24英寸LCD)。此外,如果显示器是CRT,则测量是管子测量,而不是显示区域测量。过去,当程序需要准确了解此信息时,它们会在屏幕上显示一个仪表,并要求用户将一张纸片放在屏幕上,并使用仪表测量纸片的宽度。鉴于纸张为8.5英寸或A4,则知道宽度,您可以使用他们输入的数字来计算给定显示器的真实DPI。您可能需要让他们在多监视器设置中为每个显示器执行此操作。- Adam

4

1

0

您可以从注册表中获取EDID。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接