pythonwindowswinapiscalescaling

Query Windows 10/8 Monitor Scaling from Python Script?


I have written an application in python 3.6 and would like to run a command to see what the current scaling for a monitor is in Windows 10 or 8 - something like how the following returns screen resolution:

user32 = ctypes.windll.user32
screensize_l = user32.GetSystemMetrics(0)
screensize_w = user32.GetSystemMetrics(1)

I understand that the easiest way to do this may be to make my application DPI aware, but doing so causes lots of additional problems in my application - so I would like to avoid this option.

I have looked in the windows documentation and thought "GetDpiForMonitor" or "GetScaleFactorForMonitor" may be what I am looking for, but don't know how to implement these commands.

I already use both win32api and ctypes so anything relying on either of these would be fine - any help would be greatly appreciated!


Solution

  • On Windows 10, the following code (you need pywin32):

    import ctypes
    import win32api
    
    PROCESS_PER_MONITOR_DPI_AWARE = 2
    MDT_EFFECTIVE_DPI = 0
    
    def print_dpi():
        shcore = ctypes.windll.shcore
        monitors = win32api.EnumDisplayMonitors()
        hresult = shcore.SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE)
        assert hresult == 0
        dpiX = ctypes.c_uint()
        dpiY = ctypes.c_uint()
        for i, monitor in enumerate(monitors):
            shcore.GetDpiForMonitor(
                monitor[0].handle,
                MDT_EFFECTIVE_DPI,
                ctypes.byref(dpiX),
                ctypes.byref(dpiY)
            )
            print(
                f"Monitor {i} (hmonitor: {monitor[0]}) = dpiX: {dpiX.value}, dpiY: {dpiY.value}"
            )
    
    
    if __name__ == "__main__":
        print_dpi()
    

    gives the following output on my machine with 3 monitors and 1 scaled to 125%:

    Monitor 0 (hmonitor: <PyHANDLE:65539>) = dpiX: 96, dpiY: 96
    Monitor 1 (hmonitor: <PyHANDLE:65537>) = dpiX: 120, dpiY: 120
    Monitor 2 (hmonitor: <PyHANDLE:65541>) = dpiX: 96, dpiY: 96
    

    If you do not want to set your own application as a dpi-aware per monitor, you could try launching some variant of the above code in another process using the multiprocessing module and retrieve the results.

    hope this helps.