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).

Описанное поведение наблюдается только тогда, когда я устанавливаю осведомленность о точном разрешении на 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;
}

person Alexander Zinovyev    schedule 22.01.2019    source источник
comment
Многое будет странно, если вы не перезагрузитесь (или, по крайней мере, не выйдете из системы) после изменения DPI. Что есть, то есть.   -  person Jonathan Potter    schedule 23.01.2019
comment
Я установил распознавание DPI с помощью SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE); и SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);, оба не могут воспроизвести ваши проблемы. Значение HORZRES всегда уменьшается при увеличении масштабного коэффициента. (100% -1920; 125% -1536; 150% -1280; 175% -1097) Я не перезагружаюсь.   -  person Rita Han    schedule 23.01.2019
comment
Я тестирую на рабочем столе Windows 10 Enterprise.   -  person Rita Han    schedule 23.01.2019
comment
@ RitaHan-MSFT, спасибо за ваш вклад. Да, я могу увидеть то же поведение, если увеличу коэффициент масштабирования со 100% по умолчанию до, например, 150%. В этом случае я вижу, что HORZRES становится меньше (как я и ожидал). Моя проблема возникает, когда я увеличиваю коэффициент масштабирования до 150% и выключаю и выключаю свой компьютер.   -  person Alexander Zinovyev    schedule 23.01.2019


Ответы (1)


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

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

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

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

person Rita Han    schedule 25.01.2019