2

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.

Robinson
  • 9,666
  • 16
  • 71
  • 115
  • 1
    Your "conclusion" sounds like it should be posted as an answer rather than an edit to the question. See [Can I answer my own question?](https://stackoverflow.com/help/self-answer) – Remy Lebeau Mar 28 '23 at 15:56
  • I think so, possibly. I was just waiting to see if any other insight appeared and writing some code and testing my conclusion just in case I've made an error. – Robinson Mar 28 '23 at 17:49
  • 1
    I agree with you. When the effective dots per inch (dpi) for a window has changed the [WM_DPICHANGED message](https://learn.microsoft.com/en-us/windows/win32/hidpi/wm-dpichanged) will be sent. The base value of DPI is defined as USER_DEFAULT_SCREEN_DPI which is set to 96. To determine the scaling factor for a monitor, take the DPI value and divide by USER_DEFAULT_SCREEN_DPI. – Jeaninez - MSFT Mar 29 '23 at 05:47
  • 1
    @Robinson - I upvoted your question and answer. I've cut my teeth on DPI issues and always appreciate seeing others go through the same learnings. Now go test how your app handles migration from mixed resolution/dpi multi-monitor setup to running in a remote desktop on a 1080p conference room projector. Fun stuff! – selbie Apr 20 '23 at 19:09

1 Answers1

1

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

Robinson
  • 9,666
  • 16
  • 71
  • 115
  • Your left-hand dialog is not just "Scaling performed by Application", it is also "Override high DPI scaling behavior". The behavior you're not liking is more reasonably explained by the "Override". – Ben Voigt Apr 20 '23 at 19:11
  • Yes. That has caused me a lot of confusion. Now I don't know what "application controlled" actually means. I will look it up sometime. – Robinson Apr 20 '23 at 20:30