dgrandm
dgrandm

Reputation: 383

Is GetScaleFactorForMonitor winapi returning incorrect scaling factor?

How do I get correct DPI scaling factor? What I need is the percentage shown in the screenshot, in this case it is 200%. The code below gives SCALE_180_PERCENT. I would expect the code below should return SCALE_200_PERCENT. The system is Windows 10, Surface 2, display resolution 3000x2000, custom scaling is off, fix scaling for apps by Windows is on.

enter image description here

#include <iostream>
#include <Windows.h>
#include <shellscalingapi.h>

#pragma comment(lib, "Shcore.lib")

int main()
{

    HWND hWnd = GetDesktopWindow();
    HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
    DEVICE_SCALE_FACTOR devScaleFactor;
    HRESULT res = GetScaleFactorForMonitor(hMonitor, &devScaleFactor);
    std::cout << "Device scale factor: " << devScaleFactor << std::endl;

    UINT dpi = GetDpiForWindow(hWnd);
    std::cout << "GetDpiForWindow DPI: " << dpi << std::endl;

    return 0;
}

Upvotes: 5

Views: 3944

Answers (2)

jojo
jojo

Reputation: 31

It turned out that this depends on the dpi awareness of the calling process. GetScaleFactorForMonitor() returns the correct result if the calling application has set its dpi awareness to [DPI_AWARENESS_UNAWARE][1].

If any of the others (DPI_AWARENESS_SYSTEM_AWARE, DPI_AWARENESS_PER_MONITOR_AWARE) is set, then the results are incorrect.

The other problem is, that GetDpiForMonitor or GetDpiForWindow work only correct if the calling process has DPI_AWARENESS_PER_MONITOR_AWARE. See: https://learn.microsoft.com/en-us/windows/win32/api/shellscalingapi/nf-shellscalingapi-getdpiformonitor

If you define your program for running on Windows 10 or higher you can determine the awareness programmatically.

#if WINVER >= 0x0A00
    DPI_AWARENESS_CONTEXT oldContext = GetThreadDpiAwarenessContext();
    DPI_AWARENESS oldDpiAwareness = GetAwarenessFromDpiAwarenessContext(oldContext);
#endif 

If not, there seems to be no way to get the correct DPI setting (or scaling factor) for the monitor you are running on.

Upvotes: 2

Ryan
Ryan

Reputation: 126

This post is pretty old but I'm encountering this issue and figued I'd share my workaround anyways.

POINT temp = { 0,0 };
HMONITOR primaryHandle = MonitorFromPoint(temp, MONITOR_DEFAULTTOPRIMARY);
UINT dpiX, dpiY;
HRESULT temp2 = GetDpiForMonitor(primaryHandle, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
double scalingFactor = dpiY / 96.0;

The above should yield scalingFactor equal to the % you have your scale set to (200% in the question asked). The GetDpiForMonitor function gives a x and y DPI, tested quickly they were the same so I just used dpiY for my final answer, however I don't believe this is always guaranteed to be the case and you may want to find them separately. The DPI of a monitor at 100% scaling is 96 which is why we are dividing the resulting new DPI by 96 in order to get the scale factor, it needs to be 96.0 because otherwise 144/96 or whatever is just going to truncate to give you 1 (you could also just multiply them both by a factor of 100 before doing the division if you'd prefer to work with integers).

I've also used a different method to find the monitor's Handle, this specific example gives the handle to the primary window.

As for the actual issue encountered (GetScaleFactorForMonitor returning the wrong scale factor), the following two posts contain a lot more information. Stack Overflow post for similar question but in c# Blog post touching on the return values you're receiving.

The TL;DR of the two links is that applications from the windows store have scaling values of 100, 140, and 180, so the best guess is that function calls to GetScaleFactorForMonitor are actually pulling from something related to that instead of the proper scaling based on the scale and layout option selected in the display settings. This seems reasonable because while I was fiddling with my scaling settings, those were the only three values that were being given to me by GetScaleFactorForMonitor.

Upvotes: 3

Related Questions