【问题标题】:How to obtain the correct physical size of the monitor?如何获得显示器的正确物理尺寸?
【发布时间】:2010-10-09 07:54:21
【问题描述】:

如何获得以厘米或英寸为单位的显示器尺寸?

此代码并不总是能正常工作:

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

尤其适用于多显示器配置。

更新:我只需要为具有恒定物理尺寸的普通显示器获取尺寸。

【问题讨论】:

    标签: c windows winapi monitor


    【解决方案1】:

    我找到了另一种方法。显示器的物理大小存储在 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;
    }
    

    【讨论】:

    • 多一点缩进会更好。这样可以确保我在 30 英寸的电脑屏幕上看不到完整的线条。
    • 另外,值得注意的是,并非所有显示器都提供 EDID 信息。
    • 如果 EDID 不存在,您需要做一些合理的事情。 (它不在我的机器上。)您还必须考虑投影仪之类的东西,其中图像的大小取决于投射距离、变焦设置等。
    • @KingDragon:我相信有些功劳。 ofekshilon.com/2014/06/19/reading-specific-monitor-dimensions
    • 我没有测试过运行这段代码,但我认为它不能在我的电脑上运行。它只会返回字节 22 和 21,这并没有给出我的屏幕尺寸。起作用的是 ofekshilon.com 提供的链接(参见上面的评论),它使用字节 66、67 和 68,并以毫米为单位给出尺寸。在我的屏幕上,我正确地得到 w=480mm 和 h=270mm。
    【解决方案2】:

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

    以投影仪设备为例,其物理尺寸取决于无法通过编程方式确定的投影区域的距离,因为视频投影仪可以随时手动移动。

    【讨论】:

    • +1,你一定想知道知道物理尺寸而不是实际分辨率有什么用。
    • /me 认为 Kosi2801 只是通过使用“beamer”这个词来证明自己是德国人。它真的不是英文的“视频投影仪”的意思。 :)
    • 呵呵,现在我知道投影仪的德语俚语了。好的! Stackoverflow 作为一个跨文化的技术大熔炉 - 太棒了。
    • 水平/垂直分辨率和像素大小究竟会如何影响显示器的物理大小?我已经改变了很多分辨率,而且我从来没有注意到我的显示器在增长或缩小。投影仪确实如此,但这并不意味着获得物理尺寸是总是不可能的。只是投影仪之类的东西是不可能的。
    • 在 CRT 显示器上,改变分辨率和像素大小会影响用于显示图像的显像管的多少,从而有效地改变显示器的大小。 CRT 图像会随着显像管的老化而略微缩小,这就是许多显示方案过扫描的原因。
    【解决方案3】:

    不仅不支持直接导航注册表,而且对于与您的设备不同的设备实际上会失败。 (例如,我测试你的代码的那个)。

    与这里的某些人所说的不同,有一种访问 EDID 密钥路径的官方方法:通过使用 Setup API,特别是 SetupDiOpenDevRegKey

    涉及一些繁琐的设置 - Sample code is here


    编辑:处理多个监视器here

    【讨论】:

    • 您的代码不适用于多台显示器,因为 GetsizeForDevID 确实忽略了使用的设备,它总是检索最后一个的大小。第一个的大小总是被忽略。
    • 警告:EDID 不适用于所有显示器。
    【解决方案4】:

    您无法获得真正的确切尺寸 - 您可以获得取决于 Windows 中的 DPI 设置和屏幕分辨率的近似值,但您不能保证这是真实尺寸。

    尤其是在具有不同显示器(例如 19 英寸 CRT 和 24 英寸 LCD)的多显示器情况下。此外,如果显示是 CRT,则测量是管测量,而不是显示区域。

    当程序过去恰好需要此信息时,它们会在屏幕上显示一个量规,并让用户将一张纸放在屏幕上并用量规测量纸张宽度。假设纸张是 8.5 英寸或 A4,那么您知道宽度,您可以使用他们输入的数字来计算给定显示器的真实 DPI。您可能需要让他们为多显示器设置中的每台显示器执行此操作。

    -亚当

    【讨论】:

      【解决方案5】:

      Windows Vista及以上支持新函数GetMonitorDisplayAreaSize() http://msdn.microsoft.com/en-us/library/ms775210%28VS.85%29.aspx

      更新:它不能正常工作

      【讨论】:

        【解决方案6】:

        您可以从 GetDeviceCaps 请求 LOGPIXELSX 以获取显示器的 DPI,但它通常会返回 96。另请参阅 MSDN article on writing DPI-aware apps

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-02-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多