Jump to content

ListView strange behavior


Recommended Posts

What do you think about this code? Bug or intended behavior?

#include <GuiListView.au3>
#include <Array.au3>

$hGUI = GUICreate('Test', 400, 400)
$cList = GUICtrlCreateListView('Col 1|Col 2|Col 3', 10, 10, 380, 380)
GUISetState(@SW_SHOW, $hGUI)

GUICtrlCreateListViewItem('Row 1 - Col 1|Row 1 - Col 2|Row 1 - Col 3', $cList)
GUICtrlCreateListViewItem('Row 2 - Col 1|Row 2 - Col 2|Row 2 - Col 3', $cList)
GUICtrlCreateListViewItem('Row 3 - Col 1|Row 3 - Col 2|Row 3 - Col 3', $cList)

_GUICtrlListView_SetItemSelected($cList, 1, True)
$iSelected = _GUICtrlListView_GetSelectedIndices($cList)

; Oups - this fails
$aText = _GUICtrlListView_GetItemTextArray($cList, $iSelected)
_ArrayDisplay($aText)

; This works
$aText = _GUICtrlListView_GetItemTextArray($cList, Number($iSelected))
_ArrayDisplay($aText)

; This is 0-based sub item index, not 1-based as it's stated in the help file
MsgBox(0, '', _GUICtrlListView_GetItemText($cList, Number($iSelected), 1))

Do
Until GUIGetMsg() = -3

It looks like AutoIt does care in some cases about the data type used for some parameters in _GUICtrlListView_* functions (_GUICtrlListView_GetItemTextArray it's not the only function with this behavior). For some reason if the index it's not a number these functions fails. This has been encountered before by others but never properly addressed. Even if I suspect AutoIt internally it's confused sometimes about how to interpret data types, at least the UDF functions should ensure that proper data types are passed as parameters when some WinAPIs are called.

Also it looks like the documentation it's dubious around sub item index in _GUICtrlListView_GetItemText() claiming it's 1-based when it's 0-based as shown in the example above.

Any thoughts are appreciated.

When the words fail... music speaks.

Link to comment
Share on other sites

Func _GUICtrlListView_GetItemTextString($hWnd, $iItem = -1)
    Local $sRow = "", $sSeparatorChar = Opt('GUIDataSeparatorChar'), $iSelected
    If $iItem = -1 Then
        $iSelected = _GUICtrlListView_GetNextItem($hWnd) ; get current row selected
    Else
        $iSelected = $iItem + 1 - 1 ; get row ; "+ 1 - 1" by argumentum
    EndIf

    ; here is required to check if _GUICtrlListView_GetNextItem() has failed
    If $iSelected < 0 Or $iSelected > _GUICtrlListView_GetItemCount($hWnd) - 1 Then Return SetError(1, 0, 0)

    For $x = 0 To _GUICtrlListView_GetColumnCount($hWnd) - 1
        $sRow &= _GUICtrlListView_GetItemText($hWnd, $iSelected, $x) & $sSeparatorChar
    Next
    Return StringTrimRight($sRow, 1)
EndFunc   ;==>_GUICtrlListView_GetItemTextString

would this do it ? ( y'all MVPs )

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

Link to comment
Share on other sites

@argumentum forcing some arithmetics would definitely convert the index to a number but it's just a local fix and this issue hits in other functions as well. For a general fix I think GUICtrlSendMsg() should ensure (and convert if it's neccesarily) that parameters have proper data types. The example below highlights the real issue.

#include <GuiListView.au3>

$hGUI = GUICreate('Test', 400, 400)
$cList = GUICtrlCreateListView('Col 1|Col 2|Col 3', 10, 10, 380, 380)
GUISetState(@SW_SHOW, $hGUI)

GUICtrlCreateListViewItem('Row 1 - Col 1|Row 1 - Col 2|Row 1 - Col 3', $cList)
GUICtrlCreateListViewItem('Row 2 - Col 1|Row 2 - Col 2|Row 2 - Col 3', $cList)
GUICtrlCreateListViewItem('Row 3 - Col 1|Row 3 - Col 2|Row 3 - Col 3', $cList)

_GUICtrlListView_SetItemSelected($cList, 1, True)
$iSelected = _GUICtrlListView_GetSelectedIndices($cList)

$tBuffer = DllStructCreate('char Text[4096]')
$tItem = DllStructCreate($tagLVITEM)

$tBuffer.Text = ''
$tItem.Mask = $LVIF_TEXT
$tItem.SubItem = 0
$tItem.Text = DllStructGetPtr($tBuffer)
$tItem.TextMax = DllStructGetSize($tBuffer)

GUICtrlSendMsg($cList, $LVM_GETITEMTEXTA, $iSelected, DllStructGetPtr($tItem))
ConsoleWrite('Text: ' & $tBuffer.Text & @CRLF)

GUICtrlSendMsg($cList, $LVM_GETITEMTEXTA, Number($iSelected), DllStructGetPtr($tItem))
ConsoleWrite('Text: ' & $tBuffer.Text & @CRLF)

Do
Until GUIGetMsg() = -3

Is there any case when wParam or lParam are not integers? If not then the function should always convert the parameter to the proper signed/unsigned integer.

When the words fail... music speaks.

Link to comment
Share on other sites

31 minutes ago, Andreik said:

...arithmetics would definitely convert the index to a number but it's just a local fix...

I was hoping for @jpm to look at it. I don't have a setup to look at the current beta.
Reformatted my PC and haven't reinstall the MVP setup yet

My all encompassing fix:

Spoiler
Func __GUICtrl_SendMsg($hWnd, $iMsg, $iIndex, ByRef $tItem, $tBuffer = 0, $bRetItem = False, $iElement = -1, $bRetBuffer = False, $iElementMax = $iElement)
    If $iElement > 0 Then
        DllStructSetData($tItem, $iElement, DllStructGetPtr($tBuffer))
        If $iElement = $iElementMax Then DllStructSetData($tItem, $iElement + 1, DllStructGetSize($tBuffer))
    EndIf

    Local $iRet
    If IsHWnd($hWnd) Then
        If _WinAPI_InProcess($hWnd, $__g_hGUICtrl_LastWnd) Then
            $iRet = DllCall("user32.dll", "lresult", "SendMessageW", "hwnd", $hWnd, "uint", $iMsg, "wparam", $iIndex, "struct*", $tItem)[0]
        Else
            Local $iItem = DllStructGetSize($tItem)
            Local $tMemMap, $pText
            Local $iBuffer = 0
            If ($iElement > 0) Or ($iElementMax = 0) Then $iBuffer = DllStructGetSize($tBuffer)
            Local $pMemory = _MemInit($hWnd, $iItem + $iBuffer, $tMemMap)
            If $iBuffer Then
                $pText = $pMemory + $iItem
                If $iElementMax Then
                    DllStructSetData($tItem, $iElement, $pText)
                Else
                    $iIndex = $pText
                EndIf
                _MemWrite($tMemMap, $tBuffer, $pText, $iBuffer)
            EndIf
            _MemWrite($tMemMap, $tItem, $pMemory, $iItem)
            $iRet = DllCall("user32.dll", "lresult", "SendMessageW", "hwnd", $hWnd, "uint", $iMsg, "wparam", $iIndex, "ptr", $pMemory)[0]
            If $iBuffer And $bRetBuffer Then _MemRead($tMemMap, $pText, $tBuffer, $iBuffer)
            If $bRetItem Then _MemRead($tMemMap, $pMemory, $tItem, $iItem)
            _MemFree($tMemMap)
        EndIf
    Else
        $iRet = GUICtrlSendMsg($hWnd, $iMsg, $iIndex + 1 - 1, DllStructGetPtr($tItem))
    EndIf

    Return $iRet
EndFunc   ;==>__GUICtrl_SendMsg

...because it all boils down to that   =/

 

Edited by argumentum
more

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

Link to comment
Share on other sites

52 minutes ago, jpm said:

...it is normal for me that the return is a string...

if __GUICtrl_SendMsg() gets patch along the lines of the change I made:
$iRet = GUICtrlSendMsg($hWnd, $iMsg, $iIndex + 1 - 1, DllStructGetPtr($tItem))

That would solve all these to behave as expected** ?

**because is expected don't mean that is computationally correct but will perform the scripter "wishes/expectations" out of this scripting language. After all, the change does not brake past scripts and makes AutoIt friendlier :)

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

Link to comment
Share on other sites

Posted (edited)

@jpm In AutoIt we don't really have to care much about data types because AutoIt decides how to interpret the datatype of a variable depending on the situation. I know the result of _GUICtrlListView_GetSelectedIndices() it's a string even for a single item selected, so none question how this function work or what returns. My question it's why AutoIt decides that wParam in GUICtrlSendMsg() can be a string and why it's not converted to the apropriate data type?

@argumentum Your fix would work but the real problem it's that the function itself doesn't work correct unless you ensure the parameters have the proper data type, whici is kinda against how AutoIt works in general. It should be the function the one that validate the parameters and decide if a data conversion it's required.

I would argue that how GUICtrlSendMsg() works right now it's not how AutoIt works in general. The line below works as expected because the argument of the Cos function it's a number so the string passed as argument it's converted (internally, not by the user) to value zero. This is the expected behavior for GUICtrlSendMsg(), wParam and lParam should be internally converted to proper unsigned / signed integers.

MsgBox(0, '', Cos("Whatever"))

 

Edited by Andreik

When the words fail... music speaks.

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

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...