【问题标题】:SetWindowPos() cross-process DPI awareSetWindowPos() 跨进程 DPI 感知
【发布时间】:2019-02-08 11:28:21
【问题描述】:

我正在创建一个程序,该程序使用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 的应用程序,当窗口位于辅助屏幕上时(与使用 120dpi 的主屏幕相比,它使用 96dpi),这将失败。这与窗口框架大小无关,我还不确定它究竟为什么会失败,但目标 x 和 y 坐标已按比例放大,以便窗口移出屏幕。
  • 如果由于调整大小,窗口的中心改变了屏幕,会发生什么情况?那么screenDPI 将不再正确,对吧?我将如何处理这种情况?

我知道还有AdjustWindowRectExForDpi 函数,但不知怎的我无法让它正常工作。我应该传递给它的dpi 值是什么?目标屏幕的dpi,目标窗口的dpi还是我自己程序的dpi?另外,这个功能只能从 Windows 10 开始使用,那么在旧的 Windows 客户端上我该如何处理呢?

我将不胜感激。谢谢!

【问题讨论】:

    标签: c++ windows winapi dpi


    【解决方案1】:

    我应该传递给它的 dpi 值是多少?目标屏幕的dpi,目标窗口的dpi还是我自己程序的dpi?

    您需要从一个屏幕移动到下一个屏幕的窗口的 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);
    }
    

    窗口可以从一个屏幕移动到下一个屏幕并成功重新计算窗口大小。

    【讨论】:

    • 太棒了,谢谢。但是,如果我将窗口从屏幕 A 移动到 B 并且屏幕 B 上的窗口 dpi 不同,那么我是否必须使用未来的 dpi 来调整窗口矩形?
    • 不,您不必这样做。当您移动到具有新 dpi 的屏幕时,该窗口还将收到 WM_DPICHANGED 消息并重新计算窗口大小。
    猜你喜欢
    • 1970-01-01
    • 2011-05-03
    • 1970-01-01
    • 1970-01-01
    • 2015-10-09
    • 1970-01-01
    • 1970-01-01
    • 2012-12-01
    相关资源
    最近更新 更多