Jump to content

making your compiled application DPI Aware


Recommended Posts

Since this thread is getting long, I thought I'd copy all the relevant information to the 1st post.

Three different types of DPI Aware'ness are made available from Microsoft.
1. System Aware (introduced with Windows Vista)
2. Per Monitor Aware (Introduced with Windows 8.1)
3. Per Monitor Aware V2 (Introduced with Windows 10 1703)

Method #1 - Programmatically  - Only available for Windows 8.1 and 10

; For values available to Windows 10 users - https://docs.microsoft.com/en-gb/windows/win32/hidpi/dpi-awareness-context

If @OSVersion = 'WIN_10' Then DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext" , "HWND", "DPI_AWARENESS_CONTEXT" -2)
If @OSVersion = 'WIN_81' Then DllCall("User32.dll", "bool", "SetProcessDPIAware")

;  *** Microsoft warns that these entries need to be executed before the GUI is created ***
;This is the only reason they recommend using the Manifest method over the programmatic method

When using the above code, Windows 10 and 8.1 will recognize your compiled app as DPI System Aware.

 

Method #2 - using Manifest entries - Available for all Windows starting with Vista, up to current and future releases of Windows.
Using #AutoIt3Wrapper_Res_HiDpi=Y will set your compiled app as DPI System Aware (Win Vista, 7, 8.1, 10).

 For Windows 8.1, using the Manifest method is the only way to have Per Monitor Aware.
Follow this link to learn how to create Per Monitor Aware and Per Monitor Aware V2 apps for Windows 10.  Learn how here
      *** This is the Microsoft recommended method of creating a DPI Aware application ***

 

*** If you choose either method above, you will still need to deal with the GUI itself and all of its Controls. ***
Being DPI Aware only deals with text. The simplest method to achieve this is to adapt the code below into your script.

Global $iScale = RegRead("HKCU\Control Panel\Desktop\WindowMetrics", "AppliedDPI") / 96
GUICreate("DPI test", 200 * $iScale, 100 * $iScale)
GUICtrlCreateButton("Ima Button", 35 * $iScale, 115 * $iScale, 55 * $iScale, 21 * $iScale)

; For reference, I have a single App with 579 occurances of $iScale
; Also know; I've had to make some small x,y adjustments to keep the GUI looking good but nothing drastic.
; Be sure to test your compiled app at 125% and at 200%. This is how I found small descrepancies requiring x,y adjustments.

more importantly, this registry value can be had, programmatically and in my opinion, more efficiently
With 4k monitors becoming a norm, this should set you well on your way to creating a DPI Aware applications that looks good on any system.

Edited by Shark007
Link to comment
Share on other sites

To followup further, I have even tried editing  AutoIt3Wrapper.au3 to place the following into the manifest

<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
        <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
        <dpiAware>true</dpiAware>
    </windowsSettings>
</application>
</assembly>

but still no joy. It does the same as before I edited it.

Link to comment
Share on other sites

I know you won't like it, but to resolve all those problems, change resolution to 1600 x 900 or 1440 x 900 depending how it is managed by the OS. And put Windows scale at 100%. Problem solved, at least for many of us...

Link to comment
Share on other sites

20 hours ago, Shark007 said:

It resizes the text OK but not the GUI.

System will not resize GUI and controls according to DPI scaling. You must do this manually. For example

; current value of primary monitor DPI is stored in the registry:
; HKCU\Control Panel\Desktop\WindowMetrics, AppliedDPI
; need to relogin after DPI changed to update this value

$Scale = RegRead("HKCU\Control Panel\Desktop\WindowMetrics", "AppliedDPI") / 96

DllCall("User32.dll", "bool", "SetProcessDPIAware")

GUICreate("DPI test", 200 * $Scale, 100 * $Scale)
GUICtrlCreateButton("test button text", 10 * $Scale, 10 * $Scale, 80 * $Scale, 25 * $Scale)
GUICtrlCreateCombo("test combo text", 10 * $Scale, 40 * $Scale, 100 * $Scale, 25 * $Scale)
GUICtrlCreateLabel("test label text", 10 * $Scale, 70 * $Scale, 70 * $Scale, 25 * $Scale)
GUISetState()
Do
Until GUIGetMsg() = -3

 

Edited by InnI
Link to comment
Share on other sites

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

After much testing, I ended up using using the Microsoft recommended manifest entries which are applied properly using
#AutoIt3Wrapper_Res_HiDpi=Y resulting in the compiled app being recognized as DPI System Aware.
Then I combined this with similar coding as depicted below to adjust the entire GUI and all of its controls.

$Scale = RegRead("HKCU\Control Panel\Desktop\WindowMetrics", "AppliedDPI") / 96
GUICreate("DPI test", 200 * $Scale, 100 * $Scale)

Everything in my app(s) is functioning well (with some slight x,y adjustments to keep it beautiful) in 32 and 64bit compiles.

Thanks to all those offering helpful advice!

Edited by Shark007
code typo
Link to comment
Share on other sites

22 hours ago, Shark007 said:

So it seems I just needed more complete syntax.

For Windows 10... working code

If @OSVersion = 'WIN_10' Then DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext" , "HWND", "DPI_AWARENESS_CONTEXT" -2)

With the available values documented HERE

 

Just to complete my initial request of solving DPI Awareness programmatically, even though I decided t to use the manifest method instead,

below is working code for Windows 8.1

If @OSVersion = 'WIN_81' Then DllCall("User32.dll", "bool", "SetProcessDPIAware")

The above code will make your compiled app DPI System Aware on Windows 8.1 systems. It does not have any parameters like the Windows 10 version has.

Link to comment
Share on other sites

Can you please test this code on different OS builds?

;Coded by UEZ build 2019-08-05 beta

#AutoIt3Wrapper_Res_HiDpi=Y

#include <Array.au3>
#include <GUIConstantsEx.au3>
#include <GDIPlus.au3>
#include <WinAPIGdiDC.au3>
#include <WinAPISysWin.au3>

_GDIPlus_Startup()

; enum _PROCESS_DPI_AWARENESS -> https://msdn.microsoft.com/en-us/library/windows/desktop/dn280512(v=vs.85).aspx
Global Enum $DPI_AWARENESS_INVALID = -1, $PROCESS_DPI_UNAWARE = 0, $PROCESS_SYSTEM_DPI_AWARE, $PROCESS_PER_MONITOR_DPI_AWARE

Global Enum $Context_UnawareGdiScaled = -5, $Context_PerMonitorAwareV2, $Context_PerMonitorAware, $Context_SystemAware, $Context_Unaware

; enum _MONITOR_DPI_TYPE
Global Enum $MDT_EFFECTIVE_DPI = 0, $MDT_ANGULAR_DPI, $MDT_RAW_DPI
Global Const $MDT_DEFAULT = $MDT_EFFECTIVE_DPI

Global Const $WM_DPICHANGED = 0x02E0

Global $dpiScaledX, $dpiScaledY, $aCtrlFS[5][2]

If Not @Compiled Then _WinAPI_SetDPIAwareness()

Example1()

_GDIPlus_Shutdown()

Func Example1() ;example by alpines
    Local Const $GUI_CLIENT_WIDTH = 314
    Local Const $GUI_CLIENT_HEIGHT = 130

    Local $hGUI = GUICreate("Example 1", $GUI_CLIENT_WIDTH, $GUI_CLIENT_HEIGHT, -1, -1)
    GUISetFont(12, 400, 0, "Times New Roman")

    $aCtrlFS[0][0] = GUICtrlCreateLabel("Label1", 16, 16, 36, 21)
    $aCtrlFS[0][1] = 10
    GUICtrlSetFont(-1, $aCtrlFS[0][1], 400, 0, "Times New Roman", 5)
    GUICtrlSetBkColor(-1, 0x3399FF)
    GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

    $aCtrlFS[1][0] = GUICtrlCreateLabel("Label2", 64, 16, 36, 21)
    $aCtrlFS[1][1] = 10
    GUICtrlSetFont(-1, $aCtrlFS[1][1], 400, 0, "Times New Roman", 5)
    GUICtrlSetBkColor(-1, 0x3399FF)
    GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

    $aCtrlFS[2][0] = GUICtrlCreateLabel("Label3", 112, 16, 36, 21)
    $aCtrlFS[2][1] = 10
    GUICtrlSetFont(-1, $aCtrlFS[2][1], 400, 0, "Times New Roman", 5)
    GUICtrlSetBkColor(-1, 0x3399FF)
    GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

    $aCtrlFS[3][0] = GUICtrlCreateInput("Input1", 160, 16, 137, 21)
    $aCtrlFS[3][1] = 10
    GUICtrlSetFont(-1, $aCtrlFS[3][1], 400, 0, "Times New Roman", 5)
    GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

    $aCtrlFS[4][0] = GUICtrlCreateButton("Close", 16, 48, 283, 65)
    $aCtrlFS[4][1] = 10
    GUICtrlSetFont(-1, $aCtrlFS[4][1], 400, 0, "Times New Roman", 5)
    GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

    GUISetState(@SW_SHOW, $hGUI)
    GUIRegisterMsg($WM_DPICHANGED, "WM_DPICHANGED")

    While True
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE, $aCtrlFS[4][0]
                GUIDelete($hGUI)
                ExitLoop
        EndSwitch
    WEnd
EndFunc   ;==>Example1

Func WM_DPICHANGED($hWnd, $iMsg, $wParam, $lParam)
    Local $tRECT = DllStructCreate($tagRECT, $lParam)
    Local $iDPIX = _WinAPI_LoWord($wParam), $iDPIY = _WinAPI_HiWord($wParam)
    $dpiScaledX = $iDPIX / 96
    $dpiScaledY = $iDPIY / 96
    Local $iW = $tRECT.right - $tRECT.left, $iH = $tRECT.bottom - $tRECT.top
    Local $aWPos = WinGetPos($hWnd)
    _WinAPI_SetWindowPos($hWnd, 0, $aWPos[0], $aWPos[1], $iW, $iH, BitOR($SWP_NOZORDER, $SWP_NOACTIVATE))
    UpdateControlFontSize()
EndFunc   ;==>WM_DPICHANGED

Func UpdateControlFontSize()
    Local $i
    For $i = 0 To UBound($aCtrlFS) - 1
        GUICtrlSetFont($aCtrlFS[$i][0], $aCtrlFS[$i][1] * $dpiScaledX)
    Next
EndFunc   ;==>UpdateControlFontSize

Func _GDIPlus_GraphicsGetDPIRatio($iDPIDef = 96)
    Local $hGfx = _GDIPlus_GraphicsCreateFromHWND(0)
    If @error Then Return SetError(1, @extended, 0)
    Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipGetDpiX", "handle", $hGfx, "float*", 0)
    If @error Then Return SetError(2, @extended, 0)
    Local $iDPI = $aResult[2]
    _GDIPlus_GraphicsDispose($hGfx)
    Local $aResults[2] = [$iDPIDef / $iDPI, $iDPI / $iDPIDef]
    Return $aResults
EndFunc   ;==>_GDIPlus_GraphicsGetDPIRatio

Func _WinAPI_GetDpiForMonitor($dpiType = $MDT_DEFAULT, $iDPIDef = 96)
    Local $aMonitors = _WinAPI_EnumDisplayMonitors()
    Local $x, $y
    Local $aRet = DllCall("Shcore.dll", "long", "GetDpiForMonitor", "long", $aMonitors[1][0], "int", $dpiType, "uint*", $x, "uint*", $y)
    If @error Or Not IsArray($aRet) Then Return SetError(1, 0, 0)
    Local $aDPI[2] = [$iDPIDef / $aRet[3], $aRet[3] / $iDPIDef]
    Return $aDPI
EndFunc   ;==>_WinAPI_GetDpiForMonitor

Func _WinAPI_SetDPIAwareness($hGUI = 0)
    Switch @OSBuild
        Case 6000 To 9199
            If Not DllCall("user32.dll", "bool", "SetProcessDPIAware") Then Return SetError(1, 0, 0)
            Return 1
        Case 9200 To 13999
            _WinAPI_SetProcessDpiAwareness($PROCESS_PER_MONITOR_DPI_AWARE)
            If @error Then Return SetError(2, 0, 0)
            Return 1
        Case @OSBuild > 13999
            #cs
                Context_Unaware = ((DPI_AWARENESS_CONTEXT)(-1)),
                Context_SystemAware = ((DPI_AWARENESS_CONTEXT)(-2)),
                Context_PerMonitorAware = ((DPI_AWARENESS_CONTEXT)(-3)),
                Context_PerMonitorAwareV2 = ((DPI_AWARENESS_CONTEXT)(-4)),
                Context_UnawareGdiScaled = ((DPI_AWARENESS_CONTEXT)(-5))
            #ce
            _WinAPI_SetProcessDpiAwarenessContext($Context_PerMonitorAwareV2, $hGUI)
            If @error Then Return SetError(3, @error, 0)
            Return 1
    EndSwitch
    Return -1
EndFunc   ;==>_WinAPI_SetDPIAwareness

Func _WinAPI_SetProcessDpiAwareness($DPIAware) ;https://docs.microsoft.com/en-us/windows/desktop/api/shellscalingapi/nf-shellscalingapi-setprocessdpiawareness
    Local $aResult = DllCall("Shcore.dll", "long", "SetProcessDpiAwareness", "int", $DPIAware)
    If @error Then Return SetError(1, 0, 0)
    Return 1
EndFunc   ;==>_WinAPI_SetProcessDpiAwareness

Func _WinAPI_SetProcessDpiAwarenessContext($DPIAwareContext = $Context_PerMonitorAware, $hGUI = 0, $iMode = 3) ;https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-setprocessdpiawarenesscontext
    $DPIAwareContext = ($DPIAwareContext < -5) ? -5 : ($DPIAwareContext > -1) ? -1 : $DPIAwareContext
    $iMode = ($iMode < 1) ? 1 : ($iMode > 3) ? 3 : $iMode
    Switch $iMode
        Case 1
            Local $hDC = _WinAPI_GetDC($hGUI)
            Local $aResult1 = DllCall("user32.dll", "int", "GetDpiFromDpiAwarenessContext", "ptr", $hDC)
            If @error Or Not IsArray($aResult1) Then Return SetError(11, 0, 0)
            _WinAPI_ReleaseDC(0, $hDC)
            Local $aResult = DllCall("user32.dll", "Bool", "SetProcessDpiAwarenessContext", "int", $aResult1[0] + $DPIAwareContext)
            If @error Or Not IsArray($aResult) Then Return SetError(12, 0, 0)
        Case 2
;~          If Not $hGUI Then $hGUI = WinGetHandle(AutoItWinGetTitle())
            Local $aResult2 = DllCall("user32.dll", "int", "GetWindowDpiAwarenessContext", "ptr", $hGUI)
            If @error Or Not IsArray($aResult2) Then Return SetError(21, 0, 0)
            Local $aResult = DllCall("user32.dll", "Bool", "SetProcessDpiAwarenessContext", "int", $aResult2[0] + $DPIAwareContext)
            If @error Or Not IsArray($aResult) Then Return SetError(22, 0, 0)
        Case 3
            Local $aResult31 = DllCall("user32.dll", "ptr", "GetThreadDpiAwarenessContext")
            If @error Or Not IsArray($aResult31) Then Return SetError(31, 0, 0)
            Local $aResult32 = DllCall("user32.dll", "int", "GetAwarenessFromDpiAwarenessContext", "ptr", $aResult31[0])
            If @error Or Not IsArray($aResult32) Then Return SetError(32, 0, 0)
            Local $aResult = DllCall("user32.dll", "Bool", "SetThreadDpiAwarenessContext", "int", $aResult32[0] + $DPIAwareContext)
            If @error Or Not IsArray($aResult) Then Return SetError(33, 0, 0)
    EndSwitch

    Return 1
EndFunc   ;==>_WinAPI_SetProcessDpiAwarenessContext

Does it work properly?

 

Here the manifest I used:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >

	<!-- Identify the application security requirements. -->
	<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
		<security>
			<requestedPrivileges>
				<requestedExecutionLevel
 					level="asInvoker"
					uiAccess="false"
				/>
			</requestedPrivileges>
		</security>
	</trustInfo>

	<!-- Identify the application dependencies. -->
	<dependency>
		<dependentAssembly>
			<assemblyIdentity
				type="win32"
				name="Microsoft.Windows.Common-Controls"
				version="6.0.0.0"
				language="*"
				processorArchitecture="*"
				publicKeyToken="6595b64144ccf1df"
			/>
		</dependentAssembly>
	</dependency>

	<asmv3:application>
		<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
		      <dpiAware>true/pm</dpiAware>
		</asmv3:windowsSettings>
		<asmv3:windowsSettings xmlns="https://schemas.microsoft.com/SMI/2016/WindowsSettings">
		      <dpiAwareness>PerMonitorV2, system</dpiAwareness>
		</asmv3:windowsSettings>
		<asmv3:windowsSettings xmlns="https://schemas.microsoft.com/SMI/2017/WindowsSettings">
		      <gdiScaling>true</gdiScaling>
		</asmv3:windowsSettings>
	</asmv3:application>

	<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
		<application>
			<!--The ID below indicates application support for Windows Vista -->
			<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
			<!--The ID below indicates application support for Windows 7 -->
			<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
			<!--The ID below indicates application support for Windows 8 -->
			<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
			<!--The ID below indicates application support for Windows 8.1 -->
			<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
			<!--The ID below indicates application support for Windows 10 -->
			<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
		</application>
	</compatibility>
</assembly>

 

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

UEZ, the code does not compile. I get several failures of undefined functions. (and this was before my morning coffee!)

this is what happens before coffee! It was not a complete copy/paste of the code causing the failures. I'll get back to you later after testing of diff systems.

 

To toss some credit in your direction, I am using some code that originated with you and was later modified by mLipok and then by me.
EDIT... and streamlined further by UEZ!  (and then some more editing by me)

I use it to replace the reg query, 

$iScale = RegRead("HKCU\Control Panel\Desktop\WindowMetrics", "AppliedDPI") /96
Func DPIRatio()
    _GDIPlus_Startup()
    Local $hGfx = _GDIPlus_GraphicsCreateFromHWND(0)
    If @error Then Return SetError(1, @extended, 0)
    Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipGetDpiX", "handle", $hGfx, "float*", 0)
    If @error Then Return SetError(2, @extended, 0)
    _GDIPlus_GraphicsDispose($hGfx)
    _GDIPlus_Shutdown()
    Return $aResult[2] / 96
EndFunc   ;==>DPIRatio

$iScale = DPIRatio()

In my testing, the Return'd value is the same as the value in the RegRead. I've tested this on Windows 7 , 8.1 and 10.

Edited by Shark007
Link to comment
Share on other sites

UEZ, results on Diff systems.

Windows 7, other than my eyes, I cannot test for DPI Awareness. On Windows 8.1 and Windows 10, it tested as DPI System Aware

Windows 7 - 125% scaling

Windows7 - 125% scaling.jpg

Windows 8.1 - 150% scaling

Windows8 - 150% scaling- DPI System aware.jpg

Windows 10 - 200% scaling

Windows10 - 200% scaling - DPI System aware.jpg

Edited by Shark007
Link to comment
Share on other sites

@Shark007 Thanks for testing but that's odd. On my Win10 v1803 b17134 it works as expected on my 3 screens whereas I set the right one (not primary) to 150%.

 

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

@Shark007 Can you test the compiled code from attached file please? It it the same code as above but I've modified the manifest which will be created by AutoIt3Wrapper.exe. 

Thx.

 

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

@Shark007Ok, can you try this one please?

 

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...