博客
关于我
EDID的获取和解析
阅读量:725 次
发布时间:2019-03-21

本文共 4512 字,大约阅读时间需要 15 分钟。

EDID信息是显示器的 firmware 资料,通常保存了显示器的屏幕尺寸数据。通过分析显示器的分辨率,我们可以计算出 PPI,从而更好地调整字体大小以适应不同尺寸的屏幕。

从多种方法获取显示器尺寸信息

在实际操作中,获取显示器的物理尺寸信息可能会遇到一些挑战。以下是几种常用的方法及其优缺点分析:

1. 使用 WMI (Windows Management Instrumentation)

WMI 是一个强大的工具,能够提供对显示器等设备的详细监控信息。然而,这种方法有着较高的复杂性,尤其是对于老旧的图形驱动程序(如 Windows Vista 时期的),可能会导致易卡或错误的 LastError 式。因此,尽管 WMI 有潜力,但在实际应用中可能并不适用于所有场景。

2. 调用 SetupAPI

最终,通过 SetupAPI 方法,可以以相对简便的方式获取到设备信息。Calvin Guan 提供的实现方式简单来说有以下几个关键步骤:

  • 调用 SetupDiGetClassDevsEx 获取对应设备的 HDEVINFO 描述信息。
  • 使用 HDEFINFO 调用 SetupDiEnumDeviceInfo 的方法,获取每个设备的详细信息。
  • 通过 SetupDiOpenDevRegKey 方法,获取到与设备相关的注册表项(HKEY),进而 提取 EDID 数据。

这种方法的优点是支持直接从 EDID 中提取尺寸信息,并且可以在非管理员权限下运行。具体实现中,需要注意以下几点:

  • SetupDiOpenDevRegKey 调用时,使用 KEY_READ 而非 KEY_ALL_ACCESS,以确保非管理员用户也有权限访问。
  • 定期进行内存清理,避免因未释放资源而引发错误。
  • 从 EDID 数据中提取屏幕尺寸的逻辑需要特别注意,确保正确解析出毫米级的数值,而非厘米级。

3. 从注册表提取 EDID 数据

EDID 数据是设备制造商提供的信息,通常位于注册表中的特定路径。但这一路径在不同厂商之间存在较大的差异,并且文档中通常不会明确指出具体路径。这意味着我们需要自行查找相关信息,这可能会带来较为复杂的工作量。

尽管如此,从 EDID 数据中提取显示器尺寸仍然是一个值得采取的方向。它能够提供与屏幕实际尺寸相关的详尽信息,并且这种方法在 registry 中通常更容易长期维护。

重点改进及代码实践

在实际开发过程中,可能会遇到多个需要重点改进的点。例如:

  • 需要确保代码中的错误处理能够全面覆盖所有可能的错误情形。
  • 提升代码的稳定性,避免因资源泄漏或不规范使用而导致系统崩溃。
  • 充分解析 EDID 数据中的物理尺寸参数,确保数值是什么单位(毫米 vs 厘米)。

针对这些改进点,我们可以参考以下代码示例:

#include 
#include
#pragma comment(lib, "setupapi.lib")#define NAME_SIZE 128const GUID GUID_CLASS_MONITOR = {0x4d36e96e, 0xe325, 0x11ce, 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18};bool GetMonitorSizeFromEDID(const HKEY hDevRegKey, short& WidthMm, short& HeightMm) { DWORD dwType; DWORD AcutalValueNameLength = NAME_SIZE; TCHAR valueName[NAME_SIZE]; BYTE EDIDdata[1024]; DWORD edidsize = sizeof(EDIDdata); for (LONG i = 0, retValue = ERROR_SUCCESS; retValue != ERROR_NO_MORE_ITEMS; ++i) { retValue = RegEnumValue(hDevRegKey, i, &valueName[0], AcutalValueNameLength, NULL, &dwType, EDIDdata, edidsize); if (retValue != ERROR_SUCCESS || 0 != _tcscmp(valueName, _T("EDID"))) { continue; } WidthMm = ((EDIDdata[68] & 0xF0) << 4) + EDIDdata[66]; HeightMm = ((EDIDdata[68] & 0x0F) << 8) + EDIDdata[67]; return true; // valid EDID found } return false; // EDID not found}bool GetSizeForDevID(const CString& TargetDevID, short& WidthMm, short& HeightMm) { HDEVINFO devInfo = SetupDiGetClassDevsEx(&GUID_CLASS_MONITOR, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL); if (NULL == devInfo) { return false; } bool bRes = false; for (ULONG i = 0; ERROR_NO_MORE_ITEMS != GetLastError(); ++i) { SP_DEVINFO_DATA devInfoData; memset(&devInfoData, 0, sizeof(devInfoData)); devInfoData.cbSize = sizeof(devInfoData); if (SetupDiEnumDeviceInfo(devInfo, i, &devInfoData)) { HKEY hDevRegKey = SetupDiOpenDevRegKey(devInfo, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); if (!hDevRegKey || hDevRegKey == INVALID_HANDLE_VALUE) { RegCloseKey(hDevRegKey); continue; } bool trovato = GetMonitorSizeFromEDID(hDevRegKey, WidthMm, HeightMm); if (trovato) { bRes = true; } RegCloseKey(hDevRegKey); } SetupDiDestroyDeviceInfoList(devInfo); return bRes; } SetupDiDestroyDeviceInfoList(devInfo); return bRes;}int _tmain(int argc, _TCHAR* argv[]) { short WidthMm, HeightMm; DISPLAY_DEVICE dd; dd.cb = sizeof(dd); DWORD dev = 0; int id = 1; // 应与显示设置中的显示器索引匹配 CString DeviceID; bool bFoundDevice = false; while (EnumDisplayDevices(0, dev, &dd, 0) && !bFoundDevice) { DISPLAY_DEVICE ddMon; ZeroMemory(&ddMon, sizeof(ddMon)); ddMon.cb = sizeof(ddMon); DWORD devMon = 0; while (EnumDisplayDevices(dd.DeviceName, devMon, &ddMon, 0) && !bFoundDevice) { if ((ddMon.StateFlags & DISPLAY_DEVICE_ACTIVE) && !(ddMon.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)) { DeviceID.Format("%s", ddMon.DeviceID); size_t pos = DeviceID.Find(_T("\\"), 9) - 8; DeviceID = DeviceID.Mid(8, pos - 8); bFoundDevice = GetSizeForDevID(DeviceID, WidthMm, HeightMm); } devMon++; ZeroMemory(&ddMon, sizeof(ddMon)); ddMon.cb = sizeof(ddMon); } } ZeroMemory(&dd, sizeof(dd)); dd.cb = sizeof(dd); dev++;}

总结

总之,获取显示器尺寸信息的方法有很多,正确的方法需要根据具体的场景和需求来选择。通过合理地结合不同技术手段,可以有效地解决问题。同时,写代码的时候一定要注重代码的可维护性和稳定性,避免以后遇到新的问题。

转载地址:http://sjfgz.baihongyu.com/

你可能感兴趣的文章
mysql5.5和5.6版本间的坑
查看>>
mysql5.5最简安装教程
查看>>
mysql5.6 TIME,DATETIME,TIMESTAMP
查看>>
mysql5.6.21重置数据库的root密码
查看>>
Mysql5.6主从复制-基于binlog
查看>>
MySQL5.6忘记root密码(win平台)
查看>>
MySQL5.6的Linux安装shell脚本之二进制安装(一)
查看>>
MySQL5.6的zip包安装教程
查看>>
mysql5.7 for windows_MySQL 5.7 for Windows 解压缩版配置安装
查看>>
Webpack 基本环境搭建
查看>>
mysql5.7 安装版 表不能输入汉字解决方案
查看>>
MySQL5.7.18主从复制搭建(一主一从)
查看>>
MySQL5.7.19-win64安装启动
查看>>
mysql5.7.19安装图解_mysql5.7.19 winx64解压缩版安装配置教程
查看>>
MySQL5.7.37windows解压版的安装使用
查看>>
mysql5.7免费下载地址
查看>>
mysql5.7命令总结
查看>>
mysql5.7安装
查看>>
mysql5.7性能调优my.ini
查看>>
MySQL5.7新增Performance Schema表
查看>>