argumentum Posted August 12 Posted August 12 7 minutes ago, WildByDesign said: I am thinking that we should make Win10 version 1607 the lowest supported build for this UDF. disable in the UDF it's use so that scripts will run regardless, just without the goodies. WildByDesign 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting
ioa747 Posted August 13 Posted August 13 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
ioa747 Posted August 13 Posted August 13 expandcollapse popup#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 run the configured script call About move the About window to another DPI scaled monitor and close it Now move the main window to another DPI scaled monitor WildByDesign 1 I know that I know nothing
argumentum Posted August 13 Posted August 13 1 hour ago, ioa747 said: working case ..I'll do you one better: expandcollapse popup#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 WildByDesign 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting
ioa747 Posted August 13 Posted August 13 1 hour ago, argumentum said: ..I'll do you one better: I don't understand what is better I ran it and didn't notice any difference with GuiCenterOnMonitorFromMousePosition or without GuiCenterOnMonitorFromMousePosition I know that I know nothing
argumentum Posted August 13 Posted August 13 Just now, ioa747 said: I ran it and didn't notice any difference place your mouse on the other monitor then run the script. Is just a way for .. testing ? 🤔 ioa747 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting
ioa747 Posted August 13 Posted August 13 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) argumentum 1 I know that I know nothing
WildByDesign Posted August 13 Author Posted August 13 (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 run the configured script call About move the About window to another DPI scaled monitor and close it 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 August 13 by WildByDesign argumentum and ioa747 2
WildByDesign Posted August 13 Author Posted August 13 5 hours ago, ioa747 said: working case run the configured script call About move the About window to another DPI scaled monitor and close it 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.
ioa747 Posted August 13 Posted August 13 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 WildByDesign 1 I know that I know nothing
WildByDesign Posted August 13 Author Posted August 13 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.
WildByDesign Posted August 13 Author Posted August 13 (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 run the configured script call About move the About window to another DPI scaled monitor and close it 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 August 13 by WildByDesign ioa747 and argumentum 2
WildByDesign Posted August 14 Author Posted August 14 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? ioa747 1
ioa747 Posted August 14 Posted August 14 Func _IsNativeControl($hCtrl) Local $iCtrlID = _WinAPI_GetDlgCtrlID($hCtrl) If @error Or $iCtrlID <= 0 Then Return False Return (GUICtrlGetHandle($iCtrlID) = $hCtrl) EndFunc WildByDesign 1 I know that I know nothing
WildByDesign Posted August 15 Author Posted August 15 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. ioa747 and argumentum 2
ioa747 Posted August 15 Posted August 15 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
WildByDesign Posted August 15 Author Posted August 15 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.
ioa747 Posted August 15 Posted August 15 (edited) I left the old ones so you can see the differences. Spoiler expandcollapse popup; 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 August 15 by ioa747 WildByDesign 1 I know that I know nothing
WildByDesign Posted August 16 Author Posted August 16 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. ioa747 1
Engr Posted August 16 Posted August 16 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: expandcollapse popup#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 ...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now