19
votes

I am making my app dpi-aware per monitor by setting <dpiAware>True/PM</dpiAware> in the manifest file. I can verify with process explorer that this is indeed working or by calling GetProcessDpiAwareness.

This is all working fine and I can scale anything in the client area fine in my code. However, my only problem is that if I drag my app from a system-dpi monitor to a non-system dpi monitor, the title bar and any system menu would either become too big or too small. This isn't the case for most built-in apps (e.g. calc, edge browser, etc..) so there must be away to scale it properly. Does anyone how the devs at MS did this?

The screenshot below should explain my problem better. Also notice, that the padding between the close, min, and max button is different when it's scaled (96dpi).

Screenshot

Sample app I'm attaching a very simple app that is per-monitor dpi aware.

4
What does your per-monitor code looks like? Handler for WM_DPICHANGED?David Heffernan
Is your app a universal application? Both calculator and Edge aremelak47
@melak47 It isn't a universal app. This is an old app - just wanted to make it scale properly on all my monitors. Do you think windows would scale the title bar if it is a universal app?Dealdiane
Hmm.. ok. I'm not sure how you can call it off-topic but here's an off-the-shelf scaffold with the dpi-awareness code that I'm using. The code that sets the dpi-awareness is in wWinMain (not in manifest). WndProc handles WM_DPICHANGED as well but doesn't do anything. You can verify the dpi-awareness of the app using process explorer (the app runs its own verifications too)Dealdiane
ok done. afaik, the sample app is pretty much standard but I hope people would find that relevant.Dealdiane

4 Answers

7
votes

Does anyone how the devs at MS did this?

This has a pretty disappointing answer. Using Alin Constantin's WinCheat and inspecting the top-level window of Calculator, I see a window size of 320x576, and a client size that is also 320x576.

In other words, Microsoft entirely avoids the problem by suppressing the non-client area of the window, putting everything in the client area instead. Making this work well for you may involve custom drawing of the title bar.

Something worth noting is that Calculator and e.g. Windows Explorer don't use the same colour for the title bars. Calculator doing custom drawing of the title bar would explain that perfectly.

10
votes

The Windows 10 Anniversary Update (v1607) has added a new API you must call to enable DPI scaling of the non-client areas: EnableNonClientDpiScaling. This function should be called, when WM_NCCREATE is received. The message is sent to the window's procedure callback during window creation.

Example:

case WM_NCCREATE:
{
    if (!EnableNonClientDpiScaling(hWnd))
    {
        // Error handling
        return FALSE;
    }

    return DefWindowProcW(...);
}

If the application's DPI-awareness context is DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, then calling EnableNonClientDpiScaling should be omitted, as it won't have any effect, although the function will still return successfully.

From the documentation:

Non-client scaling for top-level windows is not enabled by default. You must call this API to enable it for each individual top-level window for which you wish to have the non-client area scale automatically. Once you do, there is no way to disable it. Enabling non-client scaling means that all the areas drawn by the system for the window will automatically scale in response to DPI changes on the window. That includes areas like the caption bar, the scrollbars, and the menu bar. You want to call EnableNonClientDpiScaling when you want the operating system to be responsible for rendering these areas automatically at the correct size based on the API of the monitor.

See this blog post for additional information about DPI scaling changes in Windows 10 AU.

0
votes

The documentation says:

Note that the non-client area of a per monitor–DPI aware application is not scaled by Windows, and will appear proportionately smaller on a high DPI display.

The Microsoft apps that you link to deal with this by removing the non-client area and making the client area cover the entire window.

0
votes

UPDATE:

It is enough to add new <dpiAwarness> declaration to manifest to solve all this mess. Example is here.

Remnants of former investigations (obsolete):

More investigations on the subject.

System setup: two monitors, one is 96 dpi another one is 267 dpi (Microsoft Surface 4).

Testing window is moved to secondary 96 dpi monitor:

Here is rendering (wrong, IMO) with <dpiAware>true/pm</dpiAware> in manifest:

enter image description here

Note huge size of caption bar and wrong sizes of window icons.

And here is correct rendering using <dpiAware>true</dpiAware>

enter image description here

And I suspect that MSDN documentation is plainly misleading about values of PROCESS_DPI_AWARENESS. I do not see any differences in messages and styles between <dpiAware>true</dpiAware> and <dpiAware>true/pm</dpiAware>. The later one just makes caption larger. In both case application receives WM_DPICHANGED message while moving between monitors with different DPI.

Sigh.