Я разрабатываю приложение winapi с поддержкой dpi и столкнулся с проблемой, что его окно масштабируется дважды без какой-либо причины.Похоже, что масштабирование точек на дюйм применяется дважды после создания окна приложения.Вот мой кодСначала я делаю свое приложение с поддержкой dpi:
::SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
Затем я рассчитываю шкалу dpi моего монитора и создаю окно 500 на 500 с учетом этой шкалы:
dpiScale = <get dpi scale using ::GetDpiForMonitor()> / 96.f;
hWnd = ::CreateWindow(..., 0, 0, 500 * dpiScale, 500 * dpiScale, ...);
Я также хочу, чтобы мойминимальный размер окна 500 на 500:
case WM_GETMINMAXINFO:
{
MINMAXINFO* info = (MINMAXINFO*)lParam;
info->ptMinTrackSize.x = 500 * dpiScale;
info->ptMinTrackSize.y = 500 * dpiScale;
break;
}
Затем я реализую обработку событий, измененную в dpi:
case WM_DPICHANGED:
{
dpiScale = LOWORD(wParam) / 96.f;
RECT rect = *(RECT *)lParam;
::SetWindowPos(... rect ...);
break;
}
Когда я запускаю свое приложение на мониторе с высоким dpi, например 120 (масштабный коэффициент х1.25) окно приложения выглядит больше, чем должно быть.Я также попытался распечатать размер окна для каждого WM_SIZE
сообщения:
case WM_SIZE:
{
RECT r;
::GetWindowRect(hWnd, &r);
int width = (r.right - r.left);
int height = (r.bottom - r.top);
std::cout << "WM_SIZE " << width << " " << height << " scale factor: " << (width / 500.f) << std::endl;
break;
}
и вот что я получаю:
WM_SIZE 625 625 scale factor: 1.25
WM_SIZE 781 781 scale factor: 1.562
Обратите внимание, что есть только два места, где я применяю масштабирование dpiвручную - создание окон и обработка сообщений WM_GETMINMAXINFO
.Я также попытался удалить масштабирование dpi из вызова ::CreateWindow()
, и это ничего не изменило.
Похоже, что Windows автоматически изменяет размер моего окна, хотя я установил осведомленность о dpi для своего приложения.Почему это происходит?Как я могу обойти это?Спасибо.
ОБНОВЛЕНО:
Я создал минимальный воспроизводимый пример, который можно найти здесь: https://gist.github.com/APodlinny/179b90b9e0d45ea9576d33dca2273dce Вы можете использовать файлы проекта MSVC.из gist или создайте пустой проект Win32 MSVC, добавьте исходный файл main.cpp
и убедитесь, что: * используется Windows SDK 8.1 или новее (проверьте каталоги VC в настройках проекта, а именно каталоги include и library) * shcore.lib
библиотека находится вДополнительные зависимости компоновщика * Подсистема компоновщика Console
Может быть, в Windows 10 включено масштабирование с высоким разрешением для приложения?
Я сомневаюсь, что это потому, что я явно установил осведомленность о dpiдля моего приложения в начале функции _tWinMain(...)
.Параметр «Переопределить масштабирование точек на дюйм» в настройках совместимости приложения также не отмечен.Кроме того, само содержимое окна также не масштабируется.
Есть ли разница в масштабировании между основным монитором и тем, на котором работает ваше приложение?
Да, но я не думаю, что это имеет значение.У меня есть два монитора, и я протестировал свое приложение в двух конфигурациях: 1. Вторичный 125%, первичный 100%, окно создано на вторичном мониторе.2. Вторичный 125%, первичный 125%, окно создано на основном мониторе.В обоих случаях окно масштабируется дважды.
Ошибка в другом коде.Установите точку останова на ::GetWindowRect(hWnd, &r);
в обработчике WM_SIZE
.Запустите отладчик, когда он остановится на точке останова, и снова запустите его.Когда он останавливается во второй раз, проверьте стек вызовов, чтобы найти источник второго WM_SIZE
.
Спасибо за подсказку!Оказалось, что второе сообщение WM_SIZE
вызвано сообщением WM_WINDOWPOSCHANGED
, отправленным Windows:
case WM_WINDOWPOSCHANGED:
{
WINDOWPOS* pos = (WINDOWPOS*)lParam;
std::cout << "WM_WINDOWPOSCHANGED " << pos->x << " " << pos->y << " " << pos->cx << " " << pos->cy << std::endl;
return DefWindowProc(hWnd, message, wParam, lParam);
}
WM_WINDOWPOSCHANGED 100 100 625 625
WM_SIZE 625 625 scale factor: 1.25
WM_WINDOWPOSCHANGED 100 100 781 781
WM_SIZE 781 781 scale factor: 1.562
Первая пара WM_WINDOWPOSCHANGED
/ WM_SIZE
вызвана ::ShowWindow()
вызов, второй можно отследить до ::GetMessage()
, но я не знаю, что вызывает отправку этих сообщений.