Jump to content

Recommended Posts

Posted
4 hours ago, WildByDesign said:

Do you think that it would be worthwhile to have a DPI menu in the menubar of the Example

this would only be useful as a tool for testing.
My opinion is not to introduce it at all, and if you want to use it as a tool better keep it separate
The script is already complicated why complicate it further?

I know that I know nothing

Posted
#AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
#include <WindowsStylesConstants.au3>
#include <GuiEdit.au3>
#include <FontConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsNotifsConstants.au3>
#include <WindowsConstants.au3>

#include "PMv2.au3"
_PM_Init()

Global $g_hStatus, $g_hGui, $g_idAboutOK = Null

Example()

Func Example()
    Local Const $fDefFontSize = 8.5, $fDefPixel = $fDefFontSize * 96 / 72
    Local $iCtrlSpaceV = 10
    Local $sAppName = "High DPI Scaling Example"
    Local $hGUI = GUICreate($sAppName, 400, 300)
    ;Local $hGUI = GUICreate($sAppName, 330, 260, -1, -1, BitOR($GUI_SS_DEFAULT_GUI,$WS_MAXIMIZEBOX,$WS_SIZEBOX,$WS_THICKFRAME,$WS_TABSTOP))
    $g_hGui = $hGUI

    ; Get DPI Awareness Context
    Local $hContext = _WinAPI_GetWindowDpiAwarenessContext($hGUI)

    Local $hStatus = _GUICtrlStatusBar_Create($hGUI)
    $g_hStatus = $hStatus

    Local $idMnu_File = GUICtrlCreateMenu("&File")
    Local $idMnu_View = GUICtrlCreateMenu("View")
    Local $idMnu_Help = GUICtrlCreateMenu("Help")
    Local $idMni_Info = GUICtrlCreateMenuItem("About", $idMnu_Help)
    Local $idMni_Exit = GUICtrlCreateMenuItem("Exit", $idMnu_File)
    Local $idStatusShow = GUICtrlCreateMenuItem("Status Bar", $idMnu_View)
    GUICtrlSetState($idStatusShow, $GUI_CHECKED)

    ; Create new scaled font
    Local Static $hCtrlFont = _WinAPI_CreateFont(-Round($fDefPixel), 0, 0, 0, $FW_NORMAL, False, False, False, _
            $DEFAULT_CHARSET, $OUT_DEFAULT_PRECIS, $CLIP_DEFAULT_PRECIS, $PROOF_QUALITY, $DEFAULT_PITCH, "Segoe UI")

    Local $idTestName = GUICtrlCreateLabel("Test Label ", 15, 20, 180, 24)
    Local $aPos = ControlGetPos($hGUI, "", $idTestName)
    Local $iPrev = $aPos[1] + $aPos[3] + $iCtrlSpaceV
    GUICtrlSetFont(-1, 10, $FW_NORMAL, $GUI_FONTNORMAL, "Comic Sans MS")

    Local $idTestInput = GUICtrlCreateInput("Test Input", 15, $iPrev, 260, 22)
    $aPos = ControlGetPos($hGUI, "", $idTestInput)
    $iPrev = $aPos[1] + $aPos[3] + $iCtrlSpaceV
    GUICtrlSetFont(-1, 8.5, $FW_NORMAL, $GUI_FONTUNDER, "Segoe UI")

    Local $idCheckbox1 = GUICtrlCreateCheckbox("Test Checkbox1 ", 15, $iPrev, -1, -1)
    $aPos = ControlGetPos($hGUI, "", $idCheckbox1)
    $iPrev = $aPos[1] + $aPos[3] + $iCtrlSpaceV
    GUICtrlSetFont(-1, 8.5, $FW_NORMAL, $GUI_FONTSTRIKE, "Segoe UI")

    Local $idCheckbox2 = GUICtrlCreateCheckbox("Test Checkbox2 ", 15, $iPrev, -1, -1)
    $aPos = ControlGetPos($hGUI, "", $idCheckbox2)
    $iPrev = $aPos[1] + $aPos[3] + $iCtrlSpaceV
    GUICtrlSetFont(-1, 8.5, $FW_NORMAL, $GUI_FONTNORMAL, "Segoe UI")

    Local $hEditCtrl = _GUICtrlEdit_Create($hGUI, "This was created with standard UDF" & @CRLF & "function _GUICtrlEdit_Create().", 15, $iPrev, 300, 64)
    _SendMessage($hEditCtrl, $WM_SETFONT, $hCtrlFont, True)

    ; Set statusbar text
    Local $fDpiScale = __PM_GetWindowDPI($g_hGui) / 96
    Local $aParts[3] = [72 * $fDpiScale, 200 * $fDpiScale, -1]
    _GUICtrlStatusBar_SetParts($g_hStatus, $aParts)
    _GUICtrlStatusBar_SetText($g_hStatus, __PM_GetWindowDPI($g_hGui) & "dpi", 0)
    _GUICtrlStatusBar_SetText($g_hStatus, (__PM_GetWindowDPI($g_hGui) / 96) * 100 & "% scaling", 1)
    _GUICtrlStatusBar_SetText($g_hStatus, _GetDpiAwarenessContextName($hContext), 2)

    ; Register custom window message for the purpose of updating statusbar text
    GUIRegisterMsg($__PM_WM_DPICHANGED, "_WM_DPICHANGED")

    _PM_Scale($hGUI)
    GUISetState(@SW_SHOW, $hGUI)

    While 1
        Switch GUIGetMsg()
            Case $idStatusShow
                Local $bStatusShow
                If $hStatus Then
                    $bStatusShow = _WinAPI_IsWindowVisible($hStatus)
                    Switch $bStatusShow
                        Case 1
                            _GUICtrlStatusBar_ShowHide($hStatus, @SW_HIDE)
                            GUICtrlSetState($idStatusShow, $GUI_UNCHECKED)
                        Case 0
                            _GUICtrlStatusBar_ShowHide($hStatus, @SW_SHOW)
                            GUICtrlSetState($idStatusShow, $GUI_CHECKED)
                    EndSwitch
                EndIf

            Case $idMni_Info
;~              MsgBox($MB_TOPMOST, "PMv2", "PMv2 Version: " & _PM_Version() & @CRLF)
                _AboutGUI()

            Case $GUI_EVENT_CLOSE, $idMni_Exit, $g_idAboutOK
                If WinGetTitle("[ACTIVE]") = "About PMv2" Then
                    GUIDelete(WinGetHandle("About PMv2"))
                    ContinueLoop
                EndIf

                ExitLoop

        EndSwitch
    WEnd

    If $hCtrlFont Then _WinAPI_DeleteObject($hCtrlFont)
    GUIDelete($hGUI)
EndFunc   ;==>Example

Func _WM_DPICHANGED($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam

    ; Obtain new DPI value (raw)
    Local $iDPI = _WinAPI_LoWord($wParam)

    Local $hContext = _WinAPI_GetWindowDpiAwarenessContext($hWnd)
    Local $fDpiScale = $iDPI / 96
    Local $aParts[3] = [72 * $fDpiScale, 200 * $fDpiScale, -1]
    _GUICtrlStatusBar_SetParts($g_hStatus, $aParts)
    _GUICtrlStatusBar_SetText($g_hStatus, $iDPI & "dpi", 0)
    _GUICtrlStatusBar_SetText($g_hStatus, ($iDPI / 96) * 100 & "% scaling", 1)
    _GUICtrlStatusBar_SetText($g_hStatus, _GetDpiAwarenessContextName($hContext), 2)

    Return 0
EndFunc   ;==>_WM_DPICHANGED

Func _AboutGUI()
    Local $hGUIAbout = GUICreate("About PMv2", 240, 100)
    GUICtrlCreateLabel("PMv2 Version: " & _PM_Version(), 10, 20, 220, 25)
    GUICtrlSetFont(-1, 12, $FW_NORMAL, $GUI_FONTNORMAL, "Comic Sans MS")
    $g_idAboutOK = GUICtrlCreateButton("OK", 70, 70, 100, 25)
    GUISetState(@SW_SHOW, $hGUIAbout)
EndFunc   ;==>_AboutGUI

 

working case 

  1. run the configured script
  2. call About
  3. move the About window to another DPI scaled monitor and close it
  4. Now move the main window to another DPI  scaled monitor

 

 

I know that I know nothing

Posted
1 hour ago, ioa747 said:

working case 

..I'll do you one better:

#AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
#include <WindowsStylesConstants.au3>
#include <GuiEdit.au3>
#include <FontConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsNotifsConstants.au3>
#include <WindowsConstants.au3>

#include "PMv2.au3"
_PM_Init()

Global $g_hStatus, $g_hGui, $g_idAboutOK = Null

Example()

Func Example()
    Local Const $fDefFontSize = 8.5, $fDefPixel = $fDefFontSize * 96 / 72
    Local $iCtrlSpaceV = 10
    Local $sAppName = "High DPI Scaling Example"
    Local $hGUI = GUICreate($sAppName, 400, 300)
    ;Local $hGUI = GUICreate($sAppName, 330, 260, -1, -1, BitOR($GUI_SS_DEFAULT_GUI,$WS_MAXIMIZEBOX,$WS_SIZEBOX,$WS_THICKFRAME,$WS_TABSTOP))
    GuiCenterOnMonitorFromMousePosition($hGUI)
    $g_hGui = $hGUI

    ; Get DPI Awareness Context
    Local $hContext = _WinAPI_GetWindowDpiAwarenessContext($hGUI)

    Local $hStatus = _GUICtrlStatusBar_Create($hGUI)
    $g_hStatus = $hStatus

    Local $idMnu_File = GUICtrlCreateMenu("&File")
    Local $idMnu_View = GUICtrlCreateMenu("View")
    Local $idMnu_Help = GUICtrlCreateMenu("Help")
    Local $idMni_Info = GUICtrlCreateMenuItem("About", $idMnu_Help)
    Local $idMni_Exit = GUICtrlCreateMenuItem("Exit", $idMnu_File)
    Local $idStatusShow = GUICtrlCreateMenuItem("Status Bar", $idMnu_View)
    GUICtrlSetState($idStatusShow, $GUI_CHECKED)

    ; Create new scaled font
    Local Static $hCtrlFont = _WinAPI_CreateFont(-Round($fDefPixel), 0, 0, 0, $FW_NORMAL, False, False, False, _
            $DEFAULT_CHARSET, $OUT_DEFAULT_PRECIS, $CLIP_DEFAULT_PRECIS, $PROOF_QUALITY, $DEFAULT_PITCH, "Segoe UI")

    Local $idTestName = GUICtrlCreateLabel("Test Label ", 15, 20, 180, 24)
    Local $aPos = ControlGetPos($hGUI, "", $idTestName)
    Local $iPrev = $aPos[1] + $aPos[3] + $iCtrlSpaceV
    GUICtrlSetFont(-1, 10, $FW_NORMAL, $GUI_FONTNORMAL, "Comic Sans MS")

    Local $idTestInput = GUICtrlCreateInput("Test Input", 15, $iPrev, 260, 22)
    $aPos = ControlGetPos($hGUI, "", $idTestInput)
    $iPrev = $aPos[1] + $aPos[3] + $iCtrlSpaceV
    GUICtrlSetFont(-1, 8.5, $FW_NORMAL, $GUI_FONTUNDER, "Segoe UI")

    Local $idCheckbox1 = GUICtrlCreateCheckbox("Test Checkbox1 ", 15, $iPrev, -1, -1)
    $aPos = ControlGetPos($hGUI, "", $idCheckbox1)
    $iPrev = $aPos[1] + $aPos[3] + $iCtrlSpaceV
    GUICtrlSetFont(-1, 8.5, $FW_NORMAL, $GUI_FONTSTRIKE, "Segoe UI")

    Local $idCheckbox2 = GUICtrlCreateCheckbox("Test Checkbox2 ", 15, $iPrev, -1, -1)
    $aPos = ControlGetPos($hGUI, "", $idCheckbox2)
    $iPrev = $aPos[1] + $aPos[3] + $iCtrlSpaceV
    GUICtrlSetFont(-1, 8.5, $FW_NORMAL, $GUI_FONTNORMAL, "Segoe UI")

    Local $hEditCtrl = _GUICtrlEdit_Create($hGUI, "This was created with standard UDF" & @CRLF & "function _GUICtrlEdit_Create().", 15, $iPrev, 300, 64)
    _SendMessage($hEditCtrl, $WM_SETFONT, $hCtrlFont, True)

    ; Set statusbar text
    Local $fDpiScale = __PM_GetWindowDPI($g_hGui) / 96
    Local $aParts[3] = [72 * $fDpiScale, 200 * $fDpiScale, -1]
    _GUICtrlStatusBar_SetParts($g_hStatus, $aParts)
    _GUICtrlStatusBar_SetText($g_hStatus, __PM_GetWindowDPI($g_hGui) & "dpi", 0)
    _GUICtrlStatusBar_SetText($g_hStatus, (__PM_GetWindowDPI($g_hGui) / 96) * 100 & "% scaling", 1)
    _GUICtrlStatusBar_SetText($g_hStatus, _GetDpiAwarenessContextName($hContext), 2)

    ; Register custom window message for the purpose of updating statusbar text
    GUIRegisterMsg($__PM_WM_DPICHANGED, "_WM_DPICHANGED")

    _PM_Scale($hGUI)
    GUISetState(@SW_SHOW, $hGUI)

    While 1
        Switch GUIGetMsg()
            Case $idStatusShow
                Local $bStatusShow
                If $hStatus Then
                    $bStatusShow = _WinAPI_IsWindowVisible($hStatus)
                    Switch $bStatusShow
                        Case 1
                            _GUICtrlStatusBar_ShowHide($hStatus, @SW_HIDE)
                            GUICtrlSetState($idStatusShow, $GUI_UNCHECKED)
                        Case 0
                            _GUICtrlStatusBar_ShowHide($hStatus, @SW_SHOW)
                            GUICtrlSetState($idStatusShow, $GUI_CHECKED)
                    EndSwitch
                EndIf

            Case $idMni_Info
;~              MsgBox($MB_TOPMOST, "PMv2", "PMv2 Version: " & _PM_Version() & @CRLF)
                _AboutGUI()

            Case $GUI_EVENT_CLOSE, $idMni_Exit, $g_idAboutOK
                If WinGetTitle("[ACTIVE]") = "About PMv2" Then
                    GUIDelete(WinGetHandle("About PMv2"))
                    ContinueLoop
                EndIf

                ExitLoop

        EndSwitch
    WEnd

    If $hCtrlFont Then _WinAPI_DeleteObject($hCtrlFont)
    GUIDelete($hGUI)
EndFunc   ;==>Example

Func _WM_DPICHANGED($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam

    ; Obtain new DPI value (raw)
    Local $iDPI = _WinAPI_LoWord($wParam)

    Local $hContext = _WinAPI_GetWindowDpiAwarenessContext($hWnd)
    Local $fDpiScale = $iDPI / 96
    Local $aParts[3] = [72 * $fDpiScale, 200 * $fDpiScale, -1]
    _GUICtrlStatusBar_SetParts($g_hStatus, $aParts)
    _GUICtrlStatusBar_SetText($g_hStatus, $iDPI & "dpi", 0)
    _GUICtrlStatusBar_SetText($g_hStatus, ($iDPI / 96) * 100 & "% scaling", 1)
    _GUICtrlStatusBar_SetText($g_hStatus, _GetDpiAwarenessContextName($hContext), 2)

    Return 0
EndFunc   ;==>_WM_DPICHANGED

Func _AboutGUI()
    Local $hGUIAbout = GUICreate("About PMv2", 240, 100)
    GUICtrlCreateLabel("PMv2 Version: " & _PM_Version(), 10, 20, 220, 25)
    GUICtrlSetFont(-1, 12, $FW_NORMAL, $GUI_FONTNORMAL, "Comic Sans MS")
    $g_idAboutOK = GUICtrlCreateButton("OK", 70, 70, 100, 25)
    GUISetState(@SW_SHOW, $hGUIAbout)
EndFunc   ;==>_AboutGUI

Func GuiCenterOnMonitorFromMousePosition($hForm, $sText = "") ; https://www.autoitscript.com/forum/topic/139260-autoit-snippets/page/22/#findComment-1479329
    Local $aData, $hMonitor, $tPos = _WinAPI_GetMousePos()
    If Not @error Then $hMonitor = _WinAPI_MonitorFromPoint($tPos)
    If Not @error Then $aData = _WinAPI_GetMonitorInfo($hMonitor)
    If @error Then Return SetError(1, -1, 1)
    Local $iWinState = WinGetState($hForm, $sText)
    If @error Then Return SetError(2, $aData[2], 2)
    If BitAND($iWinState, $WIN_STATE_MINIMIZED) Or _
            BitAND($iWinState, $WIN_STATE_MAXIMIZED) Then WinSetState($hForm, $sText, @SW_RESTORE)
    Local $aWinPos = WinGetPos($hForm, $sText)
    If @error Then Return SetError(3, $aData[2], 3)
    WinMove($hForm, _
            $sText, _
            Int((($aData[0].Right - $aData[0].Left - $aWinPos[2]) / 2) + $aData[0].Left), _
            Int((($aData[0].Bottom - $aData[0].Top - $aWinPos[3]) / 2) + $aData[0].Top))
    If @error Then Return SetError(4, $aData[2], 4)
    Return SetError(0, $aData[2], 0)
EndFunc   ;==>GuiCenterOnMonitorFromMousePosition

:) 

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting  image.gif.922e3a93535f431de08b31ee669cc446.gif
autoit_scripter_blue_userbar.png

Posted
1 hour ago, argumentum said:

..I'll do you one better:

I don't understand what is better  :ermm:

I ran it and didn't notice any difference
with GuiCenterOnMonitorFromMousePosition
or without GuiCenterOnMonitorFromMousePosition

I know that I know nothing

Posted
1 hour ago, argumentum said:

place your mouse on the other monitor then run the script.

That was what was missing (the instructions)  :)

But if you respected the way UDF works,
the GuiCenterOnMonitorFromMousePosition function
should have been included after _PM_Scale ,
which would have completed the control recording . e.g.

_PM_Scale($hGUI)
GuiCenterOnMonitorFromMousePosition($hGUI)
GUISetState(@SW_SHOW, $hGUI)

I know that I know nothing

Posted (edited)

I'm working on the PMv1 scaling right now and it is quite a bit different.

For PMv2, I don't need to lock down control resizing at all with GUICtrlSetResizing since I figured out the right order, timing and technique: 1. Record control coordinates, 2. SetWindowPos on GUI, 3. Move/size individual controls based on coordinates from 1 x new DPI scale and set fonts.

Anything outside of that exact order gets really messy. It took a lot of trial and error to get that PMv2 scaling technique to work without having to modify GUICtrlSetResizing like I initially did in early testing.

But for PMv1, that technique is not so good. And it seems that I may have to use GUICtrlSetResizing to lock down control resizing for PMv1 only. Or I may have to figure out a specific order, timing and technique for PMv1 that is different from PMv2.

Anyway, I had success with PMv1 for enabling EnableNonClientDpiScaling which is needed for OS builds between 14393 and 15063 which do not have PMv2 yet.

AI said that it was impossible in AutoIt to catch $WM_NCCREATE early enough to have success enabling EnableNonClientDpiScaling. But I did not listen. GUIRegisterMsg fails to catch it. A regular SetWindowSubclass fails to catch it.

However, a CBTProc with $HCBT_CREATEWND allows AutoIt to catch $WM_NCCREATE. So I have PMv1 resizing the menubar automatically now which is nice. :)

The only negative with PMv1 is that it does not have a way to automatically scale ComCtl v6 stuff like statusbar, theme related things like checkbox, etc. So PMv1 is a work in progress and a real pain to handle so it will take some time.

 

5 hours ago, ioa747 said:

working case 

  1. run the configured script
  2. call About
  3. move the About window to another DPI scaled monitor and close it
  4. Now move the main window to another DPI  scaled monitor

Thank you for bringing this to my attention. This clearly shows that there is a fundamental problem right now with multiple GUI's. Definitely something that I need to fix. I'll add dig into this today and fix it. :)

Edited by WildByDesign
Posted
5 hours ago, ioa747 said:

working case 

  1. run the configured script
  2. call About
  3. move the About window to another DPI scaled monitor and close it
  4. Now move the main window to another DPI  scaled monitor

I have to admit that I am a bit stumped on this issue right now.

I added a bunch of logging to try and figure out where the issue is occurring. I thought that maybe _WinAPI_EnumChildWindows was picking up child controls for the main GUI when the _AboutGUI() was scaled on the other monitor. But it seems that is not the issue.

I'm guessing that it must have more to do with the $fNewDPI / $fPrevDPI math specifically in this multiple GUI scenario. The math works fine for single GUI. So if this is the root of the issue, I may need some sort of way to store (globally or static) and array or map that keeps the previous DPI and current DPI for each top-level GUI window handle.

Posted
6 hours ago, ioa747 said:

:) that's exactly the problem,
$fPrevDPI is the previous DPI that appeared in the _PM_Scale function
and not the previous DPI of the running window

Thank you for confirming.

I just fixed the issue. I just followed your repro steps and confirmed that the issue is fixed.

However, those exact same repro steps found another issue. The fonts are getting deleted from the main GUI. So I've just got to fix that and will upload another release soon after.

I got all of the PMv1 scaling stuff sorted out although I don't know how many users would be within the OS builds that use PMv1.

Posted (edited)

PMv2 2026.08.13:

  • Improvements to PMv1 scaling as fallback to PMv2 on systems without PMv2
    • Setting EnableNonClientDpiScaling on PMv1 to enable automatic non-client area scaling (PMv1 only)
    • Added CBTHook for calling EnableNonClientDpiScaling during WM_NCCREATE (PMv1 only)
  • Fixed issue with multiple GUI scenario where previous DPI scale was incorrect - thanks ioa747

Updated download on first post. :)

 

16 hours ago, ioa747 said:

working case 

  1. run the configured script
  2. call About
  3. move the About window to another DPI scaled monitor and close it
  4. Now move the main window to another DPI  scaled monitor

I really need some help with another related issue that occurs when following these same repro steps of the script from that post.

I fixed the issue with the previous DPI scale being incorrect when multiple GUI's are used following the repro steps. However, the other related problem is that the control fonts are being deleted. When you follow those repro steps, all controls get set to a default GUI font since the UDF has deleted them.

I've added maps $__PM_mFontsCur[] and $__PM_mFontsOld[] to deal with this. But I'm not very good with maps at all.

EDIT: I may have this fixed now after I spotted my mistake. I still have to test my fix on a multi-monitor setup as soon as I can. If the fix is good, I'll have it added to the next release. :)

Edited by WildByDesign
Posted

Opinion Question:

Since the beginning of my experimentation with scaling PMv2, I've had to make a determination that depends whether or not the control is a native AutoIt control (GUICtrlCreate*) or a standard UDF created control.

Initially, I went with the assumption that anything with a CtrlID of 10,000 or higher must be standard UDF created. But I've seen some CtrlID's around 5,000 and some with no CtrlID. So I wanted to see if we can figure out a more "matter-of-fact" way to make this determination.

Here is my current idea on this (same idea in Switch and Select):

Local $iCtrlID = _WinAPI_GetDlgCtrlID($hCtrl)
Local $hCtrlTest = GUICtrlGetHandle($iCtrlID)

Switch $hCtrlTest
    Case 0
        ConsoleWrite("This is NOT a native AutoIt control: " & $sClass & @CRLF)
    Case Else
        ConsoleWrite("This is a native AutoIt control: " & $sClass & @CRLF)
EndSwitch

Select
    Case Not $hCtrlTest
        ConsoleWrite("This is NOT a native AutoIt control: " & $sClass & @CRLF)
    Case Else
        ConsoleWrite("This is a native AutoIt control: " & $sClass & @CRLF)
EndSelect

I was going to try this first with GUICtrlGetState for the purpose of this test, but it had different results for ListView controls. So from what I can tell, GUICtrlGetHandle might be the best. If it's not a native AutoIt control, it cannot get the handle and therefore returns 0.

Does this seem like a more reasonable way to make the determination of native AutoIt control or not native?

Posted

PMv2 2026.08.15:

  • Added AutoIt version and OS architecture to the window title of the Example
  • Added _IsNativeControl() function to determine if control is a native AutoIt control - thanks ioa747
  • Fixed an issue where incorrect font handles were deleted in some multi-GUI scenarios

 

@ioa747 Your _IsNativeControl() function is pure genius. Thank you. The boolean return made it easier to have simple Switch Case and made to overall code cleaner. :)

To fix the issue with the fonts being incorrectly deleted in that multi-GUI scenerio that you shared (with the About GUI), I created another map $__PM_mFontsOld and renamed the original map to $__PM_mFontsCur. It fixed the issue and I was able to confirm that the correct fonts are being deleted at the correct moment in time now for the correct window.

I did not (yet) do any kind of garbage collector cleanup destroyed windows with the $__PM_mFontsOld map. That is still happening with the original $__PM_mFontsCur map though. I just have a lot of trouble visualizing map structure for some reason.

Should we also have garbage collector cleanup destroyed windows on $__PM_mFontsOld?

If yes, could you please help me with that?

You have a very good structure set up for that stuff but I don't understand it very well. Possibly your cleanup functions (__PM_Free and __PM_Cleanup) can be extended to handle cleanup of the $__PM_mFontsOld map as well. Possibly even adding a parameter to the function to pass the different map variables into the function. I just don't understand it well enough and don't want to wreck your nice functions.

 

Posted
2 hours ago, WildByDesign said:

You have a very good structure set up for that stuff but I don't understand it very well. Possibly your cleanup functions (__PM_Free and __PM_Cleanup) can be extended to handle cleanup of the $__PM_mFontsOld map as well. Possibly even adding a parameter to the function to pass the different map variables into the function. I just don't understand it well enough and don't want to wreck your nice functions.

in this logic it would be better to put all the Arrays in the map $__PM_mDPI[$hWnd]
it would be more efficient, because deleting $hWnd from the map would automatically delete the Arrays related to $hWnd

I went to make an example on it and discovered that you don't use the map $__PM_mFontsCur anywhere, I mean
You only copy the static $aCtrlFonts
, which normally you don't even need the static since you have the map
just as you don't need $__PM_mFontsOld either because you can temporarily assign it to a Local varible until you replace $__PM_mFontsCur

and that's why I got stuck

I know that I know nothing

Posted
6 hours ago, ioa747 said:

in this logic it would be better to put all the Arrays in the map $__PM_mDPI[$hWnd]
it would be more efficient, because deleting $hWnd from the map would automatically delete the Arrays related to $hWnd

This definitely makes sense. Sorry about that. I'm really in the dark when it comes to maps. Everytime that I've tried to learn about maps, I could never quite understand. For example, I don't know how to put multiple arrays in $__PM_mDPI[$hWnd]. I will have to spend some time to get a better understanding of maps so that I can simply this.

6 hours ago, ioa747 said:

just as you don't need $__PM_mFontsOld either because you can temporarily assign it to a Local varible until you replace $__PM_mFontsCur

I see what you mean about this. This change I can make. It's just the map stuff that confuses me.

Posted (edited)

I left the old ones so you can see the differences.

Spoiler
; This also solves the issue he had with the new window that was passing $fPrevDPI incorrectly.

Func _PM_Scale($hWnd, $iDPI = "", $tRect = 0)
    Local $iCtrlID

    Local $bInitial = False

    If Not $iDPI Then
        $bInitial = True
        $iDPI = __PM_GetWindowDPI($hWnd)
        If @error Then $iDPI = 96
        $iDPI = 96
    EndIf

    Local $fNewDPI = $iDPI / 96
    Local $fScale = __PM_GetWindowDPI($hWnd) / 96
    If @error Then $fScale = 1

;~  Local Static $fPrevDPI = $fNewDPI
;~  Local Static $aCtrlFonts[0]

    Local $bPMv1 = False, $bPMv2 = False
    Local $hContext = _WinAPI_GetWindowDpiAwarenessContext($hWnd)
    If _WinAPI_AreDpiAwarenessContextsEqual($hContext, $DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) Then $bPMv2 = True
    If _WinAPI_AreDpiAwarenessContextsEqual($hContext, $DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE) Then $bPMv1 = True

    If $fScale <> $fNewDPI Then
        $fNewDPI = $fScale
    EndIf

    Local $fPrevDPI, $aData, $aFontsCur[0], $aFontsOld[0]

    ; Get previous DPI from map
    If MapExists($__PM_mDPI, $hWnd) Then
        ; Window found in Map -> get $aData
        $aData = $__PM_mDPI[$hWnd]

        $fPrevDPI = $aData[0]
        $aFontsOld = $aData[1]
;~      $fPrevDPI = $__PM_mDPI[$hWnd]
    Else
        ; Window not found in Map -> AUTOMATIC FIRST TIME INITIALIZATION
        Dim $aData[3]
        ; [0]=$fPrevDPI, [1]=$aFontsCur, [2]=...
        $aData[0] = 0   ; placeholder for $fPrevDPI
        $aData[1] = 0   ; placeholder for $aFontsCur
        $aData[2] = 0   ; placeholder for ...(for anything related to the current window)

        $fPrevDPI = $fNewDPI
;~      $__PM_mDPI[$hWnd] = $fPrevDPI
    EndIf
    
    ConsoleWrite("$fNewDPI=" & $fNewDPI & @CRLF)    
    ConsoleWrite("$fPrevDPI=" & $fPrevDPI & @CRLF)

    __PM_Free() ; Auto-garbage collect obsolete/destroyed windows from $__PM_mFontsCur

    Local $iMoveX, $iMoveY, $iMoveW, $iDiffW, $bCentered = False
    If Not IsDllStruct($tRect) Then
        $tRect = _WinAPI_GetWindowRect($hWnd)
        ; Used to determine GUI position only after initial scaling
        $iMoveX = DllStructGetData($tRect, "Left")
        $iMoveY = DllStructGetData($tRect, "Top")
        ; Used to determine if initial GUI was centered at startup
        $iMoveW = DllStructGetData($tRect, "Right") - DllStructGetData($tRect, "Left")
        $iDiffW = $iMoveX - (@DesktopWidth - $iMoveW - $iMoveX)
        If $iDiffW < 10 And $iDiffW > -10 Then $bCentered = True

        DllStructSetData($tRect, "Left", DllStructGetData($tRect, "Left") * $fNewDPI)
        DllStructSetData($tRect, "Top", DllStructGetData($tRect, "Top") * $fNewDPI)
        DllStructSetData($tRect, "Right", DllStructGetData($tRect, "Right") * $fNewDPI)
        DllStructSetData($tRect, "Bottom", DllStructGetData($tRect, "Bottom") * $fNewDPI)
    EndIf

;~  ; Clear previous font handle array
;~  If UBound($aCtrlFonts) > 0 Then
;~      For $i = 0 To UBound($aCtrlFonts) - 1
;~          _ArrayDelete($aCtrlFonts, 0)
;~      Next
;~  EndIf

    ; Lock window drawing for smoother transition
    _WinAPI_LockWindowUpdate($hWnd)

    ; Enumerate child windows and fill array with coordinates (before GUI gets resized) to resize controls AFTER GUI resize
    Local $aCtrls = _WinAPI_EnumChildWindows($hWnd, False)
    Local $hCtrl, $sClass, $aPos, $tPoint, $iX, $iY, $iW, $iH, $tLOGFONT, $hCtrlFont, $hDC
    Local $hFontTest, $hSelectObject, $tTEXTMETRIC
    If IsArray($aCtrls) Then
        _ArrayColInsert($aCtrls, 2)
        _ArrayColInsert($aCtrls, 2)
        _ArrayColInsert($aCtrls, 2)
        _ArrayColInsert($aCtrls, 2)
        _ArrayColInsert($aCtrls, 2)
        For $i = 1 To $aCtrls[0][0]
            $hCtrl = $aCtrls[$i][0]
            $sClass = $aCtrls[$i][1]
            $aPos = WinGetPos($hCtrl)
            $tPoint = DllStructCreate("int X;int Y")
            DllStructSetData($tPoint, "X", $aPos[0])
            DllStructSetData($tPoint, "Y", $aPos[1])

            If $bPMv1 Then
                ; Lock down control resizing with GUICtrlSetResizing for PMv1 only
                If _IsNativeControl($hCtrl) Then
                    $iCtrlID = _WinAPI_GetDlgCtrlID($hCtrl)
                    GUICtrlSetResizing($iCtrlID, $GUI_DOCKLEFT + $GUI_DOCKTOP + $GUI_DOCKWIDTH + $GUI_DOCKHEIGHT)
                EndIf
            EndIf

            ; Skip child controls that belong to other controls (e.g., Edit inside ComboBox, SysHeader32 inside ListView)
            If _WinAPI_GetParent($hCtrl) <> $hWnd Then ContinueLoop

            ; Skip statusbar control if context is PMv2 since OS will scale automatically
            If $sClass = "msctls_statusbar32" And $bPMv2 Then ContinueLoop
            ;If $sClass = "ToolbarWindow32" And $bPMv2 Then ContinueLoop
            ;If $sClass = "Button" And $bPMv2 Then ContinueLoop

            ; Convert screen coordinates to client
            _WinAPI_ScreenToClient($hWnd, $tPoint)
            $iX = DllStructGetData($tPoint, "X")
            $iY = DllStructGetData($tPoint, "Y")
            $iW = _WinAPI_GetWindowWidth($hCtrl)
            $iH = _WinAPI_GetWindowHeight($hCtrl)

            ; Determine new control sizes
            $iX = Floor((($fNewDPI / $fPrevDPI) * $iX))
            $iY = Floor((($fNewDPI / $fPrevDPI) * $iY))
            $iW = Floor((($fNewDPI / $fPrevDPI) * $iW))
            $iH = Floor((($fNewDPI / $fPrevDPI) * $iH))

            ; Fill remainder of array with coordinates
            $aCtrls[$i][2] = $iX
            $aCtrls[$i][3] = $iY
            $aCtrls[$i][4] = $iW
            $aCtrls[$i][5] = $iH

            ; Get the current font per control and select into DC to gather details
            $hDC = _WinAPI_GetDC($hCtrl)
            $hFontTest = _SendMessage($hCtrl, $WM_GETFONT)
            $hSelectObject = _WinAPI_SelectObject($hDC, $hFontTest)

            ; Obtain TEXTMETRIC structure with current font details
            $tTEXTMETRIC = _WinAPI_GetTextMetrics($hDC)

            ; Obtain LOGFONT structure to use directly with _WinAPI_CreateFontIndirect
            $tLOGFONT = DllStructCreate($tagLOGFONT)
            _WinAPI_GetObject($hFontTest, DllStructGetSize($tLOGFONT), DllStructGetPtr($tLOGFONT))
            If $bInitial Then
                Switch _IsNativeControl($hCtrl)
                    Case True
                        $tLOGFONT.Height = DllStructGetData($tTEXTMETRIC, "tmHeight")
                    Case False
                        ;SysIPAddress32
                        If $sClass = "msctls_statusbar32" And $bPMv1 Then ContinueLoop
                        If $sClass = "SysIPAddress32" Then
                            $tLOGFONT.Height = DllStructGetData($tTEXTMETRIC, "tmHeight")
                        Else
                            $tLOGFONT.Height = Ceiling(($fNewDPI / $fPrevDPI) * DllStructGetData($tTEXTMETRIC, "tmHeight"))
                        EndIf
                EndSwitch
            Else
                $tLOGFONT.Height = Ceiling(($fNewDPI / $fPrevDPI) * DllStructGetData($tTEXTMETRIC, "tmHeight"))
            EndIf

            ; Get custom control font adjusted for new DPI factor
            $hCtrlFont = _WinAPI_CreateFontIndirect($tLOGFONT)

            ; Add specific control font handle to main static array and secondary array to help with cleanup
            $aCtrls[$i][6] = $hCtrlFont
;~          _ArrayAdd($aCtrlFonts, $hCtrlFont)
            _ArrayAdd($aFontsCur, $hCtrlFont)

            _WinAPI_SelectObject($hDC, $hSelectObject)
            _WinAPI_ReleaseDC($hCtrl, $hDC)
        Next
        ; Save updated font handles back to global Map
        $aData[1] = $aFontsCur ; add to  placeholder
;~      $__PM_mFontsCur[$hWnd] = $aCtrlFonts
    EndIf

    ; Resize GUI window to adjust to new DPI factor
    Local $iXP = $tRect.left, $iYP = $tRect.top, $iWP = $tRect.right - $iXP, $iHP = $tRect.bottom - $iYP
    _WinAPI_SetWindowPos($hWnd, 0, $iXP, $iYP, $iWP, $iHP, BitOR($SWP_NOZORDER, $SWP_NOACTIVATE))
    If $bInitial Then
        ; On initial GUI launch, center if GUI was centered else use initial coordinates
        If $bCentered Then
            $aPos = WinGetPos($hWnd)
            WinMove($hWnd, "", (@DesktopWidth / 2 - ($aPos[2] / 2)), (@DesktopHeight / 2 - ($aPos[3] / 2)))
        Else
            WinMove($hWnd, "", $iMoveX, $iMoveY)
        EndIf
    EndIf

    ; Resize controls to adjust to new DPI factor
    If IsArray($aCtrls) Then
        For $i = 1 To $aCtrls[0][0]
            $hCtrl = $aCtrls[$i][0]
            $sClass = $aCtrls[$i][1]
            $iX = $aCtrls[$i][2]
            $iY = $aCtrls[$i][3]
            $iW = $aCtrls[$i][4]
            $iH = $aCtrls[$i][5]
            $hCtrlFont = $aCtrls[$i][6]

            ; Skip child controls that belong to other controls (e.g., Edit inside ComboBox, SysHeader32 inside ListView)
            If _WinAPI_GetParent($hCtrl) <> $hWnd Then ContinueLoop

            Switch _IsNativeControl($hCtrl)
                Case True
                    $iCtrlID = _WinAPI_GetDlgCtrlID($hCtrl)
                    GUICtrlSetPos($iCtrlID, $iX, $iY, $iW, $iH)
                    _SendMessage($hCtrl, $WM_SETFONT, $hCtrlFont, True)
                    _WinAPI_SetWindowPos($hCtrl, 0, 0, 0, 0, 0, BitOR($SWP_NOMOVE, $SWP_NOSIZE, $SWP_NOZORDER, $SWP_FRAMECHANGED))
                Case False
                    If $sClass = "ToolbarWindow32" And $bPMv2 Then ContinueLoop
                    _SendMessage($hCtrl, $WM_SETFONT, $hCtrlFont, True)
                    If $sClass = "msctls_statusbar32" And $bPMv1 Then ContinueLoop
                    ;_WinAPI_SetWindowPos($hCtrl, 0, $iX, $iY, $iW, $iH, BitOR($SWP_NOZORDER, $SWP_NOACTIVATE))
                    _WinAPI_SetWindowPos($hCtrl, 0, $iX, $iY, $iW, $iH, BitOR($SWP_NOZORDER, $SWP_NOACTIVATE, $SWP_FRAMECHANGED))
            EndSwitch
        Next
    EndIf

    ; Update previous DPI with the now current DPI
;~  $fPrevDPI = $fNewDPI
;~  $__PM_mDPI[$hWnd] = $fPrevDPI

    $aData[0] = $fNewDPI ; add to placeholder for $fPrevDPI
    $__PM_mDPI[$hWnd] = $aData ; add $aData back to map

    _WinAPI_LockWindowUpdate(0)

    ; Delete previous font resources after all changes
;~  Local $aPreviousFonts
;~  If MapExists($__PM_mFontsOld, $hWnd) Then
;~      $aPreviousFonts = $__PM_mFontsOld[$hWnd]
;~      For $i = 0 To UBound($aPreviousFonts) - 1
        For $i = 0 To UBound($aFontsOld) - 1
;~          _WinAPI_DeleteObject($aPreviousFonts[$i])
            _WinAPI_DeleteObject($aFontsOld[$i])
        Next
;~  EndIf

;~  ; Update map with fonts that will be previous fonts on next DPI change
;~  $__PM_mFontsOld[$hWnd] = $aCtrlFonts
EndFunc   ;==>_PM_Scale

Func __PM_Cleanup()
    Local $aKeys = MapKeys($__PM_mDPI)
    If @error Or Not IsArray($aKeys) Then Return True

    Local $hWnd

    ; Iterate through all registered GUI windows in the global Map
    For $i = 0 To UBound($aKeys) - 1
        $hWnd = $aKeys[$i]
        __PM_Free($hWnd)
    Next

    Return True
EndFunc   ;==>__PM_Cleanup

Func __PM_Free($hWnd = 0)
    Local $aKeys = MapKeys($__PM_mDPI)
    If @error Or Not IsArray($aKeys) Then Return True

    Local $hTargetWnd, $aData, $aFontsOld, $hFont

    ; === CASE 1: AUTO GARBAGE COLLECTION FOR ALL DESTROYED WINDOWS ===
    If $hWnd = 0 Then
        For $i = 0 To UBound($aKeys) - 1
            $hTargetWnd = $aKeys[$i]
            If Not IsHWnd($hTargetWnd) Then ContinueLoop
            If Not WinExists(HWnd($hTargetWnd)) Then
                $aData = $__PM_mDPI[$hTargetWnd]
                $aFontsOld = $aData[1]
                If IsArray($aFontsOld) Then
                    For $j = 0 To UBound($aFontsOld) - 1
                        $hFont = $aFontsOld[$j]
                        If $hFont Then _WinAPI_DeleteObject($hFont)
                    Next
                EndIf
                MapRemove($__PM_mDPI, $hTargetWnd)
            EndIf
        Next
        Return True
    EndIf

    ; === CASE 2: SPECIFIC WINDOW PURGE ===
    If Not MapExists($__PM_mDPI, $hWnd) Then Return SetError(1, 0, False)
    $aData = $__PM_mDPI[$hWnd]
    $aFontsOld = $aData[1]
    If IsArray($aFontsOld) Then
        For $i = 0 To UBound($aFontsOld) - 1
            $hFont = $aFontsOld[$i]
            If $hFont Then
                _WinAPI_DeleteObject($hFont)
                $aFontsOld[$i] = 0
            EndIf
        Next
    EndIf

    MapRemove($__PM_mDPI, $hWnd)
    Return True
EndFunc   ;==>__PM_Free

 

 

Edited by ioa747

I know that I know nothing

Posted
11 hours ago, ioa747 said:

I left the old ones so you can see the differences.

This was very helpful. Thank you so much. Also, leaving the old lines is was beneficial for me to see where I went wrong and how it can be done better. So it was helpful for me to learn from. I appreciate it. :)

Posted

WildByDesign, thank you (and others) for developing this scaling UDF!  I have been playing around with it, and on several of my AutoIt scripts it seems to function well.  However, any GUI that utilizes a GUICtrlCreateUpdown, the "box" for the control is missing.  My system is Windows 10 IoT Enterprise LTSC with a 4K monitor at 175% scaling.  Following is some code that demonstrates the issue - when your UDF is commented out, things look correct, when enabled, the GUICtrlCreateUpdown controls are wrong:

#include <Misc.au3>
#include <GUIConstantsEx.au3>

#include "PMv2.au3"
_PM_Init()

Opt("GUIOnEventMode", 1)

Global $hMainGUI = GUICreate("PMv2 Test", 450, 75)

    GUISetOnEvent($GUI_EVENT_CLOSE, "f_Exit")

    ;CD Begin Folder Input
    GUICtrlCreateLabel("CD Begin:", 50, 10, 150, 20)
    GUICtrlCreateLabel("CD", 50, 27, 20, 20)
    Global $g_idCDBeginDir = GUICtrlCreateInput("1", 70, 25, 40, 24, 0x2000)
    GUICtrlCreateUpdown(-1, 0x21)
    GUICtrlSetLimit (-1, 16, 1)

    ;CD End Folder Input
    GUICtrlCreateLabel("CD End:", 150, 10, 150, 20)
    GUICtrlCreateLabel("CD", 150, 27, 20, 20)
    Global $g_idCDEndDir = GUICtrlCreateInput("10", 170, 25, 40, 24, 0x2000)
    GUICtrlCreateUpdown(-1, 0x21)
    GUICtrlSetLimit (-1, 16, 1)

    ;Number Of Tracks Per CD Folder Input
    GUICtrlCreateLabel("Tracks/CD:", 250, 10, 140, 20)
    Global $g_idTracksPerCD = GUICtrlCreateInput("20", 250, 25, 40, 24, 0x2000) ;300L, 200T, 40W, 24H
    GUICtrlCreateUpdown(-1, 0x21)
    GUICtrlSetLimit (-1, 99, 1)

    ;Number Of MP3s Per Track Input
    GUICtrlCreateLabel("MP3s/Track:", 340, 10, 140, 20)
    Global $g_idMP3sPerTrack = GUICtrlCreateInput("3", 340, 25, 40, 24, 0x2000) ;390L, 200T, 40W, 24H
    GUICtrlCreateUpdown(-1, 0x21)
    GUICtrlSetLimit (-1, 16, 1)

_PM_Scale($hMainGUI)
GUISetState(@SW_SHOW, $hMainGUI)

While 1
    Sleep(100)
WEnd

Func f_Exit()
    Exit
EndFunc

Hope this helps ...

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
×
×
  • Create New...