本文共 4512 字,大约阅读时间需要 15 分钟。
EDID信息是显示器的 firmware 资料,通常保存了显示器的屏幕尺寸数据。通过分析显示器的分辨率,我们可以计算出 PPI,从而更好地调整字体大小以适应不同尺寸的屏幕。
在实际操作中,获取显示器的物理尺寸信息可能会遇到一些挑战。以下是几种常用的方法及其优缺点分析:
WMI 是一个强大的工具,能够提供对显示器等设备的详细监控信息。然而,这种方法有着较高的复杂性,尤其是对于老旧的图形驱动程序(如 Windows Vista 时期的),可能会导致易卡或错误的 LastError 式。因此,尽管 WMI 有潜力,但在实际应用中可能并不适用于所有场景。
最终,通过 SetupAPI 方法,可以以相对简便的方式获取到设备信息。Calvin Guan 提供的实现方式简单来说有以下几个关键步骤:
SetupDiGetClassDevsEx
获取对应设备的 HDEVINFO 描述信息。SetupDiEnumDeviceInfo
的方法,获取每个设备的详细信息。SetupDiOpenDevRegKey
方法,获取到与设备相关的注册表项(HKEY),进而 提取 EDID 数据。这种方法的优点是支持直接从 EDID 中提取尺寸信息,并且可以在非管理员权限下运行。具体实现中,需要注意以下几点:
SetupDiOpenDevRegKey
调用时,使用 KEY_READ 而非 KEY_ALL_ACCESS,以确保非管理员用户也有权限访问。EDID 数据是设备制造商提供的信息,通常位于注册表中的特定路径。但这一路径在不同厂商之间存在较大的差异,并且文档中通常不会明确指出具体路径。这意味着我们需要自行查找相关信息,这可能会带来较为复杂的工作量。
尽管如此,从 EDID 数据中提取显示器尺寸仍然是一个值得采取的方向。它能够提供与屏幕实际尺寸相关的详尽信息,并且这种方法在 registry 中通常更容易长期维护。
在实际开发过程中,可能会遇到多个需要重点改进的点。例如:
针对这些改进点,我们可以参考以下代码示例:
#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/