Sizing GUI-Elements: dpi dpiaware has to be set to "True/PM" to work on Win 8.1 and above

Report problems with documented functionality
User avatar
Taurus
Posts: 62
Joined: 20 Jan 2015, 10:31

Sizing GUI-Elements: dpi dpiaware has to be set to "True/PM" to work on Win 8.1 and above

09 Feb 2016, 10:49

Hi,

I got the following problem:

Code: Select all

SysGet, MonitorWorkArea_, MonitorWorkArea

MsgBox % A_ScreenDPI "-" MonitorWorkArea_Bottom "-" MonitorWorkArea_Right "
Result on Win 10 with 125% text-size (monitor config) is wrong (shows 96 dpi). It includes the dpi-sizing, so you can't use this for positioning own GUIs.

Solution for the ahk-Team:

This has to be done while compiling autohotkey in Visual Studio:

Open source\resources\AutoHotkey.exe.manifest > set dpiaware to "True/PM"

Solution for people who can't wait for an update:

Open your compiled script with resource hacker > Manifest > 1 > set dpiaware to "True/PM"

Final should be: <dpiAware>True/PM</dpiAware>


So have a nice day without high dpi monitor problems anymore! :)

Greetings


Sources:

Figure 2 of http://www.drdobbs.com/windows/coding-f ... 736?pgno=3
https://technet.microsoft.com/en-us/lib ... 28846.aspx

This is interesting to react on dpi-changes while running (but i don't need it, so i didn't used it)
https://msdn.microsoft.com/en-us/librar ... s.85).aspx
Dev for a better world :) > PHP for Web > AHK H for Local > with KISS (Keep it Short and Simple) on Win 10 Pro (Version 1903) x64
lexikos
Posts: 6668
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Sizing GUI-Elements: dpi dpiaware has to be set to "True/PM" to work on Win 8.1 and above

09 Feb 2016, 22:32

Open source\resources\AutoHotkey.exe.manifest > set dpiaware to "True/PM"
What are you talking about? I'm pretty sure AutoHotkey.exe.manifest has had <dpiAware>true</dpiAware> since A_ScreenDPI was added.

I have just compiled a script and confirmed that it has <dpiAware>true</dpiAware>, although I have not re-tested A_ScreenDPI with 125% DPI yet.

Are you using any tools which modify or replace the manifest resource? Had you manually done so?
guest3456
Posts: 2796
Joined: 09 Oct 2013, 10:31

Re: Sizing GUI-Elements: dpi dpiaware has to be set to "True/PM" to work on Win 8.1 and above

09 Feb 2016, 23:28

true/pm (per monitor) is different than true (system wide)

https://msdn.microsoft.com/en-us/librar ... s.85).aspx

i think this also affects external win32 api calls as well such as GetPixel

lexikos
Posts: 6668
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Sizing GUI-Elements: dpi dpiaware has to be set to "True/PM" to work on Win 8.1 and above

10 Feb 2016, 02:03

Thank you for pointing out what should have been obvious; I was reading it as "true or pm", not literally, and now that you mention it I recall reading about the "true/pm" value somewhere.
lexikos
Posts: 6668
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Sizing GUI-Elements: dpi dpiaware has to be set to "True/PM" to work on Win 8.1 and above

10 Feb 2016, 03:34

A_ScreenDPI returns 120 on my Windows 10 system, and it is correct.

I also tested with a second screen at 168 (175%), and everything worked as expected. A_ScreenDPI returned the correct DPI of the primary screen; 168 or 120, depending on which screen was set as the primary.

AutoHotkey is not per-monitor DPI aware, so I'm not so sure that changing the value to true/pm is a good idea. Applications which are system DPI aware but not per-monitor DPI aware are automatically scaled when they are on a screen other than the primary one. Without this, AutoHotkey GUIs would scale according to the primary display and stay that size even if they are placed on a secondary display (which could be a vastly different size).

As far as I can tell, true vs true/pm should make no difference whatsoever on a system with only one display.
User avatar
Taurus
Posts: 62
Joined: 20 Jan 2015, 10:31

Re: Sizing GUI-Elements: dpi dpiaware has to be set to "True/PM" to work on Win 8.1 and above

10 Feb 2016, 10:30

Interessting... it's not working on my system. Only with true/pm. I will test it with true/pm. If it doesn't make any problem, i would prefer this, because it doesn't break Win 8 and older.
Dev for a better world :) > PHP for Web > AHK H for Local > with KISS (Keep it Short and Simple) on Win 10 Pro (Version 1903) x64
lexikos
Posts: 6668
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Sizing GUI-Elements: dpi dpiaware has to be set to "True/PM" to work on Win 8.1 and above

10 Feb 2016, 18:20

i would prefer this, because it doesn't break Win 8 and older.
Right, but it does break per-monitor scaling on Win 8.1 and newer.
guest3456
Posts: 2796
Joined: 09 Oct 2013, 10:31

Re: Sizing GUI-Elements: dpi dpiaware has to be set to "True/PM" to work on Win 8.1 and above

17 Feb 2016, 23:01

however, perhaps A_ScreenDPI should be renamed more accurately to A_SystemDPI

i think "Screen DPI" would be retrieved using GetDpiForMonitor()
https://msdn.microsoft.com/en-us/librar ... s.85).aspx

whereas i think GetDeviceCaps() returns the system dpi

lexikos
Posts: 6668
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Sizing GUI-Elements: dpi dpiaware has to be set to "True/PM" to work on Win 8.1 and above

18 Feb 2016, 00:12

Maybe, but as I said, I believe it corresponds to the primary screen. That is consistent with A_ScreenWidth/Height.

If it can return something other than the DPI of the primary screen, I would like confirmation.
guest3456
Posts: 2796
Joined: 09 Oct 2013, 10:31

Re: Sizing GUI-Elements: dpi dpiaware has to be set to "True/PM" to work on Win 8.1 and above

24 Sep 2019, 11:21

revisiting this thread
lexikos wrote:
10 Feb 2016, 03:34
AutoHotkey is not per-monitor DPI aware, so I'm not so sure that changing the value to true/pm is a good idea. Applications which are system DPI aware but not per-monitor DPI aware are automatically scaled when they are on a screen other than the primary one. Without this, AutoHotkey GUIs would scale according to the primary display and stay that size even if they are placed on a secondary display (which could be a vastly different size).
this is true in terms of AHK GUI's themselves. right now, with dpiaware=true, when i drag an AHK gui from my laptop at 125% to my external at 150%, Windows will scale up the AHK gui. if i change the manifest to true/pm, Windows will NOT scale up the gui, since it expects us to check the WM_DPICHANGED msg and rescale and redraw ourselves, like shown in this example:

https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows?redirectedfrom=MSDN#updating-existing-applications

HOWEVER..

there is a another side effect of marking the manifest as dpiaware=true/pm. that is, many win api calls return the coordinate values depending on the dpi awareness of the CALLER. that means, that with AHK being only dpiaware=true, it can sometimes get wrong results. it will get wrong results in this case: when an external monitor uses a different scaling/dpi than the primary monitor. the primary monitor defines the system dpi, which is what A_SCREENDPI reports. but if an external monitor has a different dpi/scaling, and you use AHK to query something like WinGetPos (GetWindowRect) or SysGet MonitorWorkArea, you get wrong scaled results for the size of windows and the work area of the external screen, which will be based on the system dpi, not based on the screen's individual dpi. if you compile and change the manifest to true/pm, then Windows returns correct results to those api calls.

lexikos
Posts: 6668
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Sizing GUI-Elements: dpi dpiaware has to be set to "True/PM" to work on Win 8.1 and above

05 Oct 2019, 04:55

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:
  1. Create the window while DPI awareness is on "system" (-2, already set via the manifest).
  2. 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.

Return to “Bug Reports”

Who is online

Users browsing this forum: No registered users and 11 guests