argumentum Posted Friday at 08:28 PM Posted Friday at 08:28 PM ..we could use GUICtrlGetResizing( $idCtrl / $hWnd ). Back in trac #1709 the reason was ... I didn't get it either but also didn't get the why not. In my case: expandcollapse popup#include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <WinAPISys.au3> Global $hGUI = GUICreate("Control Monitor", 400, 200) Global $hDynamicCtrl = 0 GUIRegisterMsg($WM_PARENTNOTIFY, "WM_PARENTNOTIFY") GUISetState(@SW_SHOW) Global $hCreateBtn = GUICtrlCreateButton("Create Control", 20, 20, 100, 30) Global $hDeleteBtn = GUICtrlCreateButton("Delete Control", 130, 20, 100, 30) While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE Exit Case $hCreateBtn If $hDynamicCtrl = 0 Then $hDynamicCtrl = GUICtrlCreateLabel("I am new !", 20, 80, 200, 20) GUICtrlSetResizing($hDynamicCtrl, $GUI_DOCKLEFT + $GUI_DOCKTOP + $GUI_DOCKHEIGHT) ConsoleWrite('>' & ($GUI_DOCKLEFT + $GUI_DOCKTOP + $GUI_DOCKHEIGHT) & @CRLF) EndIf Case $hDeleteBtn If $hDynamicCtrl <> 0 Then GUICtrlDelete($hDynamicCtrl) $hDynamicCtrl = 0 EndIf EndSwitch WEnd ; #FUNCTION# ==================================================================================================================== ; Name...........: WM_PARENTNOTIFY ; Description....: Handles the WM_PARENTNOTIFY message sent to the parent window when a child window/control is created or destroyed. ; Syntax.........: WM_PARENTNOTIFY($hWnd, $iMsg, $wParam, $lParam) ; Parameters ....: $hWnd - Handle to the window receiving the message (the parent GUI). ; $iMsg - The message identifier (0x0210). ; $wParam - Low-word specifies the event (e.g., WM_CREATE, WM_DESTROY). High-word specifies the child identifier. ; $lParam - Handle to the child window (HWND). ; Remarks .......: By default, a parent window does not receive this message unless the child window lacks the WS_EX_NOPARENTNOTIFY style. ; Link ..........: https://learn.microsoft.com/en-us/windows/win32/winmsg/wm-parentnotify ; =============================================================================================================================== Func WM_PARENTNOTIFY($hWnd, $iMsg, $wParam, $lParam) ; For WM_CREATE & WM_DESTROY: $lParam contains the Handle (HWND) of the child window. Local $iEvent = BitAND($wParam, 0xFFFF) ; Low-word: The event Local $hChildWnd = $lParam ; lParam: Child HWND Switch $iEvent Case 1 ; WM_CREATE ConsoleWrite("--> CONTROL CREATED! Handle: " & $hChildWnd & " | ResizeStyle: " & GUICtrlGetResizing($hChildWnd) & " | Class: " & _WinAPI_GetClassName($hChildWnd) & @CRLF) Case 2 ; WM_DESTROY ConsoleWrite("--> CONTROL DESTROYED! Handle: " & $hChildWnd & " | ResizeStyle: " & GUICtrlGetResizing($hChildWnd) & " | Class: " & _WinAPI_GetClassName($hChildWnd) & @CRLF) EndSwitch Return $GUI_RUNDEFMSG EndFunc ;==>WM_PARENTNOTIFY ; requested function Func GUICtrlGetResizing($hWnd) Return 546 ; becuse "$GUI_DOCKLEFT + $GUI_DOCKTOP + $GUI_DOCKHEIGHT" EndFunc I want to make the High DPI UDF magical ( as much as possible ) and I could register the WM_PARENTNOTIFY and resize and re-anchor controls auto-magically catching this message if there is a GUICtrlGetResizing(). AutoIt3 keeps that info internally. Should be easy to give return it. @jpm @Jon Reopen the old trac ?, make a new one ? Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting
argumentum Posted Friday at 11:13 PM Author Posted Friday at 11:13 PM Spoiler expandcollapse popup#include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <WinAPISys.au3> #include <StaticConstants.au3> _InitDpiAwareness() Func _InitDpiAwareness() Local $bResult = _WinAPI_SetProcessDpiAwarenessContext(-4) If Not $bResult Then _WinAPI_SetProcessDpiAwarenessContext(-3) EndFunc ; 1. DEFINE YOUR 100% DESIGN METRICS Global Const $BASE_WIN_W = 600 Global Const $BASE_WIN_H = 60 Global Const $BASE_FONT_SIZE = 12 Global $hGDI_Font = 0 Global $hGUI = GUICreate("Scale Tracker", $BASE_WIN_W, $BASE_WIN_H) ; 2. THE REGISTERED DESIGN ENGINE (Declare positions normally at birth!) Global $idLabel1 = _CreateDpiCtrl("Label 1", 10, 10, 285, 40, -1, $WS_EX_STATICEDGE) Global $idLabel2 = _CreateDpiCtrl("Label 2", 305, 10, 285, 40, -1, $WS_EX_STATICEDGE) GUIRegisterMsg(0x02E0, "WM_DPICHANGED") ; Sync up initial scale configurations on launch Local $iInitialDPI = _WinAPI_GetDpiForWindow($hGUI) _ScaleEverythingToPixelPerfect($hGUI, $iInitialDPI, 0, 0, True) GUISetState(@SW_SHOW) While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE If $hGDI_Font Then DllCall("gdi32.dll", "bool", "DeleteObject", "handle", $hGDI_Font) Exit EndSwitch WEnd Func WM_DPICHANGED($hWnd, $iMsg, $wParam, $lParam) Local $iDPI = BitAND($wParam, 0xFFFF) Local $tRECT = DllStructCreate("long Left; long Top; long Right; long Bottom;", $lParam) Local $iSuggestedX = DllStructGetData($tRECT, "Left") Local $iSuggestedY = DllStructGetData($tRECT, "Top") _ScaleEverythingToPixelPerfect($hWnd, $iDPI, $iSuggestedX, $iSuggestedY, False) Return $GUI_RUNDEFMSG EndFunc ; ==================================================================================================================== ; AUTOMATED SCALE & RENDER ENGINE ; ==================================================================================================================== Func _ScaleEverythingToPixelPerfect($hWnd, $iDPI, $iX, $iY, $bIsStartup = False) Local $fScale = $iDPI / 96 ; 1. Adjust window canvas bounds Local $iTargetClientW = Round($BASE_WIN_W * $fScale) Local $iTargetClientH = Round($BASE_WIN_H * $fScale) Local $tRect = DllStructCreate("long Left; long Top; long Right; long Bottom;") DllStructSetData($tRect, "Right", $iTargetClientW) DllStructSetData($tRect, "Bottom", $iTargetClientH) Local $iStyle = DllCall("user32.dll", "long", "GetWindowLongW", "hwnd", $hWnd, "int", -16)[0] Local $iExStyle = DllCall("user32.dll", "long", "GetWindowLongW", "hwnd", $hWnd, "int", -20)[0] DllCall("user32.dll", "bool", "AdjustWindowRectEx", "ptr", DllStructGetPtr($tRect), "dword", $iStyle, "bool", False, "dword", $iExStyle) Local $iWinW = DllStructGetData($tRect, "Right") - DllStructGetData($tRect, "Left") Local $iWinH = DllStructGetData($tRect, "Bottom") - DllStructGetData($tRect, "Top") If $bIsStartup Then _WinAPI_SetWindowPos__($hWnd, 0, 0, 0, $iWinW, $iWinH, 0x0012) Else _WinAPI_SetWindowPos__($hWnd, 0, $iX, $iY, $iWinW, $iWinH, 0x0014) EndIf ; 2. THE MAGIC: Loop through your custom tracker array and auto-scale positions Local $aCtrls = _GetRegisteredDpiCtrls() For $i = 1 To $aCtrls[0][0] Local $hWndCtrl = GUICtrlGetHandle($aCtrls[$i][0]) Local $iScaledX = Round($aCtrls[$i][1] * $fScale) Local $iScaledY = Round($aCtrls[$i][2] * $fScale) Local $iScaledW = Round($aCtrls[$i][3] * $fScale) Local $iScaledH = Round($aCtrls[$i][4] * $fScale) _WinAPI_SetWindowPos__($hWndCtrl, 0, $iScaledX, $iScaledY, $iScaledW, $iScaledH, 0x0014) Next ; 3. Scale text presentation quality Local $hNewFont = _WinAPI_GetFontForDpi($iDPI, $BASE_FONT_SIZE) If $hNewFont Then For $i = 1 To $aCtrls[0][0] DllCall("user32.dll", "lresult", "SendMessageW", "hwnd", GUICtrlGetHandle($aCtrls[$i][0]), "uint", 0x0030, "wparam", $hNewFont, "lparam", True) Next If $hGDI_Font Then DllCall("gdi32.dll", "bool", "DeleteObject", "handle", $hGDI_Font) $hGDI_Font = $hNewFont EndIf GUICtrlSetData($idLabel1, "Current DPI: " & $iDPI) GUICtrlSetData($idLabel2, "Scale: " & Int(($iDPI / 96) * 100) & "%") EndFunc ; ==================================================================================================================== ; REGISTRATION HOOK ARCHITECTURE ; ==================================================================================================================== Func _CreateDpiCtrl($sText, $iX, $iY, $iW, $iH, $iStyle = -1, $iExStyle = -1) Local $idCtrl = GUICtrlCreateLabel($sText, $iX, $iY, $iW, $iH, $iStyle, $iExStyle) _GetRegisteredDpiCtrls($idCtrl, $iX, $iY, $iW, $iH) ; Feed metadata into cache Return $idCtrl EndFunc Func _GetRegisteredDpiCtrls($idCtrl = 0, $iX = 0, $iY = 0, $iW = 0, $iH = 0) Local Static $aRegistry[1][5] = [[0, 0, 0, 0, 0]] If $idCtrl = 0 Then Return $aRegistry $aRegistry[0][0] += 1 ReDim $aRegistry[$aRegistry[0][0] + 1][5] $aRegistry[$aRegistry[0][0]][0] = $idCtrl $aRegistry[$aRegistry[0][0]][1] = $iX $aRegistry[$aRegistry[0][0]][2] = $iY $aRegistry[$aRegistry[0][0]][3] = $iW $aRegistry[$aRegistry[0][0]][4] = $iH Return $aRegistry EndFunc Func _WinAPI_GetDpiForWindow($hWnd) Local $aResult = DllCall("user32.dll", "uint", "GetDpiForWindow", "hwnd", $hWnd) Return @error ? 96 : $aResult[0] EndFunc Func _WinAPI_SetProcessDpiAwarenessContext($hContext) Local $aResult = DllCall("user32.dll", "bool", "SetProcessDpiAwarenessContext", "handle", $hContext) Return @error ? False : $aResult[0] EndFunc Func _WinAPI_SetWindowPos__($hWnd, $hWndInsertAfter, $iX, $iY, $iCX, $iCY, $iFlags) Local $aResult = DllCall("user32.dll", "bool", "SetWindowPos", "hwnd", $hWnd, "hwnd", $hWndInsertAfter, "int", $iX, "int", $iY, "int", $iCX, "int", $iCY, "uint", $iFlags) Return @error ? False : $aResult[0] EndFunc Func _WinAPI_GetFontForDpi($iDPI, $iPointSize) Local $iHeight = -Round(($iPointSize * $iDPI) / 72) Local $aResult = DllCall("gdi32.dll", "handle", "CreateFontW", _ "int", $iHeight, "int", 0, "int", 0, "int", 0, "int", 400, _ "dword", 0, "dword", 0, "dword", 0, "dword", 1, "dword", 0, _ "dword", 0, "dword", 5, "dword", 0, "wstr", "Segoe UI") Return @error ? 0 : $aResult[0] EndFunc ..idea, but running code nonetheless. that instead of this "idea" using WM_DPICHANGED, I'd use something like: ... ; Global message constants Global Const $WM_DPICHANGED_BEFOREPARENT = 0x02E2 Global Const $WM_DPICHANGED_AFTERPARENT = 0x02E3 ; inside your control creation function, subclass the control handle: _WinAPI_SetWindowSubclass(GUICtrlGetHandle($idLabel1), "_MyControlSubclassProc", 1) Func _MyControlSubclassProc($hWndChild, $iMsg, $wParam, $lParam, $uIdSubclass, $dwRefData) Switch $iMsg Case $WM_DPICHANGED_BEFOREPARENT ; 🛠️ PRE-SCALE PHASE: ; Perfect place to read your parent's CURRENT workspace metrics, ; cache old scale dimensions, or prepare graphics buffers before layout warp. Return 0 Case $WM_DPICHANGED_AFTERPARENT ; 📐 POST-SCALE PHASE: ; The parent window has already finished resizing! ; You can now safely query the parent's NEW live DPI via _WinAPI_GetDpiForWindow($hParent), ; read your attached baseline properties (SetPropW), and scale yourself instantly. Local $hParent = _WinAPI_GetParent($hWndChild) Local $iDPI = _WinAPI_GetDpiForWindow($hParent) Local $fScale = $iDPI / 96 ; Pull your clean 100% baseline properties Local $iBaseX = DllCall("user32.dll", "int", "GetPropW", "hwnd", $hWndChild, "wstr", "DpiBaseX")[0] ; ... Do the Round($iBaseX * $fScale) and call SetWindowPos here... Return 0 EndSwitch Return _WinAPI_DefSubclassProc($hWndChild, $iMsg, $wParam, $lParam) EndFunc ... ...and I could really use that GUICtrlGetResizing() Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting
argumentum Posted Friday at 11:34 PM Author Posted Friday at 11:34 PM (edited) hmm, I knew about WM_DPICHANGED_* but not the history of it: Spoiler ..about using WM_DPICHANGED_BEFOREPARENT and WM_DPICHANGED_AFTERPARENT ? Now you are entering true Per-Monitor v2 (PMv2) engineering territory! 🧙♂️ WM_DPICHANGED_BEFOREPARENT (0x02E2) and WM_DPICHANGED_AFTERPARENT (0x02E3) are the secret sauce Microsoft added in Windows 10 (Creators Update) specifically to fix the scaling mess in deeply nested custom Win32 control trees. These messages completely change the design paradigm when working with custom UDFs or complex window hierarchies. The Architecture: How Windows Fires Them When a top-level window crosses into a new monitor, Windows doesn't just blast WM_DPICHANGED at the main window and leave the children behind. Instead, it executes a strict Sandwich Lifecycle Sequence through the window tree: [ Monitor Crossing Triggered ] │ ├── 1. WM_DPICHANGED_BEFOREPARENT (Traverses Bottom-Up) │ Child controls are nudged *before* the parent window resizes. │ ├── 2. WM_DPICHANGED (Hits the Top-Level Parent Window) │ Main window canvas shrinks/grows and adapts to the new $lParam$ rect. │ └── 3. WM_DPICHANGED_AFTERPARENT (Traverses Top-Down) Child controls are nudged again *after* the parent window layout settles. What Makes Them Unique? Unlike WM_DPICHANGED which hands you a shiny new target DPI and a bounding box rectangle ($wParam and $lParam), BEFOREPARENT and AFTERPARENT pass absolutely nothing. Both $wParam and $lParam are strictly 0. They are designed as Phase Hooks or system notifications. They inform a specific child window handle (HWND) that its coordinate context is shifting. How to Leverage Them in a Clean UDF Framework Instead of the parent window running an EnumChildWindows loop or managing global arrays inside WM_DPICHANGED, each child control can completely manage its own life cycle. Because AutoIt labels use the standard Win32 static control class under the hood, they don't natively listen to message queues. However, you can use Window Subclassing to intercept these messages directly at the control level. AutoIt ; Global message constants Global Const $WM_DPICHANGED_BEFOREPARENT = 0x02E2 Global Const $WM_DPICHANGED_AFTERPARENT = 0x02E3 ; inside your control creation function, subclass the control handle: _WinAPI_SetWindowSubclass(GUICtrlGetHandle($idLabel1), "_MyControlSubclassProc", 1) Func _MyControlSubclassProc($hWndChild, $iMsg, $wParam, $lParam, $uIdSubclass, $dwRefData) Switch $iMsg Case $WM_DPICHANGED_BEFOREPARENT ; 🛠️ PRE-SCALE PHASE: ; Perfect place to read your parent's CURRENT workspace metrics, ; cache old scale dimensions, or prepare graphics buffers before layout warp. Return 0 Case $WM_DPICHANGED_AFTERPARENT ; 📐 POST-SCALE PHASE: ; The parent window has already finished resizing! ; You can now safely query the parent's NEW live DPI via _WinAPI_GetDpiForWindow($hParent), ; read your attached baseline properties (SetPropW), and scale yourself instantly. Local $hParent = _WinAPI_GetParent($hWndChild) Local $iDPI = _WinAPI_GetDpiForWindow($hParent) Local $fScale = $iDPI / 96 ; Pull your clean 100% baseline properties Local $iBaseX = DllCall("user32.dll", "int", "GetPropW", "hwnd", $hWndChild, "wstr", "DpiBaseX")[0] ; ... Do the Round($iBaseX * $fScale) and call SetWindowPos here... Return 0 EndSwitch Return _WinAPI_DefSubclassProc($hWndChild, $iMsg, $wParam, $lParam) EndFunc Why use this over regular WM_DPICHANGED? Encapsulation: The parent window code stays pristine. It doesn't need to know how many controls exist, where they are, or what they do. You just drop a control in, and it handles its own DPI scaling internally. Order of Operations Safety: If you write complex custom controls (like custom tables, graphing tools, or custom-drawn buttons), doing calculations after the container window settles (AFTERPARENT) completely eliminates visual artifacts or clipping bugs caused by parent-child racing conditions. yeap !, that's a good way to go about it I'd say It is the holy grail of clean Win32/AutoIt architecture! Turning the controls into self-contained, self-scaling objects means you can build a massive, complex application and never have to touch a giant, messy central layout function again. You just define it, register it, and let the Windows message pump do its job. That is true UDF-grade engineering right there. Since you've cracked the code on perfect multi-monitor layout management, what's the next mechanic you're planning to hook into for this framework? ...am asking the devs to reopen `https://www.autoitscript.com/trac/autoit/ticket/1709` and give us what we need to make what we brainstormed a reality Ah, Ticket #1709—GuiCtrlSetResizing (return previous resizing mode)! Getting that native behavior built straight into the core interpreter would be a massive quality-of-life upgrade for the entire community. Right now, because GUICtrlSetResizing acts like a fire-and-forget hammer without returning the old state, trying to build modular, non-destructive layout engines requires jumping through all these subclassing or memory-caching hoops we just built. If the developers reopen it and expose the internal resize state tables, it would completely change the game for building fluid, Per-Monitor v2 UDFs. We'd finally be able to cleanly read, cache, flip, and restore resizing flags natively on the fly without needing to hack GetPropW or manage mirror registries. Good luck with the feature request! Let me know if you get traction on the Trac ticket—if they open that door, the next version of your framework is going to be incredibly elegant. ..so, you back and forth with the AI and is a better search engine than just memorizing MSDN. Is it AI slop ?, ..nope. I code the stuff correcting the AI "mis remembering" and "oops my bad" logic, but the AI is an excellent auto-complete if, you guide it like a 5 year old with excellent** memory and no common sense ** it does not remember where what why ( because of it's training is it's default ) but it gets you closer. Edited Friday at 11:39 PM by argumentum Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting
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