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 many, many, 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

#include

#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.

你可能感兴趣的:(WTL,gdi+,WTL,gdi)