SetWindowPos () с поддержкой DPI - PullRequest
0 голосов
/ 08 февраля 2019

Я создаю программу, которая перемещает / изменяет размеры окон из другого процесса с помощью SetWindowPos().Моя собственная программа PROCESS_PER_MONITOR_DPI_AWARE.Другими программами могут быть что угодно: PROCESS_DPI_UNAWARE, PROCESS_SYSTEM_DPI_AWARE или PROCESS_PER_MONITOR_DPI_AWARE.

Поскольку моя собственная программа PROCESS_PER_MONITOR_DPI_AWARE, координаты, которые я передаю SetWindowPos(), находятся в физических координатах.Теперь я хочу изменить размер клиентской области до определенного размера в логических координатах .

То, что я пытался сделать, это

  1. Получите DPI монитора, в котором расположено окно, как screenDPI.
  2. Получите DPI целевого окна как windowDPI.
  3. Получите scaleFactor как screenDPI / windowDPI.
  4. Масштабирование желаемого размера клиентской области на scaleFactor
  5. Вычислили дополнительный размер для оконной рамы путем вычитания текущего размера прямоугольника клиента из размера прямоугольника окна.

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

  • вычисление размера рамки окна отключается, если я перемещаю окно с одного экранак следующему.
  • это не работает для приложения, которое использует PROCESS_SYSTEM_DPI_AWARE, когда окно расположено на дополнительном экране (который использует 96 точек на дюйм по сравнению с основным экраном с 120 точек на дюйм).Это не имеет никакого отношения к размеру оконной рамы, и я пока не уверен, почему именно он терпит неудачу, но координаты x и y цели масштабируются так, что окно перемещается за экран.
  • чтопроизойдет, если из-за изменения размера центр окна изменит экран?Тогда screenDPI больше не будет правильным, верно?Как бы я справился с этим делом?

Я знаю, что есть также функция AdjustWindowRectExForDpi, но почему-то я не могу заставить ее работать должным образом.Какое значение dpi я должен передать?Точек на дюйм целевого экрана, точек на дюйм целевого окна или точек на дюйм моей собственной программы?Кроме того, эта функция доступна только в Windows 10 и далее, так как бы я справился с ней на более старом клиенте Windows?

Буду признателен за помощь.Спасибо!

1 Ответ

0 голосов
/ 13 февраля 2019

Какое значение dpi я должен передать?Точка на дюйм целевого экрана, точка на дюйм целевого окна или точка на дюйм моей собственной программы?

Точка на дюйм окна, которую необходимо переместить с одного экрана на другой.

пример кода:

#include <Windows.h>

LRESULT CALLBACK startup_window_procedure(HWND window, UINT message, WPARAM w_param, LPARAM l_param)
{
    switch (message)
    {
    case WM_DESTROY:
    {
        PostQuitMessage(0);
        return 0;
    }

    case WM_DPICHANGED:
    {
        // Resize the window
        RECT* new_rect = reinterpret_cast<RECT*>(l_param);

        if (!SetWindowPos(window, nullptr, new_rect->left, new_rect->top, new_rect->right - new_rect->left, new_rect->bottom - new_rect->top, SWP_NOZORDER | SWP_NOACTIVATE))
        {
            return 1;
        }

        return 0;
    }
    }

    return DefWindowProcW(window, message, w_param, l_param);
}

int CALLBACK wWinMain(HINSTANCE instance, HINSTANCE prev_instance, PWSTR cmd_line, int cmd_show)
{
    constexpr auto window_class_name = L"example_dialog";
    constexpr auto window_style = WS_OVERLAPPEDWINDOW;

    // Enable per-monitor DPI-awareness version 2
    if (!SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2))
    {
        return 1;
    }

    // Create the window
    WNDCLASSEXW window_class;
    window_class.cbSize = sizeof(window_class);
    window_class.style = CS_HREDRAW | CS_VREDRAW;
    window_class.lpfnWndProc = startup_window_procedure;
    window_class.cbClsExtra = 0;
    window_class.cbWndExtra = 0;
    window_class.hInstance = instance;
    window_class.hIcon = nullptr;
    window_class.hCursor = nullptr;
    window_class.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
    window_class.lpszMenuName = nullptr;
    window_class.lpszClassName = window_class_name;
    window_class.hIconSm = nullptr;

    if (!RegisterClassExW(&window_class))
    {
        return 1;
    }

    HWND window = CreateWindowExW(0, window_class_name, L"Example window", window_style, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, nullptr, nullptr, instance, nullptr);

    if (!window)
    {
        return 1;
    }

    UINT dpi = GetDpiForWindow(window);
    float scaling_factor = static_cast<float>(dpi) / 96;
    // Actually set the appropriate window size
    RECT scale;
    scale.left = 0;
    scale.top = 0;
    scale.right = static_cast<LONG>(300 * scaling_factor);
    scale.bottom = static_cast<LONG>(150 * scaling_factor);

    if (!AdjustWindowRectExForDpi(&scale, window_style, false, 0, dpi))
    {
        return 1;
    }

    if (!SetWindowPos(window, nullptr, 0, 0, scale.right - scale.left, scale.bottom - scale.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE))
    {
        return 1;
    }

    ShowWindow(window, SW_SHOWNORMAL);

    // Message loop
    MSG message;
    int result;

    while ((result = GetMessageW(&message, nullptr, 0, 0)) != 0)
    {
        if (result == -1)
        {
            return 1;
        }
        else
        {
            TranslateMessage(&message);
            DispatchMessageW(&message);
        }
    }

    return static_cast<int>(message.wParam);
}

Окна могут перемещаться с одного экрана на другой и успешно пересчитывать размер окна.

...