I've been recently playing with WINAPI to retrieve scaling setting and screen resolution and I came across this strange behaviour.
I've got small C++ program (listing available below) that retrieves values of VERTRES, HORZRES, DESKTOPVERTRES and DESKTOPHORZRES. My program also sets DPI awareness mode to 1 (system level awareness). I noticed that values reported by VERTRES, HORZRES depends on the system scaling factor used on system start up.
So on my laptop I have configured system scaling factor to 150% and resolution as 1920x1080. That is the configuration on start up. When I retrieve HORZRES and DESKTOPHORZRES both values are reported as 1920.
If I change scaling setting to 100% and don't reboot my computer, next time when I retrieve these values they are reported as 2880 (for HORZRES) and 1920 (for DESKTOPHORZRES).
After I reboot my computer with scaling settings set to 100% both values are reported at 1920 again.
If I change scaling to 150% again, values are reported as 1280 (HORZRES) and 1920 (DESKTOPHORZRES).
The described behaviour only observed when I'm setting DPI awareness to 1, if it is set to 0 ("not aware") or 2 ("per screen awareness") values are always reported as 1280 (HORZRES) and 1920(DESKTOPHORZRES) regardless of how scaling was configured on start up.
I was wondering why does the value reported by HORZRES (or VERTRES) depends on a scaling factor used on system start up? Is that expected behaviour?
If above has already been described somewhere, l'd appreciate any references.
#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;
}