SetWindowPos scaling behaviour with WS_EX_LAYERED windows

reuk 6 Reputation points
2021-05-04T18:09:56.153+00:00

I've written an application on Windows 10 19042.964 which shows four transparent windows (i.e. with WS_EX_LAYERED set) in a square configuration. These windows move back and forth between two external displays with different DPI settings. The primary display is on the right, and has its scaling set to 175%. The screen on the left has its scale set to 200%. The program calls SetProcessDpiAwareness shortly after startup, passing DPI_AWARENESS_SYSTEM_AWARE.

I set the size of each window to be 100x100, and then set the window positions so that all windows are adjacent and arranged in a square. When the windows move to the monitor on the left, their positions and sizes seem to be modified by the OS, so that the windows are no longer arranged in a square. However, if I avoid using the the WS_EX_LAYERED flag, the relative positions of the windows remain correct.

In short, enabling the WS_EX_LAYERED flag seems to cause the OS to interpret the arguments to SetWindowPos differently than when the WS_EX_LAYERED bit is not set. Windows seems to apply some additional scaling in this case. Is this behaviour documented anywhere? What technique should be used to precisely position WS_EX_LAYERED windows across multiple monitors with different scaling in an app with system DPI awareness? Is it possible to force SetWindowPos to behave in the same way, regardless of whether or not WS_EX_LAYERED is set?

Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,426 questions
0 comments No comments
{count} vote

3 answers

Sort by: Most helpful
  1. Xiaopo Yang - MSFT 11,496 Reputation points Microsoft Vendor
    2021-05-05T06:47:16.907+00:00

    For DPI_AWARENESS_SYSTEM_AWARE, the application will be automatically scaled up or down by the system whenever the DPI changes from the system value. Maybe the layered window's off-screen buffer causes the curious behavior. Try to use UpdateLayeredWindow instead of SetLayeredWindowAttributes and SetWindowPos.

    It is recommended that desktop applications be updated to use per-monitor DPI awareness mode. When an application reports to Windows that it wants to run in this mode, Windows will not bitmap stretch the application when the DPI changes, instead sending WM_DPICHANGED to the application window. It is then the complete responsibility of the application to handle resizing itself for the new DPI. more information and a sample.

    It is recommended that you set the process-default DPI awareness via application manifest. See Setting the default DPI awareness for a process for more information.

    1 person found this answer helpful.
    0 comments No comments

  2. Edward 1 Reputation point
    2022-03-25T04:37:48.407+00:00

    Hi..

    I am experiencing the same case as you.

    A child window with WS_EX_LAYERED attribute is created, and it is tested while moving it to the primary monitor (Scale Factor=123%) and secondary monitor (Scale Factor=100%).

    There is no problem with the primary monitor (125%).
    (If Scale factor are same for each monitors, then there is no problems)

    However, if the window is moved to the secondary monitor, the x and y values of the child windows jump to extremely large values.
    Of course, if remove the WS_EX_LAYERED attribute, there is no problem.

    Do you have any solution?

    0 comments No comments

  3. Yusuf Ramazan Karagöz 10 Reputation points
    2023-01-27T11:01:44.5+00:00

    We face the similar issue with gdi-scaling. child windows that have layered style, has been drawn on false coordinates.

    The same issue we have also with WebView2, which i think also uses layered style.

    It is not the option for us to change the dpi awareness easily, because of expenditure of huge old application.

    Is there any workaround?