Jump to content

Child Window with TabStops and Scroll


sudeepjd
 Share

Recommended Posts

I am building an application which needs a child panel in the GUI Control that needs to be scrolled as it contains controls that extend beyond the panel height. But I also need to have the users to be able to Tab through those controls. I don't seem to be able to to both working together. 

The Tabstops can be allowed on children by using $WS_EX_CONTROLPARENT and the Scrollbar creation using the GUIScrollBar.au3.

If I set the $WS_EX_CONTROLPARENT it drags the entire child and does not allow the scroll, but if I remove it, the Scroll works but the tabstops don't. Please see the below sample application that can help reproduce this problem.

 

#include <GUIConstants.au3>
#include <GUIScrollbars.au3>
#include <WindowsConstants.au3>

Opt("GUIOnEventMode", 1)  ; Change to OnEvent mode

Global $width=500, $height=500, $titel="Tabtest in Childwindow"

; create the parentwindow
$mainwindow = GUICreate($titel, $width, $height, -1, -1)
GUISetBkColor(0x00ffff)
; show the parentwindow
GUISetState(@SW_SHOW,$mainwindow)

; check on screen
sleep(1000)

; create the childwindow with the scrollbars active ->TABSTOPS Dont work
$childwindo1 = GUICreate("child", 220,$height, 10 ,0,$WS_CHILD, -1 ,$mainwindow)
_GUIScrollBars_Init($childwindo1, 100, 100)
GUISetBkColor(0xff0000, $childwindo1)
GUISetState(@SW_SHOW, $childwindo1)

$input_1 = GUICtrlCreateInput("Scroll Works",10,10)
$input_2 = GUICtrlCreateInput("Tab Does Not",10,40)

; create the childwindow with the scrollbars active ->TABSTOPS Work, Scroll does not because window moves
$childwindo2 = GUICreate("child", 220, $height, 240 ,0,$WS_CHILD, $WS_EX_CONTROLPARENT ,$mainwindow)

_GUIScrollBars_Init($childwindo2, 100, 100)
GUISetBkColor(0xff0000, $childwindo2)
GUISetState(@SW_SHOW, $childwindo2)

$input_3 = GUICtrlCreateInput("Tab Works",10,10)
$input_4 = GUICtrlCreateInput("Scroll Does Not",10,40)

GUIRegisterMsg($WM_VSCROLL, "WM_VSCROLL")

; register close
GUISetOnEvent($GUI_EVENT_CLOSE, "close_it",$mainwindow)

;loop
While 1
    Sleep(100)  ; Idle around
WEnd

Func close_it()
    ; exit application
    GUIDelete($mainwindow)
    exit
EndFunc

$childwindo = GUICreate("child",$width,$height,0,0,$WS_CHILD,-1,$mainwindow)
GUISetBkColor(0xff0000)
GUISetState(@SW_SHOW,$childwindo)

Func WM_VSCROLL($hWnd, $iMsg, $wParam, $lParam)
    #forceref $iMsg, $wParam, $lParam
    Local $iScrollCode = BitAND($wParam, 0x0000FFFF)
    Local $iIndex = -1, $iCharY, $iPosY
    Local $iMin, $iMax, $iPage, $iPos, $iTrackPos

    For $x = 0 To UBound($__g_aSB_WindowInfo) - 1
        If $__g_aSB_WindowInfo[$x][0] = $hWnd Then
            $iIndex = $x
            $iCharY = $__g_aSB_WindowInfo[$iIndex][3]
            ExitLoop
        EndIf
    Next
    If $iIndex = -1 Then Return 0

    ; Get all the vertial scroll bar information
    Local $tSCROLLINFO = _GUIScrollBars_GetScrollInfoEx($hWnd, $SB_VERT)
    $iMin = DllStructGetData($tSCROLLINFO, "nMin")
    $iMax = DllStructGetData($tSCROLLINFO, "nMax")
    $iPage = DllStructGetData($tSCROLLINFO, "nPage")
    ; Save the position for comparison later on
    $iPosY = DllStructGetData($tSCROLLINFO, "nPos")
    $iPos = $iPosY
    $iTrackPos = DllStructGetData($tSCROLLINFO, "nTrackPos")

    Switch $iScrollCode
        Case $SB_TOP ; user clicked the HOME keyboard key
            DllStructSetData($tSCROLLINFO, "nPos", $iMin)

        Case $SB_BOTTOM ; user clicked the END keyboard key
            DllStructSetData($tSCROLLINFO, "nPos", $iMax)

        Case $SB_LINEUP ; user clicked the top arrow
            DllStructSetData($tSCROLLINFO, "nPos", $iPos - 1)

        Case $SB_LINEDOWN ; user clicked the bottom arrow
            DllStructSetData($tSCROLLINFO, "nPos", $iPos + 1)

        Case $SB_PAGEUP ; user clicked the scroll bar shaft above the scroll box
            DllStructSetData($tSCROLLINFO, "nPos", $iPos - $iPage)

        Case $SB_PAGEDOWN ; user clicked the scroll bar shaft below the scroll box
            DllStructSetData($tSCROLLINFO, "nPos", $iPos + $iPage)

        Case $SB_THUMBTRACK ; user dragged the scroll box
            DllStructSetData($tSCROLLINFO, "nPos", $iTrackPos)
    EndSwitch

    ; // Set the position and then retrieve it.  Due to adjustments
    ; //   by Windows it may not be the same as the value set.

    DllStructSetData($tSCROLLINFO, "fMask", $SIF_POS)
    _GUIScrollBars_SetScrollInfo($hWnd, $SB_VERT, $tSCROLLINFO)
    _GUIScrollBars_GetScrollInfo($hWnd, $SB_VERT, $tSCROLLINFO)
    ;// If the position has changed, scroll the window and update it
    $iPos = DllStructGetData($tSCROLLINFO, "nPos")

    If ($iPos <> $iPosY) Then
        _GUIScrollBars_ScrollWindow($hWnd, 0, $iCharY * ($iPosY - $iPos))
        $iPosY = $iPos
    EndIf

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_VSCROLL

 

Any help to get both working together is appreciated.

Thanks,

Sudeep.

Link to comment
Share on other sites

  • Moderators

sudeepjd,

This is a known problem within AutoIt - I ran into the same problem with my GUIFrame UDF. As you can read by following the various links there is not a lot to be done about it I am afraid.

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to comment
Share on other sites

Thanks Melba for your reply.. Appreciate it.

If it is a known problem, then I cannot solve out of the box. I had to work around it..

What I did was to add in a {TAB} and +{TAB} Hotkey and wrote out a function to manually tab into the controls. This seems to solve my problem of scrolling and tabbing at the same time. 

Here is my pseudo fix, if anyone comes upon the same problem.

#include <GUIConstants.au3>
#include <GUIScrollbars.au3>
#include <WindowsConstants.au3>
#include <WinAPISysWin.au3>

Opt("GUIOnEventMode", 1)  ; Change to OnEvent mode

HotKeySet("{TAB}", "tabFunc")  ;<-- Hotkey for tabbing forward
HotKeySet("+{TAB}", "tabFunc") ;<--Hotkey for tabbing backwards

Global $width=500, $height=500, $titel="Tabtest in Childwindow"

; create the parentwindow
$mainwindow = GUICreate($titel, $width, $height, -1, -1)
GUISetBkColor(0x00ffff)

; show the parentwindow
GUISetState(@SW_SHOW, $mainwindow)

; check on screen
Sleep(1000)

$input_1 = GUICtrlCreateInput("Outside Input", 10, 10, 100, 20)

; create the childwindow with the scrollbars active ->TABSTOPS Dont work
$childwindo1 = GUICreate("child", 220,$height-50, 10 , 50, $WS_CHILD, -1 ,$mainwindow)
_GUIScrollBars_Init($childwindo1, 100, 100)
GUISetBkColor(0xff0000, $childwindo1)
GUISetState(@SW_SHOW, $childwindo1)

$input_2 = GUICtrlCreateInput("Scroll Works",10,10)
$input_3 = GUICtrlCreateInput("Tab Does Not without tabFunc",10,40)

; create the childwindow with the scrollbars active ->TABSTOPS Work, Scroll does not because window moves
$childwindo2 = GUICreate("child", 220, $height, 240 ,0,$WS_CHILD, $WS_EX_CONTROLPARENT ,$mainwindow)

_GUIScrollBars_Init($childwindo2, 100, 100)
GUISetBkColor(0xff0000, $childwindo2)
GUISetState(@SW_SHOW, $childwindo2)

$input_4 = GUICtrlCreateInput("Tab Works",10,10)
$input_5 = GUICtrlCreateInput("Scroll Does Not",10,40)

GUIRegisterMsg($WM_VSCROLL, "WM_VSCROLL")

; register close
GUISetOnEvent($GUI_EVENT_CLOSE, "close_it",$mainwindow)

;loop
While 1
    Sleep(100)  ; Idle around
WEnd

Func close_it()
    ; Exit application
    GUIDelete($mainwindow)
    Exit
EndFunc

$childwindo = GUICreate("child",$width,$height,0,0,$WS_CHILD,-1,$mainwindow)
GUISetBkColor(0xff0000)
GUISetState(@SW_SHOW,$childwindo)

Func tabFunc()
    ;Get the Control in Focus
    $itemInFocusHndl = ControlGetHandle($mainwindow, "", ControlGetFocus($mainwindow))
    $ctrlID = _WinAPI_GetWindowLong($itemInFocusHndl, $GWL_ID)
    
    ;Decide on which side to move
    Switch @HotKeyPressed
    Case "{TAB}"
        $ctrlID = $ctrlID+1
        While _WinAPI_GetClassName(GUICtrlGetHandle($ctrlID)) = "Static"
            $ctrlID = $ctrlID+1
        Wend
    Case "+{TAB}"
        $ctrlID = $ctrlID-1
        While _WinAPI_GetClassName(GUICtrlGetHandle($ctrlID)) = "Static"
            $ctrlID = $ctrlID-1
        Wend
    EndSwitch
    
    ;Shift the focus to the control accordingly.
    ControlFocus($mainwindow, "", GUICtrlGetHandle($ctrlID))
EndFunc

Func WM_VSCROLL($hWnd, $iMsg, $wParam, $lParam)
    #forceref $iMsg, $wParam, $lParam
    Local $iScrollCode = BitAND($wParam, 0x0000FFFF)
    Local $iIndex = -1, $iCharY, $iPosY
    Local $iMin, $iMax, $iPage, $iPos, $iTrackPos

    For $x = 0 To UBound($__g_aSB_WindowInfo) - 1
        If $__g_aSB_WindowInfo[$x][0] = $hWnd Then
            $iIndex = $x
            $iCharY = $__g_aSB_WindowInfo[$iIndex][3]
            ExitLoop
        EndIf
    Next
    If $iIndex = -1 Then Return 0

    ; Get all the vertial scroll bar information
    Local $tSCROLLINFO = _GUIScrollBars_GetScrollInfoEx($hWnd, $SB_VERT)
    $iMin = DllStructGetData($tSCROLLINFO, "nMin")
    $iMax = DllStructGetData($tSCROLLINFO, "nMax")
    $iPage = DllStructGetData($tSCROLLINFO, "nPage")
    ; Save the position for comparison later on
    $iPosY = DllStructGetData($tSCROLLINFO, "nPos")
    $iPos = $iPosY
    $iTrackPos = DllStructGetData($tSCROLLINFO, "nTrackPos")

    Switch $iScrollCode
        Case $SB_TOP ; user clicked the HOME keyboard key
            DllStructSetData($tSCROLLINFO, "nPos", $iMin)

        Case $SB_BOTTOM ; user clicked the END keyboard key
            DllStructSetData($tSCROLLINFO, "nPos", $iMax)

        Case $SB_LINEUP ; user clicked the top arrow
            DllStructSetData($tSCROLLINFO, "nPos", $iPos - 1)

        Case $SB_LINEDOWN ; user clicked the bottom arrow
            DllStructSetData($tSCROLLINFO, "nPos", $iPos + 1)

        Case $SB_PAGEUP ; user clicked the scroll bar shaft above the scroll box
            DllStructSetData($tSCROLLINFO, "nPos", $iPos - $iPage)

        Case $SB_PAGEDOWN ; user clicked the scroll bar shaft below the scroll box
            DllStructSetData($tSCROLLINFO, "nPos", $iPos + $iPage)

        Case $SB_THUMBTRACK ; user dragged the scroll box
            DllStructSetData($tSCROLLINFO, "nPos", $iTrackPos)
    EndSwitch

    ; // Set the position and then retrieve it.  Due to adjustments
    ; //   by Windows it may not be the same as the value set.

    DllStructSetData($tSCROLLINFO, "fMask", $SIF_POS)
    _GUIScrollBars_SetScrollInfo($hWnd, $SB_VERT, $tSCROLLINFO)
    _GUIScrollBars_GetScrollInfo($hWnd, $SB_VERT, $tSCROLLINFO)
    ;// If the position has changed, scroll the window and update it
    $iPos = DllStructGetData($tSCROLLINFO, "nPos")

    If ($iPos <> $iPosY) Then
        _GUIScrollBars_ScrollWindow($hWnd, 0, $iCharY * ($iPosY - $iPos))
        $iPosY = $iPos
    EndIf

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_VSCROLL

 

Basically, pass the Tab through a functions that looks for the handle to the item in focus, gets the Control ID and increments or decrements it and then selects the next appropriate one. 

The only issue with doing this is that Tab and Shift+Tab is being captured by the application and any other windows are not able to use the Tab keys. Is there another way to capture the Tab and Shift+Tab without blocking the inputs to other windows applications?

It tried GUIRegisterMsg($WM_KEYDOWN, 'WM_KEYDOWN') but it does not capture the TAB key needed for this, but seems to capture other keys. 

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

    ConsoleWrite(_WinAPI_GetKeyNameText($lParam) & @CRLF)
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_KEYDOWN

I can use _IsPressed() but I will have to call that in the While loop and it will eat up the CPU Cycles. 

@Melba23 Do you know any other methods I can check if the Tab and Shift+Tab get pressed?

 

Thanks,
Sudeep

Edited by sudeepjd
Updated to account for Static Controls
Link to comment
Share on other sites

You could use GUISetAccelerators function.

Or you can register a LL KB callback function.

Or you can use _WinAPI_SetWindowLong(GUICtrlGetHandle($idXXX), $GWL_WNDPROC, DllCallbackGetPtr($wProcHandle)) for each field that can be tabbed in.

Edited by Nine
Link to comment
Share on other sites

Hi sudeepjd
I registered the WM_NCHITTEST message with interesting results, both child windows having the $WS_EX_CONTROLPARENT extended style. Now everything is scrollable & tabbable.

You can even scroll the child Gui's (without moving them +++) by dragging the mouse anywhere inside the child GUI's, which could be interesting too.

#include <GUIConstants.au3>
#include <GUIScrollbars.au3>
#include <WindowsConstants.au3>

Opt("GUIOnEventMode", 1)  ; Change to OnEvent mode

Global $width=500, $height=500, $titel="Tabtest in Childwindow"

; create the parentwindow
$mainwindow = GUICreate($titel, $width, $height, -1, -1)
GUISetBkColor(0x00ffff)

; show the parentwindow
GUISetState(@SW_SHOW,$mainwindow)

; check on screen
; sleep(1000)

; create the childwindow with the scrollbars active ->TABSTOPS Dont work
$childwindo1 = GUICreate("child", 220, $height, 10, 10, $WS_CHILD, $WS_EX_CONTROLPARENT, $mainwindow)
_GUIScrollBars_Init($childwindo1, 100, 100)
GUISetBkColor(0xff0000, $childwindo1)
GUISetState(@SW_SHOW, $childwindo1)

$input_1 = GUICtrlCreateInput("Scroll Works",10,10)
$input_2 = GUICtrlCreateInput("Tab too",10,40)
$label_2a = GUICtrlCreateLabel("Low label 1", 10, $height + 100)

; create the childwindow with the scrollbars active ->TABSTOPS Work, Scroll does not because window moves
$childwindo2 = GUICreate("child", 220, $height, 240, 10, $WS_CHILD, $WS_EX_CONTROLPARENT, $mainwindow)
_GUIScrollBars_Init($childwindo2, 100, 100)
GUISetBkColor(0xff0000, $childwindo2)
GUISetState(@SW_SHOW, $childwindo2)

$input_3 = GUICtrlCreateInput("Tab Works",10,10)
$input_4 = GUICtrlCreateInput("Scroll too",10,40)
$label_4a = GUICtrlCreateLabel("Low label 2", 10, $height + 100)

GUIRegisterMsg($WM_VSCROLL, "WM_VSCROLL")
GUIRegisterMsg($WM_NCHITTEST, "WM_NCHITTEST")

; register close
GUISetOnEvent($GUI_EVENT_CLOSE, "close_it",$mainwindow)

;loop
While 1
    Sleep(100)  ; Idle around
WEnd

;===============================================
Func close_it()
    ; exit application
    GUIDelete($mainwindow)
    exit
EndFunc

;===============================================
Func WM_VSCROLL($hWnd, $iMsg, $wParam, $lParam)
    #forceref $iMsg, $wParam, $lParam
    Local $iScrollCode = BitAND($wParam, 0x0000FFFF)
    Local $iIndex = -1, $iCharY, $iPosY
    Local $iMin, $iMax, $iPage, $iPos, $iTrackPos

    For $x = 0 To UBound($__g_aSB_WindowInfo) - 1
        If $__g_aSB_WindowInfo[$x][0] = $hWnd Then
            $iIndex = $x
            $iCharY = $__g_aSB_WindowInfo[$iIndex][3]
            ExitLoop
        EndIf
    Next
    If $iIndex = -1 Then Return 0

    ; Get all the vertial scroll bar information
    Local $tSCROLLINFO = _GUIScrollBars_GetScrollInfoEx($hWnd, $SB_VERT)
    $iMin = DllStructGetData($tSCROLLINFO, "nMin")
    $iMax = DllStructGetData($tSCROLLINFO, "nMax")
    $iPage = DllStructGetData($tSCROLLINFO, "nPage")
    ; Save the position for comparison later on
    $iPosY = DllStructGetData($tSCROLLINFO, "nPos")
    $iPos = $iPosY
    $iTrackPos = DllStructGetData($tSCROLLINFO, "nTrackPos")

    Switch $iScrollCode
        Case $SB_TOP ; user clicked the HOME keyboard key
            DllStructSetData($tSCROLLINFO, "nPos", $iMin)

        Case $SB_BOTTOM ; user clicked the END keyboard key
            DllStructSetData($tSCROLLINFO, "nPos", $iMax)

        Case $SB_LINEUP ; user clicked the top arrow
            DllStructSetData($tSCROLLINFO, "nPos", $iPos - 1)

        Case $SB_LINEDOWN ; user clicked the bottom arrow
            DllStructSetData($tSCROLLINFO, "nPos", $iPos + 1)

        Case $SB_PAGEUP ; user clicked the scroll bar shaft above the scroll box
            DllStructSetData($tSCROLLINFO, "nPos", $iPos - $iPage)

        Case $SB_PAGEDOWN ; user clicked the scroll bar shaft below the scroll box
            DllStructSetData($tSCROLLINFO, "nPos", $iPos + $iPage)

        Case $SB_THUMBTRACK ; user dragged the scroll box
            DllStructSetData($tSCROLLINFO, "nPos", $iTrackPos)
    EndSwitch

    ; // Set the position and then retrieve it.  Due to adjustments
    ; //   by Windows it may not be the same as the value set.

    DllStructSetData($tSCROLLINFO, "fMask", $SIF_POS)
    _GUIScrollBars_SetScrollInfo($hWnd, $SB_VERT, $tSCROLLINFO)
    _GUIScrollBars_GetScrollInfo($hWnd, $SB_VERT, $tSCROLLINFO)
    ;// If the position has changed, scroll the window and update it
    $iPos = DllStructGetData($tSCROLLINFO, "nPos")

    If ($iPos <> $iPosY) Then
        _GUIScrollBars_ScrollWindow($hWnd, 0, $iCharY * ($iPosY - $iPos))
        $iPosY = $iPos
    EndIf

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_VSCROLL

;===============================================
Func WM_NCHITTEST($hWnd, $iMsg, $wParam, $lParam)
    #forceref $iMsg, $wParam, $lParam

    If $hWnd = $childwindo1 Or $hWnd = $childwindo2 Then
        Return $HTVSCROLL
    EndIf

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NCHITTEST

 

Link to comment
Share on other sites

@Nine Yes.. GUISetAccelerators can be used, and it needs to be linked to a dummy control, but should work.

Too many controls to tab through for the _WinAPI_SetWindowLong, was thinking about it, but too much coding work, for a workaround :-)

Thanks for the suggestions though. Much appreciated.

@pixelsearch Your the man!!! Thank you...

You are awesome... I never would have thought that NCHITTEST could be used for this.

Thanks a ton.

Link to comment
Share on other sites

On 10/25/2020 at 3:20 PM, pixelsearch said:

You can even scroll the child Gui's (without moving them +++) by dragging the mouse anywhere inside the child GUI's, which could be interesting too.

I reworked a bit Func WM_NCHITTEST() so it should be more flexible.
For example, maybe someone doesn't want the vertical scrolling to happen when you drag the mouse anywhere in the child windows.

Also I posted all 26 WM_NCHITTEST() return values, sorted by code (then by value +++) in this link (test forum) . It's interesting to see in the console what's the return value when you move your mouse here & there, by uncommenting this line :

; ConsoleWrite($iRet)

This amended version of Func WM_NCHITTEST() should be useful to rework in case the child windows are resizable, got a caption etc... The point is to first check WM_NCHITTEST() return value, only then decide what to do.

WM_NCHITTEST is a really surprising message, very useful :)

#include <GUIConstants.au3>
#include <GUIScrollbars.au3>
#include <WindowsConstants.au3>

Opt("GUIOnEventMode", 1)  ; Change to OnEvent mode

Global $width=500, $height=500, $titel="Tabtest in Childwindow", $hDll = DllOpen("user32.dll")

; create the parentwindow
$mainwindow = GUICreate($titel, $width, $height, -1, -1)
GUISetBkColor(0x00ffff)

; show the parentwindow
GUISetState(@SW_SHOW,$mainwindow)

; create the 1st childwindow with the scrollbars active
$childwindo1 = GUICreate("child", 220, $height, 10, 10, $WS_CHILD, $WS_EX_CONTROLPARENT, $mainwindow)
_GUIScrollBars_Init($childwindo1, 100, 100)
GUISetBkColor(0xff0000, $childwindo1)
GUISetState(@SW_SHOW, $childwindo1)

$input_1 = GUICtrlCreateInput("Scroll Works",10,10)
$input_2 = GUICtrlCreateInput("Tab too",10,40)
$label_2a = GUICtrlCreateLabel("Low label 1", 10, $height + 100)

; create the 2nd childwindow with the scrollbars active
$childwindo2 = GUICreate("child", 220, $height, 240, 10, $WS_CHILD, $WS_EX_CONTROLPARENT, $mainwindow)
_GUIScrollBars_Init($childwindo2, 100, 100)
GUISetBkColor(0xff0000, $childwindo2)
GUISetState(@SW_SHOW, $childwindo2)

$input_3 = GUICtrlCreateInput("Tab Works",10,10)
$input_4 = GUICtrlCreateInput("Scroll too",10,40)
$label_4a = GUICtrlCreateLabel("Low label 2", 10, $height + 100)

GUIRegisterMsg($WM_VSCROLL, "WM_VSCROLL")
GUIRegisterMsg($WM_NCHITTEST, "WM_NCHITTEST")

; register close
GUISetOnEvent($GUI_EVENT_CLOSE, "close_it",$mainwindow)

;loop
While 1
    Sleep(100)  ; Idle around
WEnd

;===============================================
Func close_it()
    ; exit application
    GUIDelete($mainwindow)
    DllClose($hDll)
    exit
EndFunc

;===============================================
Func WM_VSCROLL($hWnd, $iMsg, $wParam, $lParam)
    #forceref $iMsg, $wParam, $lParam
    Local $iScrollCode = BitAND($wParam, 0x0000FFFF)
    Local $iIndex = -1, $iCharY, $iPosY
    Local $iMin, $iMax, $iPage, $iPos, $iTrackPos

    For $x = 0 To UBound($__g_aSB_WindowInfo) - 1
        If $__g_aSB_WindowInfo[$x][0] = $hWnd Then
            $iIndex = $x
            $iCharY = $__g_aSB_WindowInfo[$iIndex][3]
            ExitLoop
        EndIf
    Next
    If $iIndex = -1 Then Return 0

    ; Get all the vertial scroll bar information
    Local $tSCROLLINFO = _GUIScrollBars_GetScrollInfoEx($hWnd, $SB_VERT)
    $iMin = DllStructGetData($tSCROLLINFO, "nMin")
    $iMax = DllStructGetData($tSCROLLINFO, "nMax")
    $iPage = DllStructGetData($tSCROLLINFO, "nPage")
    ; Save the position for comparison later on
    $iPosY = DllStructGetData($tSCROLLINFO, "nPos")
    $iPos = $iPosY
    $iTrackPos = DllStructGetData($tSCROLLINFO, "nTrackPos")

    Switch $iScrollCode
        Case $SB_TOP ; user clicked the HOME keyboard key
            DllStructSetData($tSCROLLINFO, "nPos", $iMin)

        Case $SB_BOTTOM ; user clicked the END keyboard key
            DllStructSetData($tSCROLLINFO, "nPos", $iMax)

        Case $SB_LINEUP ; user clicked the top arrow
            DllStructSetData($tSCROLLINFO, "nPos", $iPos - 1)

        Case $SB_LINEDOWN ; user clicked the bottom arrow
            DllStructSetData($tSCROLLINFO, "nPos", $iPos + 1)

        Case $SB_PAGEUP ; user clicked the scroll bar shaft above the scroll box
            DllStructSetData($tSCROLLINFO, "nPos", $iPos - $iPage)

        Case $SB_PAGEDOWN ; user clicked the scroll bar shaft below the scroll box
            DllStructSetData($tSCROLLINFO, "nPos", $iPos + $iPage)

        Case $SB_THUMBTRACK ; user dragged the scroll box
            DllStructSetData($tSCROLLINFO, "nPos", $iTrackPos)
    EndSwitch

    ; // Set the position and then retrieve it.  Due to adjustments
    ; //   by Windows it may not be the same as the value set.

    DllStructSetData($tSCROLLINFO, "fMask", $SIF_POS)
    _GUIScrollBars_SetScrollInfo($hWnd, $SB_VERT, $tSCROLLINFO)
    _GUIScrollBars_GetScrollInfo($hWnd, $SB_VERT, $tSCROLLINFO)
    ;// If the position has changed, scroll the window and update it
    $iPos = DllStructGetData($tSCROLLINFO, "nPos")

    If ($iPos <> $iPosY) Then
        _GUIScrollBars_ScrollWindow($hWnd, 0, $iCharY * ($iPosY - $iPos))
        $iPosY = $iPos
    EndIf

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_VSCROLL

;===============================================
Func WM_NCHITTEST($hWnd, $iMsg, $wParam, $lParam)

;~  If $hWnd = $childwindo1 Or $hWnd = $childwindo2 Then
;~      Return $HTVSCROLL
;~  EndIf

    If $hWnd = $childwindo1 Or $hWnd = $childwindo2 Then

        ; This works but opens & closes user32.dll plenty of times => commented.
        ; Local $iRet = _WinAPI_DefWindowProc($hWnd, $iMsg, $wParam, $lParam)

        ; Same as _WinAPI_DefWindowProc() in WinAPISysWin.au3 but uses $hDll
        Local $iRet = DllCall($hDll, "lresult", "DefWindowProc", "hwnd", $hWnd, _
            "uint", $iMsg, "wparam", $wParam, "lparam", $lParam)[0]

        ; ConsoleWrite($iRet)

        Switch $iRet
            Case $HTVSCROLL
                Return $HTVSCROLL
            Case Else
                Return $HTCLIENT
        EndSwitch
    EndIf

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NCHITTEST

 

Edited by pixelsearch
typo
Link to comment
Share on other sites

  • Moderators

pixelsearch,

A nice piece of detective work. Bravo!

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to comment
Share on other sites

@Melba23  : thanks for the kind words, much appreciated :)

Could you please give us an advice concerning a frequent related question ?
Topic DllCall, here is what we read in the help file :

If a dll filename is given then the DLL is automatically loaded and then closed at the end of the call. If you want to manually control the loading and unloading of the DLL then you should use DllOpen() and DllClose() and use a handle instead of a filename in this function.

It explains why I used "the handle way" in the precedent script :

DllCall($hDll, ...)

Instead of :

_WinAPI_DefWindowProc(...)
; which means :
DllCall("user32.dll", ...)

I did it only because the WM_NCHITTEST() call is done dozen of times, avoiding a non-stop dll "loading-unloading" process. I don't do this in other scripts where DllCall is used much less often.

Now the question : is it really important to "manually control the loading and unloading of the DLL" during DllCall ?

Because if it is, then we should examinate each and every Include function, to determine if the use of a Dll handle is the "good" way, as soon as this function is called plenty of times in a script.

While writing this, it makes me think at the built in FileWrite() function, with these words found in the help file :

If a filename is given rather than a file handle, the file will be opened and closed during the function call. For parsing large text files this will be much slower than using filehandles.

But what is expressly recommended for FileWrite (i.e "this will be much slower") isn't so clearly stipulated in the DllCall topic.
Thanks.

Link to comment
Share on other sites

The difference is really marginal :

#include <Constants.au3>
#include <WindowsConstants.au3>
#include <WinAPISysWin.au3>

$hGUI = GUICreate("")

$hTimer = TimerInit()
For $i = 1 to 1000
  $iRet = _WinAPI_DefWindowProc($hGUI, $WM_USER, 0, 0)
Next
ConsoleWrite (TimerDiff($hTimer) & @CRLF)

$hTimer = TimerInit()
For $i = 1 to 1000
  $iRet = DllCall("user32.dll", "lresult", "DefWindowProc", "hwnd", $hGUI, "uint", $WM_USER, "wparam", 0, "lparam", 0)[0]
Next
ConsoleWrite (TimerDiff($hTimer) & @CRLF)
+>Setting Hotkeys...--> Press Ctrl+Alt+Break to Restart or Ctrl+BREAK to Stop.
15.7746044264245
11.7413117173742
+>08:34:19 AutoIt3.exe ended.rc:0
+>08:34:19 AutoIt3Wrapper Finished.
>Exit code: 0    Time: 1.632

4ms difference between both over 1000 calls.  Unless there is an extreme necessity of light speed, I never use that feature.  It makes the script more readable using _WinAPI*.

Link to comment
Share on other sites

Hi Nine,
Thanks for your test.
My point was a bit different, opening user32.dll only once, then using its handle :

#include <Constants.au3>
#include <WindowsConstants.au3>
#include <WinAPISysWin.au3>

$hGUI = GUICreate("")

$hTimer = TimerInit()
For $i = 1 to 10000
  $iRet = _WinAPI_DefWindowProc($hGUI, $WM_USER, 0, 0)
Next
ConsoleWrite (TimerDiff($hTimer) & @CRLF)

$hDll = DllOpen("user32.dll")

$hTimer = TimerInit()
For $i = 1 to 10000
  $iRet = DllCall($hDll, "lresult", "DefWindowProc", "hwnd", $hGUI, "uint", $WM_USER, "wparam", 0, "lparam", 0)[0]
Next
ConsoleWrite (TimerDiff($hTimer) & @CRLF)

GUIDelete($hGUI)
DllClose($hDll)

But even like that, there shouldn't be much time difference between both ways, so I probably won't use the handle way anymore when using DllCall

 

Edited by pixelsearch
Modified script to open user.dll only before the 2nd timer
Link to comment
Share on other sites

  • Moderators

pixelsearch,

Quote

Could you please give us an advice concerning a frequent related question ?

is it really important to "manually control the loading and unloading of the DLL" during DllCall ?

Here is my hobbyist understanding of the situation.

DLLs are "loaded" as required but are not necessarily immediately "unloaded" once the call is over. They remain in memory with an internal counter value showing that the DLL is in use. If the same DLL is called by another process then the same code is used and the counter increased to show that there are now 2 processes using it. If one of these processes "closes" the DLL, then all that happens is that the counter is reduced and the code remains in memory as long as there is at least one process still using the code.  This would imply that you should indeed open and close the DLL when using it, but with the more common DLLs there is a pretty good chance that the code is already in memory and so there is no penalty for not doing so. There is also the fact that Windows is very likely to do some caching if the same DLL is constantly used, so the DLL code may well be kept in memory for a while even with a zero count, but I am unsure whether this actually happens. 

The situation regarding file handles is not really analogous as there is no "loading" of the file into memory in the same manner - just the creation of an index in a "small" finite sized array of "open" filenames.

I hope that helps. And if anyone wants to expand, or completely change, what I have written above please feel free to do so.

M23

 

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to comment
Share on other sites

Comment: 'System crash', 'stack overflow', 'unable to obtain handles', 'memory exhausted' error codes is what happens if you create new handles and never close them, especially in re-entrant loops.

How do I know?

Grins (or cringes)

Wait till you try and debug DLLhell, where there are different versions of exactly the same named DLL scattered across your system, all with similar internal function names but different functionality, and the order in which they are encountered by your software determines how far your system remains stable before it crashes and burns. Yes, I'm looking at you Microsoft, Oracle, Nero, Java and Adobe.

Hard and fast Rulez (from experience): If you open it, close it. If it is up, put it down when you flush. Keep subroutines and calls open for as short a time as possible. Be aware of local vs global variables. Don't be lazy with variable naming - 'temp' is often found to be a global variable. Read the doco. Test for return codes. Check for version levels during API calls. Always include a 'or else' clause. Make your error codes meaningful and easy to document. Declare your global variables at the top separately. Pseudocode is not wasted code. Planning is not an overhead. Use the right tool for the job. COBOL is not a good environment to write a keylogger package, and Excel is not a good covid data tracker! All developers speak profanity fluently. It is the first and most developed language universally. Getting your code to compile the first time is nearly as good as sex - nearly!

Edited by Confuzzled
Link to comment
Share on other sites

Create an account or sign in to comment

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

Create an account

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

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...