3

I need to get the real screen resolution, that is the resolution that actual output by the gpu, for each of the multiple screen, without affecting by dpi settings. I tried many answers from SO, but none of them seems to handle it correctly.

Some of the answers I tried:

-GetDpiForMonitor() also does not return the correct dpi. And the dpiX and dpiY it returns does not even match.

Code:

static int result = []() {SetProcessDpiAwareness(PROCESS_DPI_UNAWARE); return 1; }(); //nope

struct MonitorInfo
{
    MONITORINFO m_moniforInfo;
    UINT dpiX{};
    UINT dpiY{};
};

static std::vector<MonitorInfo> monitors;

BOOL EnumProc(HMONITOR monitor, HDC _, LPRECT __, LPARAM ___)
{
    MONITORINFO monitorInfo{ sizeof(MONITORINFO) };
    GetMonitorInfo(monitor, &monitorInfo);
    UINT dpix;
    UINT dpiy;
    GetDpiForMonitor(monitor, MDT_RAW_DPI, &dpix, &dpiy);
    monitors.push_back(MonitorInfo{ monitorInfo, dpix, dpiy});
    return true;
}

int main()
{
    SetProcessDpiAwareness(PROCESS_DPI_UNAWARE); //nope
    EnumDisplayMonitors(nullptr, nullptr, EnumProc, 0);
    for (auto const& monitor : monitors)
    {
        auto width = monitor.m_moniforInfo.rcMonitor.right - monitor.m_moniforInfo.rcMonitor.left;
        auto height = monitor.m_moniforInfo.rcMonitor.bottom - monitor.m_moniforInfo.rcMonitor.top;
        std::cout << width << " x " << height << '\t' << "dpix: " << monitor.dpiX << '\t' << monitor.dpiY << '\n';
    }
}

Output, comments in (parens)

2560 x 1440     dpix: 108       109  (this is 3840*2160 with 150% scale)
2560 x 1440     dpix: 109       109  (this is 2560*2160 with 100% scale)
1920 x 1080     dpix: 163       106  (this is 3840*2160 with 200% scale)
1080 x 1920     dpix: 90        89   (this is 1080*1920 with 100% scale, shouldn't it be 96d?)
1920 x 1080     dpix: 163       106 (this is 3840*2160 with 200% scale too)
sz ppeter
  • 1,698
  • 1
  • 9
  • 21
  • 1
    Make sure the manifest of your app is properly setup otherwise your calls may not return reality, this is by design. https://learn.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows – Simon Mourier Feb 03 '22 at 19:04
  • @SimonMourier It's a C++ console application. With this https://learn.microsoft.com/en-us/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process#setting-default-awareness-programmatically I tried both setting api-awareness in first line of `main` or setting it in global scope with some tricks. None works – sz ppeter Feb 03 '22 at 19:18
  • 1
    The code you've shown doesn't. It's practically impossible to know what's going on if you aren't sharing a [mcve]. – IInspectable Feb 03 '22 at 19:33
  • @IInspectable Okay I added those two tries – sz ppeter Feb 03 '22 at 19:54
  • 1
    `PROCESS_DPI_UNAWARE` is exactly the wrong flag to use, as it means the system will lie to you. `PROCESS_PER_MONITOR_DPI_AWARE` will give you the true dimensions of all your monitors. – Jonathan Potter Feb 03 '22 at 20:33
  • 3
    Literally the first comment to this question links to documentation that explains in excruciating detail how DPI-awareness works and what the different DPI-awareness modes mean. It remains a mystery to anyone why you decided to announce to the system that your application is DPI-unaware and then go ahead and ask why the system treats your application as DPI-unaware. – IInspectable Feb 03 '22 at 20:55

1 Answers1

2

The following answer was tested with SetProcessDPIAware() and without. In all cases, it gave the correct values.

I did not found it anywhere on the internet so I am glad to share my knowledge with the world.

To get real monitor resolution

void GetMonitorRealResolution(HMONITOR monitor, int* pixelsWidth, int* pixelsHeight)
{
    MONITORINFOEX info = { sizeof(MONITORINFOEX) };
    winrt::check_bool(GetMonitorInfo(monitor, &info));
    DEVMODE devmode = {};
    devmode.dmSize = sizeof(DEVMODE);
    winrt::check_bool(EnumDisplaySettings(info.szDevice, ENUM_CURRENT_SETTINGS, &devmode));
    *pixelsWidth = devmode.dmPelsWidth;
    *pixelsHeight = devmode.dmPelsHeight;
}

It will return that native resolution in any case, even if the OS tries to lie to you due to the DPI awareness of the process.

To get the scaling ratio between the virtual resolution and real resolution

float GetMonitorScalingRatio(HMONITOR monitor)
{
    MONITORINFOEX info = { sizeof(MONITORINFOEX) };
    winrt::check_bool(GetMonitorInfo(monitor, &info));
    DEVMODE devmode = {};
    devmode.dmSize = sizeof(DEVMODE);
    winrt::check_bool(EnumDisplaySettings(info.szDevice, ENUM_CURRENT_SETTINGS, &devmode));
    return (info.rcMonitor.right - info.rcMonitor.left) / static_cast<float>(devmode.dmPelsWidth);
}

This will give you a ratio of the real resolution relative to the virtual resolution of the given monitor.

If the main DPI of the main monitor is 225% and on the second monitor it is 100%, and you run this function for the second monitor, you will get 2.25. because 2.25 * real resolution = the virtual resolution of the monitor.

If the second monitor has 125% scaling (while the main monitor is still 225% scaling), then this function will return you 1.79999995 because 125% relative to 225% is this value (225/125 = 1.8), and again - 1.8 * real resolution=the virtual resolution of 125%`

To get the real DPI value (not relative to anything)

Given that monitor, A has 225% DPI, and monitor B has 125% DPI, as I said above, you will not get 1.25 for the second monitor (if you run the function on the second monitor. You will get 1.8 as I said).

To overcome this, use this function:

float GetRealDpiForMonitor(HMONITOR monitor)
{
    return GetDpiForSystem() / 96.0 / GetMonitorScalingRatio(monitor);
}

This function depends on the previous function that I wrote above (the function GetMonitorScalingRatio that you need to copy)

This will give you the correct value.

gil123
  • 512
  • 6
  • 12
  • Brilliant! This really works! EnumDisplaySettings does provide the native resolution and this is the only way that I was able to get the correct display scaling (GetScaleFactorForMonitor gives it wrong). – lariona May 20 '23 at 16:26