Search code examples
winapivisual-c++highdpi

Windows 10/11 High DPI Aware, not receiving WM_DPICHANGED message


My Win32 C++ application has a manifest in the resource file containing:

<asmv3:windowsSettings xmlns='https://schemas.microsoft.com/SMI/2016/WindowsSettings'>
    <dpiAwareness>PerMonitorV2,PerMonitor</dpiAwareness>"
</asmv3:windowsSettings>

At the start of the program, I call GetAwarenessFromDpiAwarenessContext() to check the DPI_AWARENESS and it returns DPI_AWARENESS_PER_MONITOR_AWARE. So far, so good.

I have two monitors attached. One a 3840x1200 ultra-wide, and the other a true 4K. When I query for scaling info, they both claim a DPI of 96. Neither monitor is being scaled by DWM in Display Settings. Use of EnumDisplayMonitors() gives:

\\.\DISPLAY1
monitor rect:    (0,0)-(3840,1200)
work rect:       (0,0)-(3840,1200)
DPI (effective): 96,96
DPI (angular):   91,91
DPI (raw):       92,92
BPP:             32
resolution:      3840,1200
frequency:       144
    
\\.\DISPLAY2
monitor rect:    (3240,-2160)-(7080,0)
work rect:       (3240,-2160)-(7080,0)
DPI (effective): 96,96
DPI (angular):   156,159
DPI (raw):       157,160
BPP:             32
resolution:      3840,2160
frequency:       60

I'm guessing the reason my application does not receive the WM_DPICHANGED message when I move the window from one monitor to the other is because Windows thinks they both have a DPI of 96. But I've told Windows in my manifest and verified the fact my application is per-monitor DPI aware.

Can anyone explain this discrepancy? How is one to develop a high DPI-aware application in this case?


Edit 1: I determined that changing scaling on the second monitor to 150% results in the WM_DPICHANGED message being received by my application when I drag it over to that monitor.

So, in conclusion, being dpi-aware means your DPI is default (96) across all monitor and windows. The default isn't the default DPI for the monitor itself. That value is then changed by the DWM scale factor, and the difference between that scale factor and the other is what causes the message to be sent.

EDIT 2:

So I refactored my application to use scaling when I receive the message. I turned on my 4K monitor. I set it to 150%. The main monitor is 100%. When I drag the application onto the 4K monitor I DO NOT get a WM_DPICHANGED message. When I tried it last week I did get this message.

NOTE: If I start the application on the 4K monitor, I get the correct scale factor (in this case, x 2). My top level Win32 window is just not getting notified of the change when I drag the window onto it. I'm asserting being DPI_AWARENESS_PER_MONITOR_AWARE at program startup, so I know it's not that.

Further: I do not get this message when I sit the application on the other monitor and play around with the monitor's scaling in Display Properties.


Solution

  • I finally solved the problem. I wasn't getting WM_DPICHANGED messages until I made this simple change to my .exe. Apparently "Scaling performed by Application" doesn't mean what you think it means.

    enter image description here