Scaling the non-client area (title bar, menu bar) for per-monitor high-DPI support - windows

Windows 8.1 introduced the ability to have different DPI settings for different monitors. This feature is known as "per-monitor high-DPI support." It persists and has been further refined in Windows 10.
If an application does not opt in (i.e., is either DPI-unaware or high-DPI aware), it will be automatically scaled up by DWM to the proper DPI. Most applications fall into one of these two categories, including most of the utilities bundled with Windows (e.g., Notepad). On my test system, the high-DPI monitor is set to 150% scale (144 DPI), while the normal monitor is set to the system DPI (100% scale, 96 DPI). Therefore, when you open one of these applications on the high-DPI screen (or drag it over there), virtualization kicks in, magnifying everything, but also making it incredibly blurry.
On the other hand, if an application explicitly indicates that it supports per-monitor high-DPI, then no virtualization is performed and the developer is responsible for scaling. Microsoft has a fairly comprehensive explanation here*, but for the benefit of a self-contained question, I'll summarize. First, you indicate support by setting <dpiAware>True/PM</dpiAware> in the manifest. This opts you into receiving WM_DPICHANGED messages, which tells you both the new DPI setting as well as a suggested new size and position for your window. It also allows you to call the GetDpiForMonitor function and obtain the actual DPI, without being lied to for compatibility reasons. Kenny Kerr has also written up a comprehensive tutorial.
I've gotten all of this going successfully in a little C++ test app. It's a lot of boilerplate and mostly project settings, so I don't see much point in posting a full example here. If you want to test it out, either follow Kenny's instructions, this tutorial on MSDN, or download the official SDK sample. Now, the text in the client area looks good (because of my handling of WM_DPICHANGED), but because virtualization is no longer performed, there is no scaling of the non-client area. The result is that the title/caption bar and the menu bar are the wrong size—they do not get larger on the high-DPI screen:
So the question is, how do I get the non-client area of the window to scale to the new DPI?
It doesn't matter whether you create your own window class or use a dialog—they have identical behavior in this respect.
It has been suggested that there is no answer—that your only choice is to custom draw the entire window, including the non-client area. While this is certainly possible, and indeed what UWP apps (those previously known as Metro) do, like the Windows 10 Calculator, it is not a workable option for desktop applications that use many non-client widgets and hope to look native.
Aside from that, it is demonstrably false. Custom-drawn title bars cannot be the only way of getting the correct behavior, since the Windows shell team has done it. The humble Run dialog behaves exactly as expected, properly resizing both the client and non-client areas as you drag it between monitors with different DPIs:
Investigation with Spy++ confirms this is just a bog-standard Win32 dialog—nothing fancy. All of the controls are standard Win32 SDK controls. It is not a UWP app, nor have they custom-drawn the title bar—it still has the WS_CAPTION style. It is launched by the explorer.exe process, which is marked as per-monitor high-DPI aware (verified with Process Explorer and GetProcessDpiAwareness). This blog post confirms that both the Run dialog and the Command Prompt have been rewritten in Windows 10 to scale correctly (see "Command shells et al."). What is the Run dialog doing to resize its title bar?
The Common Item Dialog API, responsible for new-style Open and Save dialogs, also scales correctly when launched from a process that is per-monitor high-DPI aware, as you can see when clicking the "Browse" button from the Run dialog. Same thing for the Task Dialog API, creating the odd situation where an app launches a dialog box with a different-size title bar. (The legacy MessageBox API has not been updated, however, and exhibits the same behavior as my test app.)
If the shell team is doing it, it has to be possible. I just cannot imagine that the team responsible for designing/implementing per-monitor DPI support neglected to provide a reasonable way for developers to produce compatible applications. Features like this require developer support, or they are broken out-of-the-box. Even WPF applications are broken—Microsoft's Per-Monitor Aware WPF Sample project fails to scale the non-client area, resulting in a title bar that is the wrong size. I'm not much for conspiracy theories, but this smells of a marketing move to discourage desktop app development. If so, and there is no official way, I'll accept answers that rely on undocumented behavior.
Speaking of undocumented behavior, logging window messages when the Run dialog is dragged between monitors with different DPI settings shows that it receives an undocumented message, 0x02E1. This is somewhat interesting because this message ID is exactly one greater than the documented WM_DPICHANGED message (0x02E0). My test app never gets this message, though, regardless of its DPI-awareness settings. (Curiously, close inspection does reveal that Windows slightly increases the size of the minimize/maximize/close glyphs on the title bar as the window moves onto the high-DPI monitor. They're still not as big as they are when they are virtualized, but they're slightly bigger than the glyphs that it uses for unscaled system-DPI applications.)
So far, my best idea has been to handle the WM_NCCALCSIZE message to adjust the size of the non-client area. By using the SWP_FRAMECHANGED flag with the SetWindowPos function, I can force the window to resize and redraw its non-client area in response to WM_DPICHANGED. This works fine to reduce the height of the title bar, or even remove it altogether, but it will never make it any taller. The caption seems to peak out at the height determined by the system DPI. Even if it worked, this wouldn't be the ideal solution, because it wouldn't help with the system-drawn menu bar or scroll bars…but at least it would be a start. Other ideas?
* I know that this article 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." See above for why that is (1) wrong and (2) unsatisfactory. I'm looking for a workaround other than custom-drawing the non-client area.

In any up-to-date Windows Insider builds (build >= 14342, SDK version# >= 14332) there is an EnableNonClientDpiScaling API (which takes an HWND as its argument) that will enable non-client DPI scaling for top-level HWNDs. This functionality requires that the top-level window be running in per-monitor DPI-awareness mode. This API should be called from the WM_NCCREATE handler for the window. When this API is called on a top-level window, its caption bar, top-level scrollbars, system menu and menubar will DPI scale when the application’s DPI changes (this can happen when the app is moved to a display with a different display scaling value or when the DPI changes for other reasons such as the user making a settings change or when an RDP connection changes the scale factor).
This API does not support scaling non-client area for child windows, such as scroll bars in a child window.
To my knowledge there is no way to have non-client area DPI scale automatically without this API.
Note that this API has not yet been finalized, and it may change prior to being released in the Windows 10 Anniversary update. Keep your eye on MSDN for official documentation when it becomes final.

With Per Monitor V2 DPI awareness in Windows 10 Creators Update (build 15063) you can resolve this easily without the EnableNonClientDpiScaling.
To enable Per Monitor V2 DPI awareness, while still supporting old Per Monitor DPI awareness on older Windows 10 builds and Windows 8.1 and DPI awareness on yet older versions of Windows, make your application manifest like this:
<assembly ...>
<!-- ... --->
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>True/PM</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
References:
High DPI Improvements for Desktop App Developers in the Windows 10 Creators Update – video (archived)
Application Manifests – reference
High DPI Desktop Application Development on Windows – documentation
Note that in WinForms targeting .NET 4.7 and later, you can achieve the same by adding
<add key="DpiAwareness" value="PerMonitorV2" />
to <System.Windows.Forms.ApplicationConfigurationSection> tag in app.config. I assume that in the end, this change modifies the manifest in the target binary as described above.

Related

Change Qt Creator Interface Scaling / Font Size

Can I change Qt Creator's (latest version: 4.14.0, Windows 10) general GUI font size (or scaling)?
The problem is, my system display scaling is set to 250% and I have two options for high DPI compatibility in Qt Creator, but both have issues:
I can disable Creator's built-in High DPI Scaling and force System (Enhanced) in the Windows compatibility settings:
Or I can enable Creator's built-in scaling and leave the Windows compatibility scaling override turned off (Application scaling):
In the former case, it's actually a size I find very comfortable except the cursors are all too small and Designer gets wonky (plus it's tricky to layout GUI's because apps all run in System (Enhanced) mode from Creator).
In the latter case, the cursors and Designer work, and it's nice to not have heavy anti-aliasing everywhere, except the GUI font is too large for me, and everything feels cramped (especially the top bar, and the left bar reminds me of one of those old school children's telephones with the huge buttons). Plus the general GUI font is different enough in size from my editor font that the whole thing just feels jarring and kind of stresses me out.
So what I think I'd like to do, ideally, is use application scaling like in the latter example, but just shrink the GUI font size to match the former.
I think I ran through all the options thoroughly, and all I can find is settings for the editor window and the console, but not the rest of the GUI.
To be honest, I'm actually not entirely sure why they don't look the same; my expectation was that Qt's built-in High DPI support would take the scale factor into account in the same way that Windows' System (Enhanced) mode does, but I'm sure it's more complicated than that.
The problem is Qt automatically round up your 250% scaling to 300%. For Qt Creator application, you can create a "QT_SCALE_FACTOR_ROUNDING_POLICY" environment variable, and set it to "PassThrough". You can find details in this question: Qt Creator "too big" on 3840x2160 and 150% scaling on Windows 10.
It's also worth noting that Qt also behaves like this coding a GUI program. This can be fixed with:
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);

Outlook font size for add in

I need help with the text size for my outlook add in.
I have a table view in my add in which is unreadable with bigger screen resolutions. Is there a way to zoom so the font size gets bigger?
With a selected email from my inbox it is possible to zoom with the zoom slider in the bottom right corner. Is there a way to activate the zoom slider at any time? Or is there any way to access and change/set the font size outside of the email body?
It looks like your add-in is not DPI-aware. Desktop applications using older Windows programming technologies (raw Win32 programming, Windows Forms, Windows Presentation Framework (WPF), etc.) are unable to automatically handle DPI scaling without additional developer work. Without such work, applications will appear blurry or incorrectly-sized in many common usage scenarios. To make things working better you must read the following articles:
High DPI Desktop Application Development on Windows
Writing DPI-Aware Desktop and Win32 Applications
Most probably the table view is used on a windows form, see Automatic scaling in Windows Forms for more information.

Is it possible to keep the Windows' compositor working normally when using a fullscreen OpenGL application?

Under Windows, when the DWM composition is active, there's a somehow "special path" for fullscreened OpenGL applications that prevents all other subwindows (popups, menus, tooltips) to be correctly layered on top of the fullscreened OpenGL window.
While this is useful for games and such, it's totally a pain for other fullscreen OpenGL applications (CAD, 3d editors, etc.). This problem is causing endless troubles for Qt users, see here, here, or here.
Is there a way to tell Windows not to enable the special path for a given application / fullscreen window? Either in the manifest, or via DWM APIs, I don't care.
Yes, this can be done by tricking Windows into thinking that the Window should be composited with transparency. For this you call DwmEnableBlurBehindWindow on the window. In case of a fullscreen WS_POPUP window this makes the window fully transparent (instead of the glass effect) and you can use the window's alpha channel to control the opacity; for a regular window with a title area and border you get the glassy effect then.
Now if you configure the pixelformat without an alpha channel or set the alpha channel to all 1 (full opacity) the compositor still has to assume some transparency may be present and goes through full composition.
Small update
Although WinXP and Win2k are beyond their EOL, it may be undesireable to hardlink the dwmapi.dll to the executable, e.g. if you have to support legacy systems with your software for some reason. For that I wrote a small wrapper library dwm_load that dynamically loads the dwmapi.dll if available or falls back to failsafe implementation of the DWM functions.

For the same screen resolution and DPI settings, Can the pixels for a IDE control change between two OSes?

Assume a GUI application is opened on three machines running Windows XP, Windows Vista and Windows7. In all the three machines, the screen resolution and the DPI settings are set to be the same value. Will there be a difference in the way the application is displayed on the screen in terms of number of pixels used and their position on the screen?
The reason for asking this question is:
I am using position based record-and-play method for GUI automation. Any change in the position of a control can impact the playback of the GUI recording. I want to be sure that a recording captured on Windowx XP platform works on Vista and Windows7 platforms.
Yes. The OS chrome (starting with window borders and titles) have different styles (e.g. Vista and Win7 will likely have Aero on and thus translucent title).
And that's before considering any OS dependent code in the application.
The menu bars will probably all be different sizes, so you'll probably need to record separately on all three machines.
Bearing in mind, that each user could have any number of accessibility settings on/off, any DPI setting, and also that features such as button sizes and window border sizes are different on each of these OS's...
No one here could possibly guarantee you'll be fine - the only way is to test.
A side note: there MUST be a better way than position based playback? I've used tools previously that can read screen text and base the automated navigation on that, which seems far more sensible, but still horribly flawed.

Window styles / Minimal titlebar/borders

I'm looking for some kind of a resource (website) that would list all possible window/dialog frame styles and their respective combinations with images. I'm only really interested in Vista, as my software won't support older platforms anyway.
I have a more specific case here too: I'm wondering if there are other ways to achieve a smaller-than-normal titlebar for my window than WS_EX_TOOLWINDOW? The tool window style would otherwise suit my needs, but in addition to the normal window border, it seems to add this one-pixel wide white border inside the black outline, and that just looks really ugly for my purposes.
I remember older versions of Adobe Photoshop (CS2?) having these ridiculously tiny titlebars on the tool windows, like 8-10px wide. I'm wondering if those can be done with normal winapi, since IIRC they came in vista flavour too, and conformed to whatever windows skin was in use..?
If it's Vista-only, you can try to use your own window decoration and use the Desktop Window Manager (DWM) API to still provide Aero Glass Theming.
On the other hand, if you're targeting Vista and later, you'll most likely not have to deal with low resolutions. Don't think too much about a few pixels more or less.

Resources