Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to query NATIVE hardware resolution of primary monitor in Windows?

I need to find the "best" or native resolution for an attached LCD monitor under Windows (which I'll then set programmatically and know how to do.) Let me repeat that I do not need the Current Windows resolution, nor need to worry about CRTs/projectors.

I've seen it work with this program, so I know it is possible despite the naysayers: http://www.entechtaiwan.com/util/moninfo.shtm

It would be best to talk directly to the monitor and query the EDID info. However, I've seen that it is cached in the registry and wouldn't have a problem digging it out from HKLM\SYSTEM\CurrentControlSet\Enum\DISPLAY but can't figure out how to match the data with the current primary monitor.

I did find this C program at: http://www.tech-archive.net/Archive/Development/microsoft.public.development.device.drivers/2004-08/0294.html and a similar python program: http://www.koders.com/python/fid7FCCE3C908F376DC62F06CAD9B11C6D7C1CFA78F.aspx

Unfortunately, I'm having a lot of trouble converting the C program to python, as the relevant code doesn't seem to be in the win32all modules. I'd try to compile it but don't have disk space for a big compiler and haven't used C for years. I'm a bit out of my element with ctypes as well.

My plan B will be to use EnumDisplaySettings() to find the largest value for resolution and set it to that. On the PC's I've tried it gives the correct res, but it could still be problematic.

I'd prefer a solution in python, but perhaps someone could help me modify the C program to spit out the resolution and compile it. Thanks in advance.

Update:

I've found a potential solution. I am now reading WMI to find a monitor that is available (not offline), grabbing its PNP device id, and reading EDID from the registry in the subkey with the id value. Then I parse the data for bytes 38 and 39 and calculate. Not very clean, but am getting results. If this is a reasonable way to do it I'll close this question, thanks.

like image 431
Gringo Suave Avatar asked Dec 01 '10 07:12

Gringo Suave


1 Answers

Decided to give up talking directly to the monitor and instead parsing EDID information cached in the registry. This code isn't perfect, but it works:

import win32api as api, win32con as con, pywintypes
import win32com.client
_objWMIService = win32com.client.Dispatch('WbemScripting.SWbemLocator')
_objSWbemServices = _objWMIService.ConnectServer('.', 'root\\cimv2')
wmiquery = _objSWbemServices.ExecQuery

# get_regval(regkey) is simple registry reading function.
def get_monitor_res():
    dtd = 54  # start byte of detailed timing desc.

    try:  # get PNP id to find EDID in registry
        for monitor in wmiquery('Select * from Win32_DesktopMonitor'):
            # http://msdn.microsoft.com/en-us/library/aa394122%28VS.85%29.aspx
            if monitor.Availability in (3, 7, 13, 14, 15, 16): # connected
                curres = (monitor.ScreenWidth, monitor.ScreenHeight)
                print 'DEBUG: Current monitor resolution from WMI: %s' % (curres,)
                regkey = ('HKLM\\SYSTEM\\CurrentControlSet\\Enum\\' +
                    monitor.PNPDeviceID + '\\Device Parameters\\EDID')
                edid = get_regval(regkey)
                if edid:
                    print 'DEBUG: EDID Version: %s.%s' % (edid[18], edid[19])
                    # upper nibble of byte x 2^8 combined with full byte
                    hres = ((edid[dtd+4] >> 4) << 8) | edid[dtd+2]
                    vres = ((edid[dtd+7] >> 4) << 8) | edid[dtd+5]
                    print 'DEBUG: EDID DTD0: ' + str((hres, vres))
                    res = (hres, vres)
                    break  # give up on first success
                else:
                    raise RuntimeError, 'EDID not found in registry'
    except (RuntimeError, Exception) as err:
        print 'ERROR: %s.' % err

    return res
like image 145
Gringo Suave Avatar answered Sep 20 '22 13:09

Gringo Suave