EDID的获取和解析

EDID是显示器的fireware资料,保存有显示器大小信息,可以根据显示屏分辨率计算PPI,以适应合适的字体大小。

相关资料如下:

 

https://ofekshilon.com/2011/11/13/reading-monitor-physical-dimensions-or-getting-the-edid-the-right-way/

https://www.winvistatips.com/threads/how-to-read-monitors-edid-information.181727/

 

 

Reading Monitor Physical Dimensions, or: Getting the EDID, the Right Way

Posted on November 13, 2011by Ofek Shilon

 


Edit: an improvement is published in a separate post


We recently needed to know the physical size of monitors on customer machines. Getting it right was a surprisingly tedious research – and definitely something that deserves more web presence – and so the results are below.

1. GetDeviceCaps

– is the immediate answer. The argument flags HORZSIZE / VERTSIZE are advertised to give the –

Width/Height, in millimeters, of the physical screen.

Alas, as many have discovered, GetDeviceCaps just does not work as advertised with these flags.

2. GetMonitorDisplayAreaSize

– is the next obvious guess. The documentation doesn’t state whether the obtained values are in pixels or physical units – I suspect it’s vendor specific, but didn’t get to check it myself since I kept getting the dreadful LastError 0xc0262582: “An error occurred while transmitting data to the device on the I2C bus.”. Gotta say I didn’t insist too much since the entire Monitor Configuration API set is both new to Vista and already ‘legacy graphics’, which are explicitly described as

Technologies that are obsolete and should not be used in new applications.

3. WMI

There’s a good chance that this Managed Instrumentation code  gets the job done. I didn’t get to test it, since

(1) It is exceptionally complicated (CoSetProxyBlanket anyone? How about some nice IWbemClassObjects to go with that?),

(2) WMI supports monitor classes only since Vista, which makes it irrelevant to most of the world (40%-50% as of Sep 2011).

4. Spelunking the Registry

Unlike what manymany, say, the physical display information is in fact available to the OS, via Extended Display Identification Data (EDID). A copy of the EDID block is kept in the registry, and bytes 21/22 of it contain the width/height of the monitor, in cm. Some have tried digging into the registry directly, searching for the EDID block, but the code in the link didn’t work for me and worked (I guess) for the poster by pure accident: the exact registry path to the EDID is not only undocumented, but does in practice vary from one vendor to another.

This is, however, a step in the right direction – which turned out to be:

5. SetupAPI !

Finally, here’s some code that works almost perfectly, courtesy of Calvin Guan. Turns out there is a documented way of obtaining the correct registry for a device:

  1. Call SetupDiGetClassDevsEx to get an HDEVINFO handle.
  2. Use this HDEVINFO in a call to SetupDiEnumDeviceInfo to populate an SP_DEVINFO_DATA struct.
  3. Use both HDEVICE and HDEVINFO in a call to SetupDiOpenDevRegKey, to finally get an HKEY to the desired registry key – the one that holds the EDID block.

Below is a (larger than usual) code snippet. Beyond some general cleanup, a few fixes were applied to Calvin’s original code:

(1) the REGSAM argument in SetupDiOpenDevRegKey is set to KEY_READ and not KEY_ALL_ACCESS to allow non-admins to run it, (2) Fix a small memory leak due to a missing SetupDiDestroyDeviceInfoList call (thanks @Anonymous!), (3) the monitor size is extracted from the EDID with millimeter precision, and not cm (thanks other @Anonymous!)

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

#include <atlstr.h>

#include <SetupApi.h>

#pragma comment(lib, "setupapi.lib")

 

#define NAME_SIZE 128

 

const GUID GUID_CLASS_MONITOR = {0x4d36e96e, 0xe325, 0x11ce, 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18};

 

// Assumes hDevRegKey is valid

bool GetMonitorSizeFromEDID(const HKEY hDevRegKey, short& WidthMm, short& HeightMm)

{

    DWORD dwType, 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, // buffer

            &edidsize); // buffer size

 

        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, //class GUID       NULL, //enumerator      NULL, //HWND        DIGCF_PRESENT, // Flags //DIGCF_ALLCLASSES|         NULL, // device info, create a new one.         NULL, // machine name, local machine        NULL);// reserved   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))                 continue;           bRes = GetMonitorSizeFromEDID(hDevRegKey, WidthMm, HeightMm);           RegCloseKey(hDevRegKey);        }   }   SetupDiDestroyDeviceInfoList(devInfo);  return bRes; } int _tmain(int argc, _TCHAR* argv[]) {   short WidthMm, HeightMm;    DISPLAY_DEVICE dd;  dd.cb = sizeof(dd);     DWORD dev = 0; // device index  int id = 1; // monitor number, as used by Display Properties > Settings

 

    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 (L"%s", ddMon.DeviceID);

                DeviceID = DeviceID.Mid (8, DeviceID.Find (L"\\", 9) - 8);

 

                bFoundDevice = GetSizeForDevID(DeviceID, WidthMm, HeightMm);

            }

            devMon++;

 

            ZeroMemory(&ddMon, sizeof(ddMon));

            ddMon.cb = sizeof(ddMon);

        }

 

        ZeroMemory(&dd, sizeof(dd));

        dd.cb = sizeof(dd);

        dev++;

    }

 

    return 0;

}

SetupAPI is still not the most pleasant of API sets around, but as MSFT’s Doron Holan replied to a user preferring to dig in the registry himself:

Programming is hard. Plain and simple. Some problems are simple, some are hard. Some APIs you like, some you don’t. Going behind the back of those APIs and getting at the data yourself will only cause problems for you and your customers.

I actually had to query the dimensions of a specific monitor (specified HMONITOR). This was an even nastier problem, and frankly I’m just not confident yet that I got it right. If I ever get to a code worth sharing – I’ll certainly share it here.

分析 EDID 的小工具 生成如下形式的说明文档: Adr Value Remark Description 00h 00h 01h FFh 02h FFh 03h FFh 04h FFh 05h FFh 06h FFh 07h 00h 08h 34h MAG EISA Manufacturer ID (high) 09h 27h EISA Manufacturer ID (low) 0Ah 17h 0717h EISA Product ID(low) 0Bh 07h EISA Product ID(high) 0Ch 02h 2 Serial Number 0Dh 00h Serial Number 0Eh 00h Serial Number 0Fh 00h Serial Number 10h 0Dh 13 Week of Manufacture 11h 0Eh 2004 Year of Manufacture 12h 01h 1 EDID Version 13h 03h 3 EDID Revision 14h 08h Video Input Definition Bit 7 : 0 = Analog signal Bit 6,5: 00 = 0.700,0.300(1.000Vpp) Bit 4 : 0 = No blank-to-black setup Bit 3 : 1 = Separate sync supported Bit 2 : 0 = No composite sync supported Bit 1 : 0 = No sync on green supported Bit 0 : 0 = No serration of the Vsync required 15h 22h 34 Max. Horizontal Image Size 16h 1Bh 27 Max. Vertical Image Size 17h 96h 2.5 Display Transfer Characteristic (Gamma) 18h 28h Feature Support (DPMS) Bit 7 : 0 = No stand-by mode Bit 6 : 0 = No suspend mode Bit 5 : 1 = Active off Bit 4,3: 01 = R/G/B color display Bit 2 : 0 = No standard default color space Bit 1 : 0 = No preferred timing mode Bit 0 : 0 = No GTF supported 19h 67h Red / Green Low Bits 1Ah 55h Blue/ White Low Bits 1Bh A5h 0.646 Red X 1Ch 5Bh 0.357 Red X 1Dh 47h 0.278 Green X 1Eh 9Ch 0.612 Green Y 1Fh 25h 0.146 Blue X 20h 1Eh 0.118 Blue Y 21h 4Fh 0.31 White X 22h 54h 0.329 White Y 23h BFh Established Timings 1 Bit 7 : 1 = 720 x 400 : 70 Hz Bit 6 : 0 Bit 5 : 1 = 640 x 480 : 60 Hz Bit 4 : 1 = 640 x 480 : 67 Hz Bit 3 : 1 = 640 x 480 : 72 Hz Bit 2 : 1 = 640 x 480 : 75 Hz Bit 1 : 1 = 800 x 600 : 56 Hz Bit 0 : 1 = 800 x 600 : 60 Hz 24h EFh Established Timings 2 Bit 7 : 1 = 800 x 600 : 72 Hz Bit 6 : 1 = 800 x 600 : 75 Hz Bit 5 : 1 = 832 x 624 : 75 Hz Bit 4 : 0 Bit 3 : 1 = 1024 x 768 : 60 Hz Bit 2 : 1 = 1024 x 768 : 70 Hz Bit 1 : 1 = 1024 x 768 : 75 Hz Bit 0 : 1 = 1280 x1024 : 75 Hz 25h 00h Manufacturers Timings 26h 31h 640 x 480 27h 40h 60 Hz 28h 45h 800 x 600 29h 40h 60 Hz 2Ah 61h 1024 x 768 2Bh 40h 60 Hz 2Ch 81h 1280 x 1024 2Dh 80h 60 Hz 2Eh 31h 640 x 480 2Fh 4Fh 75 Hz 30h 45h 800 x 600 31h 4Fh 75 Hz 32h 61h 1024 x 768 33h 4Fh 75 Hz 34h 81h 1280 x 1024 35h 8Fh 75 Hz 36h 30h 108Mhz Pixel Clock (low) 37h 2Ah Pixel Clock (high) 38h 00h 1280 Horizontal Active (low) 39h 98h 408 Horizontal Blank (low) 3Ah 51h H.Active/H.Blank (high) 3Bh 00h 1024 Vertikal Active (low) 3Ch 2Ah 42 Vertikal Blank (low) 3Dh 40h V.Active/V.Blank (high) 3Eh 30h 48 H.Front Porch (low) 3Fh 70h 112 H.Sync Pulse Width (low) 40h 13h 1/3 V.Front Porch/V.Sync PW (low) 41h 00h H/V Sync (high) 42h 51h 337 H.Image Size (low) 43h 0Eh 270 V.Image Size (low) 44h 11h H/V Smage Size (high) 45h 00h 0 H.Border 46h 00h 0 H.Border 47h 1Eh Flags 48h 00h Flag = 0000h 49h 00h 4Ah 00h Reserved = 00h 4Bh FFh Tag=Monitor S/N String 4Ch 00h Flag = 00h 4Dh 31h '1' 4Eh 0Ah 4Fh 20h 50h 20h 51h 20h 52h 20h 53h 20h 54h 20h 55h 20h 56h 20h 57h 20h 58h 20h 59h 20h 5Ah 00h Flag = 0000h 5Bh 00h 5Ch 00h Reserved = 00h 5Dh FCh Monitor Name 5Eh 00h Flag = 00h 5Fh 50h 'P' 60h 53h 'S' 61h 2Dh '-' 62h 37h '7' 63h 37h '7' 64h 36h '6' 65h 49h 'I' 66h 0Ah 67h 20h 68h 20h 69h 20h 6Ah 20h 6Bh 20h 6Ch 00h Flag = 0000h 6Dh 00h 6Eh 00h Reserved = 00h 6Fh FDh Tag=Monitor Range Limits 70h 00h Flag = 00h 71h 3Ch 60 Min. vert. Frequency [Hz] 72h 4Bh 75 Max. vert. Frequency [Hz] 73h 1Eh 30 Min. hor. Frequency [kHz] 74h 50h 80 Max. hor. Frequency [kHz] 75h 0Eh 140 Max. Pixel Frequency [MHz/10] 76h 00h Reserved for VESA GTF (=00) 77h 0Ah Set to 0Ah 78h 20h 79h 20h 7Ah 20h 7Bh 20h 7Ch 20h 7Dh 20h 7Eh 00h EDID Ext. Flag 7Fh 15h Checksum
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值