Once upon a time I had an impossible to ignore urge to read EDIDs of all connected displays on Windows. In bare C and WinAPI only.
Expectation: There's a function like GetEdidForMonitor(HMONITOR)
Reality: One does not simply. There are no officially documented ways to do this, no fully working code examples, and no, LLMs couldn't conjure one up either (although they did provide valuable research pointers).
- Call
EnumDisplayMonitors()
(масло масляное) to receive a bunch ofHMONITOR
handles for each connected display. GetMonitorInfo()
to getMONITORINFOEX
.szDevice
will contain display device name like\\.\DISPLAY14
.- Use
EnumDisplayDevices(szDevice, EDD_GET_DEVICE_INTERFACE_NAME)
to getDISPLAY_DEVICE.DeviceID
containing device path like\\?\DISPLAY#LKG001A#5&272c5422&0&UID513#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
- Use
SetupDiGetClassDevs(GUID_DEVINTERFACE_MONITOR, DIGCF_DEVICEINTERFACE)
to request a set of all monitor-class devices interfaces. - Enumerate the set using
SetupDiEnumDeviceInterfaces(set, GUID_DEVINTERFACE_MONITOR, index)
, gettingSP_DEVICE_INTERFACE_DATA
- For each interface retrieve
SP_INTERFACE_DEVICE_DETAIL_DATA
usingSetupDiGetDeviceInterfaceDetail(set, iface_data)
. It's a three-step process. First, get the detail struct size, then allocate it, then call the function again to fill the detail data. Also, use this function to readSP_DEVINFO_DATA
. - Compare
detail.DevicePath
with the device pathDISPLAY_DEVICE.DeviceID
from step 3. Note that they will have different case, so case-insensitive comparison is needed. If they match, then you've found associated interface for theHMONITOR
from step 1. - Use
SetupDiOpenDevRegKey(set, devinfo_data)
to open registry key. (Yes, the regedit-registry. No, it's not practical to guess the key, it's something likeHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\DISPLAY\CMN152A\5&272c5422&0&UID512\Device Parameters
and seems unstable) - Use
RegQueryValueEx(key, "EDID")
to read EDID value data into buffer of 128 bytes. No, it won't read past 128 bytes to get extensions, e.g. for CEC. - If nothing failed, you're done. Time to cleanup all this mess. Maybe the real treasure was all the handles we created and memory we allocated along the way.
The example code also reads monitor positions and current modes, so that there's association between EDID and virtual display geometry.
No, I haven't found an easy read monitor names (e.g. vendor, model). I haven't really looked, though.
This repo contains some cmake configuration to manifest this app into a DPI-aware world. Without this, the bonus one code would read dpi-unaware (scaled) monitor positions, which might be not what you want.
How to read EDID in X11:
XRRGetScreenResources()
to get resources.- For each
resources->output[i]
: useXRRGetOutputProperty(output, XInternAtom("EDID"))
, done.