Я пытаюсь сделать так, чтобы наше приложение WinAPI поддерживало DPI, и наткнулся на бесконечный цикл, когда окно постоянно получает WM_DPICHANGED. Программа всегда вызывает SetWindowPos с lParam сообщения WM_DPICHANGED, как сказано в документации.
У моего монитора есть монитор 1 справа, 1920x1080, 100% масштабирование и монитор 2 слева, выровненный снизу, 3840x2160, 150% масштабирование. Запустив следующую программу, переместите окно так, чтобы оно располагалось между двумя мониторами, но масштабировалось на 100%. Теперь возьмите окно на левом мониторе и переместите окно вверх и вниз по вертикали, чтобы оно переключалось между двумя разрешениями. В какой-то момент он входит в цикл, который, кажется, останавливает весь оконный менеджер (Ctrl-Alt-Del прервет его - нет необходимости фактически завершать программу).
В этом цикле прямоугольник, переданный в lParam, имеет размер 500x500 для 96 и 144 DPI! Почему WM_DPICHANGED получает прямоугольник, размер которого не соответствует размеру WM_GETDPISCALEDSIZE?
Я знаю, что делаю что-то необычное, уменьшая окно на мониторе с более высоким DPI (в реальном приложении наши окна имеют сложные макеты, которые меняются в зависимости от разрешения).
Почему этот цикл и как мы можем избежать цикла изменения DPI?
#include "stdafx.h"
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_GETDPISCALEDSIZE:
{
LPSIZE lpSize = (LPSIZE)lParam;
int dpi = wParam;
lpSize->cy = lpSize->cx = (dpi == 96 ? 200 : 500);
return TRUE;
}
case WM_DPICHANGED:
{
LPRECT lpRect = (LPRECT)lParam;
SetWindowPos(hWnd, nullptr, lpRect->left, lpRect->top, lpRect->right - lpRect->left, lpRect->bottom - lpRect->top, SWP_NOZORDER | SWP_NOACTIVATE);
return 0;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
WCHAR szTitle[] = L"Test"; // The title bar text
WCHAR szWindowClass[] = L"TESTCLASS"; // the main window class name
ATOM MyRegisterClass(HINSTANCE);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
MyRegisterClass(hInstance);
SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_VISIBLE,
200, 200, 200, 200, nullptr, nullptr, hInstance, nullptr);
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = nullptr;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = nullptr;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = nullptr;
return RegisterClassExW(&wcex);
}