VERTRES и HORZRES зависят от настроек масштабирования при загрузке системы

Недавно я играл с WINAPI, чтобы получить настройки масштабирования и разрешения экрана, и я столкнулся с этим странным поведением.

У меня есть небольшая программа на C++ (список доступен ниже), которая получает значения VERTRES, HORZRES, DESKTOPVERTRES и DESKTOPHORZRES. Моя программа также устанавливает режим осведомленности DPI на 1 (осведомленность на уровне системы). Я заметил, что значения, сообщаемые VERTRES, HORZRES, зависят от коэффициента масштабирования системы, используемого при запуске системы.

Поэтому на моем ноутбуке я настроил коэффициент масштабирования системы до 150% и разрешение 1920x1080. Это конфигурация при запуске. Когда я получаю HORZRES и DESKTOPHORZRES, оба значения отображаются как 1920.

Если я изменяю настройку масштабирования на 100% и не перезагружаю свой компьютер, в следующий раз, когда я получу эти значения, они будут указаны как 2880 (для HORZRES) и 1920 (для DESKTOPHORZRES).

После перезагрузки компьютера с настройками масштабирования, равными 100%, оба значения снова отображаются в 1920 году.

Если я снова изменю масштабирование на 150%, сообщается о значениях 1280 (HORZRES) и 1920 (DESKTOPHORZRES).

Описанное поведение наблюдается только тогда, когда я устанавливаю осведомленность DPI на 1, если для него установлено значение 0 ("не осведомлено") или 2 ("на экранную осведомленность"), значения всегда сообщаются как 1280 (HORZRES) и 1920 (DESKTOPHORZRES) независимо от того, как масштабирование было настроено при запуске.

Мне было интересно, почему значение, сообщаемое HORZRES (или VERTRES), зависит от коэффициента масштабирования, используемого при запуске системы? Это ожидаемое поведение?

Если выше уже было где-то описано, буду признателен за любые ссылки.

#include "stdafx.h"
#include <iostream>
#include <vector>
#include <cerrno>
#include <string>
#include <sstream>
#include <math.h>
#include <Windows.h>
#include <shellscalingapi.h>
#include <winuser.h>

class Rectangular
{
public:
    Rectangular(int height, int width) : height(height), width(width) {};
    const int height;
    const int width;
};

class MonitorInfo
{
public:
    MonitorInfo(std::string device_name, Rectangular logical_resolution, Rectangular physical_resolution, Rectangular physical_size_mm) :
        device_name(device_name), logical_resolution(logical_resolution), physical_resolution(physical_resolution),
        physical_size_mm(physical_size_mm), scaling(static_cast<int>(std::round(100.0*(float)physical_resolution.height / (float)logical_resolution.height))) {};

    void print_to_stdout() const;
    const std::string device_name;
    const Rectangular logical_resolution;
    const Rectangular physical_resolution;
    const Rectangular physical_size_mm;
    const int scaling;
};


class MonitorsInformation
{
public:
    MonitorsInformation();
    const std::vector<MonitorInfo>& get_monitors_info() const { return monitors; }
    std::string get_last_error_string() const { return last_error; }
    Rectangular get_monitors_rectangular() const;
private:
    RECT rectangular_combined;
    std::vector<MonitorInfo> monitors;
    std::string  last_error;
    static BOOL CALLBACK MonitorEnum(HMONITOR hMon, HDC hdc, LPRECT lprcMonitor, LPARAM pData);
};

void MonitorInfo::print_to_stdout() const
{
    std::cout << "\nDevice: " << device_name << "\nLogical Screen resolution: " << logical_resolution.width << "x" << logical_resolution.height;
    std::cout << "\nPhysical Screen resolution: " << physical_resolution.width << "x" << physical_resolution.height;
    std::cout << "\nDPI ratio: " << scaling;
    std::cout << "\nPhysical Size(mm): " << physical_size_mm.width << "x" << physical_size_mm.height << "\n";
}


MonitorsInformation::MonitorsInformation() : rectangular_combined(RECT()), last_error(std::string()), monitors(std::vector<MonitorInfo>())
{
    EnumDisplayMonitors(0, 0, MonitorEnum, (LPARAM)this);
}

BOOL CALLBACK MonitorsInformation::MonitorEnum(HMONITOR hMon, HDC hdc, LPRECT lprcMonitor, LPARAM pData)
{
    MonitorsInformation* pThis = reinterpret_cast<MonitorsInformation*>(pData);

    MONITORINFOEXA monitor_info;
    monitor_info.cbSize = sizeof(MONITORINFOEXA);

    if (!GetMonitorInfoA(hMon, &monitor_info))
    {
        pThis->last_error = "GetMonitorInfoA failed with error: " + errno;
        return FALSE;
    }

    UnionRect(&pThis->rectangular_combined, &pThis->rectangular_combined, lprcMonitor);

    HDC device_context = CreateDCA(NULL, monitor_info.szDevice, NULL, NULL);

    int LogicalScreenHeight = GetDeviceCaps(device_context, VERTRES);
    int LogicalScreenWidth = GetDeviceCaps(device_context, HORZRES);
    int PhysicalScreenHeight = GetDeviceCaps(device_context, DESKTOPVERTRES);
    int PhysicalScreenWidth = GetDeviceCaps(device_context, DESKTOPHORZRES);

    pThis->monitors.push_back(
        MonitorInfo(
            std::string(monitor_info.szDevice),
            Rectangular(
                LogicalScreenHeight,
                LogicalScreenWidth),
            Rectangular(
                PhysicalScreenHeight,
                PhysicalScreenWidth),
            Rectangular(
                GetDeviceCaps(device_context, VERTSIZE),
                GetDeviceCaps(device_context, HORZSIZE))));

    return TRUE;
}

Rectangular MonitorsInformation::get_monitors_rectangular() const
{
    return Rectangular(
        std::abs(rectangular_combined.top) + std::abs(rectangular_combined.bottom),
        std::abs(rectangular_combined.left) + std::abs(rectangular_combined.right));
}


int main()
{
    SetProcessDPIAware();

    for (;;)
    {
        MonitorsInformation MonitorsInfo;
        char exit_char = 'N';
        std::cout << "You have " << MonitorsInfo.get_monitors_info().size() << " monitors connected.\n";
        printf("Screen rectangular. %d x %d\n",
            MonitorsInfo.get_monitors_rectangular().width, MonitorsInfo.get_monitors_rectangular().height);

        for (auto &monitor : MonitorsInfo.get_monitors_info())
        {
            monitor.print_to_stdout();
        }
        std::cout << "Would you like to repeat? [Y]/[N]\n";
        std::cin >> exit_char;
        if (exit_char == 'N' || exit_char == 'n')
        {
            break;
        }
    }

    return 0;
}

1 ответ

@AlexanderZinovyev Спасибо за дальнейшие разъяснения, и я могу воспроизвести вашу проблему с сохранением коэффициента масштабирования до 150% и выключением и включением питания моего компьютера.

После проверки документа API GetDeviceCaps я нашел следующее примечание:

Примечание. GetDeviceCaps сообщает информацию, предоставляемую драйвером дисплея. Если драйвер дисплея отказывается сообщать какую-либо информацию, GetDeviceCaps вычисляет информацию на основе фиксированных вычислений. Если драйвер дисплея сообщает о неверной информации, GetDeviceCaps возвращает неверную информацию. Кроме того, если драйвер дисплея отказывается сообщать информацию, GetDeviceCaps может вычислять неверную информацию, поскольку он принимает либо фиксированный DPI (96 DPI), либо фиксированный размер (в зависимости от информации, которую драйвер дисплея сделал и не предоставил). К сожалению, драйвер дисплея, который реализован в модели драйвера дисплея Windows (WDDM) (представлен в Windows Vista), заставляет GDI не получать информацию, поэтому GetDeviceCaps всегда должен вычислять информацию.

Таким образом, похоже, что возвращаемое значение API GetDeviceCaps может не отражать реальную стоимость устройства.

Другие вопросы по тегам