-DPIScale not working properly

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

-DPIScale not working properly

03 Oct 2018, 04:02

Edit: see "solution" https://autohotkey.com/boards/viewtopic ... 28#p243128

I was expecting the GUI option "-DPIScale" to tell Windows not to perform any DPI scaling for the GUI, and therefore produce the same appearance as if DPI scaling had been turned off.

On my Windows 10 test system, this appears to not be the case as fonts are still being enlarged, thus breaking the GUI and therefore my entire app.

Example:

Code: Select all

#Persistent
DPIScale := "+DPIScale"
Gui , New
Gui , %DPIScale%
Gui , Add , Text , x10 y10 w105, % "A_ScreenDPI: " . A_ScreenDPI
Gui , Add , Text , xp yp+30 w105, % "DPIScale: " . DPIScale
Gui , Add , Button , xp+100 yp+50 w85 h30 , My Button Text		
Gui , Add , Button , xp yp+50 w85 h30 , My Button Text		
Gui , Add , Button , xp yp+50 w85 h30 , My Button Text
Gui , Add , DateTime , xp+125 yp+50 w50 h24 vTime 1, HH:mm
GuiControl , , Time , % "20000101 " . A_Hour . A_Min . "00"
Gui , Show , w300
return

Image


I have tried the .exe's "Disable display scaling on high DPI settings", and it has no effect whatsoever, regardless of the -DPIScale setting, and regardless of the compatibility mode. It simply does nothing. Nothing at all.

Image


Surely something has got to be wrong here, otherwise what is the point of the -DPIScale setting if it's useless since it doesn't apply to fonts?

Is there some other DllCall or way of patching the executable to force it to turn off DPI scaling for all elements?

I am sure I remember some apps in the past I have used which completely ignored Windows' DPI scaling setting.



Unfortunately DPI scaling is becoming more ubiquitous since 4k monitors are becoming more common, so it's simply not good enough to let the app be broken on systems that use DPI scaling.

Absolutely gutted by this. Big project and now it's looking like all my GUI's have to be redone :thumbdown:
Last edited by pneumatic on 10 Oct 2018, 22:55, edited 5 times in total.
Guest

Re: -DPIScale not working properly

03 Oct 2018, 04:45

Not sure, but it may have to do with the fact you don't define the font-size?
So add something like Gui, Font, s10 to ensure "it knows" what font size to use, otherwise it will use the default system setting.
Code snippet to find the default size here:
https://github.com/hi5/dpi#guidefaultfont

(perhaps dpi() itself is useful although not what you want here as dpi() actually more or less doubles dpi-scaling to enlarge the GUI)
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: -DPIScale not working properly

03 Oct 2018, 05:30

So I notice some apps are apparently "DPI unaware", and in these cases Windows uses simple raster upscaling for the GUI and you get a blurry application window.

This proves it's possible for Windows to just render the GUI internally at normal 96 DPI (100% zoom) prior to raster upscaling the GUI.

This means it must also be possible for Windows to not perform the final raster upscale and just show it at 96dpi as if DPI scaling was turned off.

This is what I thought -DPIScale would do, but it doesn't.

Honestly I am quite shocked by this situation. Not being able to easily turn off DPI scaling is simply unacceptable. Having to manually readjust the position and size of every control for every DPI scaling level (125%, 150%, 175%, 200%) is simply crazy and could take weeks. Some controls do not support DPI scaling at all, such as XGraph. Expecting everyone to write their apps in a DPI aware fashion is not a reasonable expectation either. A simple "off" switch for DPI scaling is what is needed. Just don't DPI scale my app, please. Why is this so hard?
Last edited by pneumatic on 03 Oct 2018, 05:45, edited 1 time in total.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: -DPIScale not working properly

03 Oct 2018, 05:44

Might be on to something...

https://docs.microsoft.com/en-gb/window ... iawareness

Just need to figure out how to use it. Any help appreciated.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: -DPIScale not working properly

03 Oct 2018, 06:37

Ok so it seems the problem might be the Autohotkey process DPI scaling mode is still set to "System Aware", regardless of the -DPIScale setting.

In another thread I saw we can get current DPI scaling awareness by:

Code: Select all

ProcessDPIAwareness := {0: "PROCESS_DPI_UNAWARE", 1: "PROCESS_SYSTEM_DPI_AWARE", 2: "PROCESS_PER_MONITOR_DPI_AWARE"}
Process , Exist 
ProcessHandle := DllCall("OpenProcess", "UInt", 0x0400, "Int", false, "UInt", ErrorLevel, "Ptr") 
Result := DllCall("SHcore\GetProcessDpiAwareness", "Ptr", processHandle, "UIntP", dpiAwareness)
MsgBox % "Current DPI awareness: " . ProcessDPIAwareness[dpiAwareness]
DllCall("kernel32\CloseHandle", "Ptr", processHandle)
This returns the correct value (0: "PROCESS_DPI_UNAWARE") which is also what Sysinternals Process Explorer reports.

But I'm not having any luck setting it:

Code: Select all

Result := DllCall("Shcore\SetProcessDpiAwareness", "UInt" , 0)
MsgBox % "Result: " . Result
The result I get is -2147024809 = -0x7FF8FFFB = E_ACCESSDENIED according to this.
Last edited by pneumatic on 10 Oct 2018, 21:47, edited 5 times in total.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: -DPIScale not working properly

03 Oct 2018, 06:51

Guest wrote:Not sure, but it may have to do with the fact you don't define the font-size?
So add something like Gui, Font, s10 to ensure "it knows" what font size to use, otherwise it will use the default system setting.
Code snippet to find the default size here:
https://github.com/hi5/dpi#guidefaultfont

(perhaps dpi() itself is useful although not what you want here as dpi() actually more or less doubles dpi-scaling to enlarge the GUI)
Sorry I didn't see your post here.

Reducing the font size was the first thing I tried. I can compensate by lowering it, and it works to some extent, but elements like radio buttons, checkboxes and some other things are still at the wrong size with graphical glitches. It still requires a lot of work and checking at each DPI scale level to fix.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: -DPIScale not working properly

03 Oct 2018, 07:55

Code: Select all

Result := DllCall("User32\SetProcessDpiAwarenessContext", "UInt" , -1)
;-1 = DPI_AWARENESS_CONTEXT_UNAWARE 
;-2 = DPI_AWARENESS_CONTEXT_SYSTEM_AWARE
;-3 = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE  
;-4 = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
This returns 0 meaning it succeeded, but it didn't actually change the DPI awareness setting.

Now looking into adding DPI awareness to the ahk exe's manifest
https://autohotkey.com/boards/viewtopic.php?t=9954
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: -DPIScale not working properly

03 Oct 2018, 08:11

This is working on my Windows 10 system to force raster scaling.

Code: Select all

DllCall("User32\SetThreadDpiAwarenessContext", "UInt" , -1)
Must be called before any GUI's are created, and -DPIScale must be set also.

But I have no idea how reliable it is.

Also the scaling is very ugly. Can play around a bit with Gui,Font,q3/q4/q5, doesn't really help.

After reading the documentation it seems there is no way to actually force DPI scaling entirely off for an application such that it renders exactly at 1:1 pixel mapping as if the user had chosen 96dpi. But I still seem to recall using an app which did.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: -DPIScale not working properly

03 Oct 2018, 08:53

There is another way to force the raster upscaling through the registry by adding the following key:

On
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\YourScript.exe]
"dpiAwareness"=dword:00000000

Off
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\YourScript.exe]
"dpiAwareness"=dword:00000001

But I still suspect "dpi awareness" is not the same thing as "disable DPI scaling" from the Compatibility tab of the .exe's properties.

I suspect "disable DPI scaling" will actually force the window to render at 1:1 pixel mapping 96dpi, regardless of the Windows dpi setting.

That is the solution I want.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: -DPIScale not working properly

03 Oct 2018, 09:23

So I tested a bunch of apps with the "disable DPI scaling" checked on the .exe Properties Compatibility tab.

ProcessExplorer
Notepad++
Rufus
MSI Afterburner
WinRAR
AS SSD Benchmark

None of them can disable DPI scaling either. They are either raster scaled or the usual font enlargement method.

Except for Steam, which does revert to 1:1 pixel mapping. It's the only app I have which actually fully disables DPI scaling just for that app.

So it is possible to get 1:1 pixel mapping. There must be some way. Some function that will force it :)
Guest

Re: -DPIScale not working properly

03 Oct 2018, 09:44

I don't want to stop your investigation and trial & error process, but why would you actually want to disable it? Keeping it simply ensures your GUIs are actually more usable / user-friendly on 4K monitors - otherwise they will become very "small" which isn't helping anyone. A well designed GUI with dpi-scaling should work well on any monitor. You might actually be "hurting" your users by enforcing it to be "small"?

Apart from that you could use dpi() linked above to actually "enforce" scaling by simply the "setdpi" option, so if you would use 96 and you have a 125 dpi setting it would actually scale back that 125 to the 96 dpi values - but as noted in the dpi() you should define a font-size in the GUI otherwise it would still use the default font-size which would make the text on various controls too big no doubt.

Anyway, if you do find the answer do post it as someone may have the same requirement as you at some point. Good luck.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: -DPIScale not working properly

03 Oct 2018, 18:25

Guest wrote:I don't want to stop your investigation and trial & error process, but why would you actually want to disable it? Keeping it simply ensures your GUIs are actually more usable / user-friendly on 4K monitors - otherwise they will become very "small" which isn't helping anyone. A well designed GUI with dpi-scaling should work well on any monitor. You might actually be "hurting" your users by enforcing it to be "small"?
My app uses xgraph which doesn't seem to be at all compatible with any DPI scaling setting given how it does a bit block transfer to a text control, and my gui uses a test pattern which must be scaled using Imagemagick convert.exe, because Windows crappy scaling ruins the image, and I have some elements like text labels baked in as images rotated 90 degrees because ahk/windows doesn't support rotating text controls. I have 4 other GUIs which work okay with DPI scaling and I agree that is the preferred option than forcing 1:1 96dpi.
Guest wrote:Apart from that you could use dpi() linked above to actually "enforce" scaling by simply the "setdpi" option, so if you would use 96 and you have a 125 dpi setting it would actually scale back that 125 to the 96 dpi values - but as noted in the dpi() you should define a font-size in the GUI otherwise it would still use the default font-size which would make the text on various controls too big no doubt.
Yeah, but the problem is that when you reduce the width and height of elements like checkboxes , radio buttons, up/downs etc, anything less than the default size causes weird artefacts like the top line of a checkbox becomes missing or disappears entirely when I change the Gui background colour, or its tick becomes all aliased and weird because it's at some non-integer scaling level (ends up looking like nearest-neighbour scaling) or just the vertical position being offset wrongly with respect to its buddy text. It still needs a lot of manually adjustment for each DPI level.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: -DPIScale not working properly

03 Oct 2018, 19:15

So with Resource Hacker I've modified the manifest of MyCompiledScript.exe according to these instructions

https://msdn.microsoft.com/en-gb/C94883 ... D9B869A5E2

But it simply refuses to change the value - Process Explorer reports MyCompiledScript.exe is still at "System Aware" dpi awareness regardless of what I put in the manifest.

late edit: it appears the manifest setting does work, you just need to restart windows after changing it.
Last edited by pneumatic on 10 Oct 2018, 21:49, edited 2 times in total.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: -DPIScale not working properly

03 Oct 2018, 19:17

Another fix I tried was this one which apparently patches winapi calls in realtime to force a dpiscaling mode, but it didn't work.

https://superuser.com/questions/1002267 ... es-screens
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: -DPIScale not working properly

03 Oct 2018, 19:36

[deleted]
Last edited by pneumatic on 10 Oct 2018, 21:50, edited 1 time in total.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: -DPIScale not working properly

03 Oct 2018, 20:01

[deleted]
Last edited by pneumatic on 10 Oct 2018, 21:50, edited 1 time in total.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: -DPIScale not working properly

03 Oct 2018, 21:28

I suspect it is impossible in Windows 10 to force an app to 1:1 pixel mapped 96dpi when Windows is set to something other than 96dpi.

I say this because with the aforementioned regkey, Process Explorer is reporting that all dpi scaling modes are being applied:

Code: Select all

"Unaware"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\YourScript.exe]
"dpiAwareness"=dword:00000000

"System Aware"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\YourScript.exe]
"dpiAwareness"=dword:00000001

"Per-Monitor Aware"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\YourScript.exe]
"dpiAwareness"=dword:00000002

possibly "Per-Monitor Aware V2"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\YourScript.exe]
"dpiAwareness"=dword:00000003
As mentioned previously, these can be applied in script with

Code: Select all

Result := DllCall("User32\SetProcessDpiAwarenessContext", "UInt" , -1)
;-1 = DPI_AWARENESS_CONTEXT_UNAWARE 
;-2 = DPI_AWARENESS_CONTEXT_SYSTEM_AWARE
;-3 = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE  
;-4 = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
Although it seems -DPIScale is required as well.

And I believe the reason the other SetProcessDpiAwareness function doesn't work is because ahk itself already sets the DPI awareness once at launch and according to MSDN it cannot be set more than once, which is consistent with the error code in Result.
Last edited by pneumatic on 10 Oct 2018, 21:51, edited 2 times in total.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: -DPIScale not working properly

03 Oct 2018, 22:00

Oh and there's also more regkeys that affect it

Code: Select all

reg add "HKCU\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" /v "C:\MyScript\MyScript.exe" /t REG_SZ /d "~ HIGHDPIAWARE"
reg add "HKCU\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" /v ""C:\MyScript\MyScript.exe" /t REG_SZ /d "~ DPIUNAWARE"
reg add "HKCU\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" /v "C:\MyScript\MyScript.exe" /t REG_SZ /d "~ ~GDIDPISCALING DPIUNAWARE"
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: -DPIScale not working properly

04 Oct 2018, 18:30

[deleted]
Last edited by pneumatic on 10 Oct 2018, 21:51, edited 2 times in total.
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: -DPIScale not working properly

10 Oct 2018, 07:08

After about 40 hours of research, I believe I finally understand how to actually control windows DPI scaling!

I will try and summarise what I have learned so that it may help other people who may have become as confused as I have been.

First thing I will note is that if windows dpi scaling is set to anything other than 100%, you will not be able to force your app to display as if dpi scaling was set to 100% just for your app. I thought this feature existed, but it doesn't. You can attempt to fudge it that way, but it will look ugly and many elements will not display right (more about this later).

So applications can contain a "dpi aware" flag which tells Windows whether they are aware of the current Windows dpi setting. The flag can be set in multiple places and different things happen depending on where you set it:

1. In the app's manifest (editable with Resource Hacker). The flag looks like this and goes inside the <assembly></assembly> tags:
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/W ... </dpiAware>
</windowsSettings>
</application>

A reboot is required for the manifest changes to take effect.

2. By calling SetProcessDpiAwareness (8.1+), or SetProcessDpiAwarenessContext (10+), or SetThreadDpiAwarenessContext (10+) function inside the program itself. The first 2 are global settings and do the same thing, the last is a "per thread" setting which allows you to use different setting for different GUI's. The first 2 will be overridden by the app manifest setting, and can otherwise only be set once. There is also an older SetProcessDPIAware function which is compatible with Vista+, however on my Windows 7 system it does't work properly as it doesn't cause A_ScreenDPI to receive current setting. Note: dllcalls are case-sensitive.

3. By setting the following reg key
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\YourScript.exe]
"dpiAwareness"=dword:00000001
On my system this overrides (1) and (2), however the setting is only compatible with Windows 10.

Prior to Windows 10, there is only one flag setting (true/false). Not setting the flag has the same effect as setting it to false, but the reverse is not true. After Windows 10 there are 2 extra flag settings (per monitor/per monitor v2).

For now I will ignore the per monitor settings and just explain what happens with the true/false setting.

If the flag is false (or not present), windows will use bitmap upscaling. This looks like upscaling standard definition video to high definition. Blurry and soft. With better upscaling filters it could have looked half decent, but I digress. In this mode you will not need to reposition any of your GUI elements, everything will be rendered at 1:1 100% zoom level and then finally the entire window raster will receive the bitmap upscale, so everything gets enlarged by exactly the same proportion relative to eachother. But it looks so soft and blurry I doubt you will want to use it.

If the flag is true, windows will not do bitmap scaling and leave it up to the app itself to get all the positioning right, since now all the gui elements such as text controls, check boxes, drop down lists etc. have all gotten bigger due to the windows dpi setting, and many controls will need to be adjusted manually by you, and elements cannot be made smaller, as reducing the controls below their default size results in horrible artefacts like nearest-neighbour type aliasing or bits of controls just not getting rendered at all. This is why the Autohotkey -DPIScale setting fails, it tries to shrink things back down for you, but because Windows doesn't play nice with lower than default dimensions for many controls it ends up looking nasty. You will get the same result using that mentioned dpi() library as it basically works the same way. To get a properly drawn GUI you will need to use A_ScreenDPI to adjust your control position/dimensions. This can be tricky because some things like window positions will be in absolute pixel values, others like width and height will be in relative dimensions so you will need to either multiply or divide by (A_ScreenDPI/96). Also you will need to check all levels of windows dpi scaling, as for example some of my text controls are cut off only at a windows dpi setting of 175%.


Autohotkey's executable is set to true in its app manifest.

The value can be checked with Process Explorer, but only in Windows 10.


Now we know what the true/false setting does, there are still other places where it will be overridden:

a. The exe's properties' compatibility tab's "disable display scaling on high dpi settings". Its regkey is in HKCU\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers in case you need to set it in your script. This setting confused me for so long, but here's what it does when ticked: if the app's dpi awareness flag is false or not present, Windows will force the app to a true setting. On Windows 10 it actually forces it to "per monitor", but the effect on a single monitor is the same. Since this checkbox is unticked by default, the default behaviour is that apps which have their flag set to false or unset , i.e lots of apps, will receive the crappy bitmap upscaling.

b. On Windows 7, there is a setting in the Control Panel called "use Windows XP style DPI scaling". Default on my system was ticked. This appears to effectively tick "disable display scaling on high dpi settings" for all apps. Its regkey appears to be HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM] "UseDpiScaling"=dword:00000000

So what is the best option for your ahk gui?

The answer to me is simple: bitmap upscaling looks like crap, -dpiscale looks like crap, therefore I just leave ahk's app manifest as-is (flag=true) and reposition my controls manually using A_ScreenDPI. It took a lot of effort but in the end it was worth it.

edit: forgot to mention, on 4k displays a dpi scaling setting that seems to be anecdotally popular is 200%. If windows bitmap upscaling is used, this can look better because at whole integer levels of scaling (200%, 300% etc) windows uses a different "nearest-neighbour" scaling mode which results in 1 pixel becoming exactly 4 pixels and the original look and sharpness is preserved. Therefore it may be desirable to allow the user to manually set the app's dpi flag to false to achieve this. Achieving this on windows 10 is easy - just use (3) and restart your app. Alternatively, you could delete the flag in the app manifest (not false - delete) and then control it with (2) or (a). On windows 7, because (3) doesn't work, you will have to delete the flag in the app manifest (not false - delete) which then opens up the possibility of controlling the flag by (a) or (b), preferably (a) as it doesn't require a restart.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: gabelynn1, Spawnova, vvdee and 385 guests