That does change things.
The scaling done by some of the APIs seems to depend on which monitor contained the script's last active window.
This appears to work for enabling per-monitor DPI awareness on Windows 10 (probably 1603 and later):
Code: Select all
DllCall("SetThreadDpiAwarenessContext", "ptr", -3, "ptr")
The return value can be passed back to the function to restore the previous context.
This function also allows DPI virtualization to be enabled for specific windows while being disabled for calls to system APIs:
- Create the window while DPI awareness is on "system" (-2, already set via the manifest).
- Set it to -3 or -4 after creating the window, or prior to calling any system APIs.
This is very unclear in the documentation, if not undocumented, although I suppose the existence of GetWindowDpiAwarenessContext should have been a clue.
On version 1703 and later, -4 (Per Monitor v2) can be used to enable scaling of dialogs, menus, tooltips and some other things. However, it also causes the non-client area (title bar) to scale, which ends up squashing the client area because the script isn't actually per-monitor DPI aware (doesn't respond to WM_DPICHANGED). This can be avoided by setting the context to -3 before creating the GUI, but -4 before creating any tooltips, menus or dialogs.
The DPI awareness temporarily reverts to the process default while you're moving one of the script's windows. Calling SetThreadDpiAwarenessContext immediately before calling WinGetPos, ToolTip, etc. solves the problem. Setting the process default awareness more than once is not possible, and it's already set by the manifest before the script starts.
Win32 dialogs created from a template are automatically scaled with Per Monitor v2. InputBox is scaled in full quality, while MsgBox looks blurry. Weird.
Unfortunately, there's no quick fix for Windows 8.1 users, or old builds of Windows 10. We either have automatically-scaling windows or APIs that tell the truth, not both. Proper per-monitor DPI support would require handling WM_DPICHANGED and repositioning all of the controls. I imagine that rounding could be a problem when moving a GUI back and forward between monitors, unless the position is stored (and updated whenever the control is moved by some other means).
There's a confusing mess of functions because of the different levels of DPI scaling supported by Windows Vista - 8, Windows 8.1 and 10. For Windows 8.1, there is SetProcessDpiAwareness, which cannot be used because the DPI awareness is already set via the manifest. Perhaps we can remove dpiAware from the manifest and use SetProcessDpiAwareness or SetThreadDpiAwarenessContext if available, but since the former only works once, it would probably have to be controlled by a directive if we want to set a default. Also, "Setting the process-default DPI awareness via API call can lead to unexpected application behavior."
Modifying the manifest with a resource editor is relatively trivial. It can also be done programmatically, such as by using the EnableUIAccess_SetManifest() function from the AutoHotkey installer as a basis.