Jump to content
Sign in to follow this  
Armag3ddon

Ghost inputs; where start debugging?

Recommended Posts

Armag3ddon

So this is a tough one. Kept me busy all day long especially to configure AutoIt Debugger to start the application in a decent amount of time. My project exceeded any reasonable number of lines I could post into the forum and even if I did, you wouldn't be able to test it because it's more or less a file explorer for a somewhat small game.

I will try to compile a script which demonstrates my observations tomorrow, though I fear when I do, the buggy behaviour will be gone.

Anyway, here's a rough overview over what I have so far:

A treeview created via the UDFs - the main file tree showing the files. A proper file explorer needs all kind of operations you are used to, like copy, cut, paste etc. so I did this kind of stuff.

You'd expect these options accessible via a context menu, so I did that one too. The how to do this I found here:

My setup so far:

treeview via _GUICtrlTreeView_Create etc. etc.

and context menu via GUICtrlCreateContextMenu

->

; Context menu (fileview)
Local $dummy = GUICtrlCreateDummy()
Global $ctxmenu = GUICtrlCreateContextMenu($dummy) ; Needs to be hooked up on the dummy
Global $contextmenu = GUICtrlGetHandle($ctxmenu) ; Saved for later use, the menu is called via this handle
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Context_New"), $ctxmenu), "NewFile")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Context_Cut"), $ctxmenu), "CutFile")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Context_Copy"), $ctxmenu), "CopyFile")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Context_Paste"), $ctxmenu), "PasteFile")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Context_Delete"), $ctxmenu), "DeleteFile")
GUICtrlCreateMenuItem("", $ctxmenu)
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Context_Refresh"), $ctxmenu), "RefreshFileViewWrapper")

LoadStr is just a function that returns a localized language string. The context menu is show on rightclick - of course.

This is a piece from my WM_NOTIFY:

Case $NM_RCLICK ; right click
     $mouse = _WinAPI_GetMousePos(True, $main)
     $hit_test = _GUICtrlTreeView_HitTest($fileview, DllStructGetData($mouse, "X"), DllStructGetData($mouse, "Y"))
     $selitem = _GUICtrlTreeView_HitTestItem($fileview, DllStructGetData($mouse, "X"), DllStructGetData($mouse, "Y"))
     If $hit_test = 1 Or $hit_test = 32 Then
      FileviewLoseSelection()
      RefreshContextMenu()
      _GUICtrlMenu_TrackPopupMenu($contextmenu, $main)
      Return 1
     Else
      If $selitem = 0 Then Return $GUI_RUNDEFMSG
      _GUICtrlTreeView_SelectItem($fileview, $selitem)
      _GUICtrlTreeView_SetSelected($fileview, $selitem)
      $no_selection = False
     RefreshContextMenu()
      _GUICtrlMenu_TrackPopupMenu($contextmenu, $main)
      Return 1
     EndIf

I test to see whether the user clicked on an item - if yes, select that item. If no, clear the selection of the treeview (a bit hacky though but sufficient for my purpose) so the user may create new files in the root directory (which is the game dir). That's what FileviewLoseSelection() does.

RefreshContextMenu does a bunch of de-/activation of context menu entries e.g. deactivating the Paste option of no file was selected for cutting/copying:

Func RefreshContextMenu()
ConsoleWrite("RefreshContextMenu" & @CRLF)
; Paste operation only accessible if file to paste
If Not $file_operation Then
  _GUICtrlMenu_SetItemDisabled($contextmenu, 3)
Else
  _GUICtrlMenu_SetItemEnabled($contextmenu, 3)
EndIf

[...]

The weirdness begins now. I wanted to test everything when a 'crash' occured. I looked further into this and could narrow it down to have something to do with the context menu. I discovered, using AutoIt Debugger, that this was no real crash but that my regular end program routine was called. But not only that, I got random inputs on every menu item in my whole GUI. This isn't limited to the context menu but also triggers random entries from the system menu of the GUI:

Global $optmenu = GUICtrlCreateMenu(LoadStr("Menu_Options"))
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Menu_Prefs"), $optmenu), "OpenPreferences")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Menu_Quit"), $optmenu), "EditorClose")
Global $infomenu = GUICtrlCreateMenu(LoadStr("Menu_Info"))
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Menu_About"), $infomenu), "ShowInfo")
Global $info_window ; To save the info window
Global $prefs_window ; To save the preferences window

There never was a crashed but the Menu_Quit entry was triggered. Sometimes the preferences window opened, sometimes the about screen. Sometimes the new file dialogue opens (first context menu entry), sometimes the file tree is refreshed (6th entry, not counting the separator) or I am asked if I want to delete the file. These ghost inputs occur on two different occasions:

1. The context menu is opened; AutoIt Debugger told me that RefreshContextMenu was called, ConsoleWrite wrote the console but then the execution of RefreshContextMenu was interrupted and the other function kicked in e.g. I get prompted for file deletion. After this interrupting process is done, RefreshContextMenu continues and the context menu opens.

2. I select an entry from the context menu. The entry is processed and when that's finished the random function is done. This often was my 'crash' because Menu_Quit was called. Though I get a crash report from Windows and everything.

After I disabled the creation of the system menu, at least quitting of the program and opening of the preferences stopped. So that's my current status. I have no idea how to access this problem. AutoIt Debugger doesn't give me any more useful information than the interruption of the context refresh function. I'm happy to provide anyone who is willing to help with the full source (~33 MB). I'm desperate. I invested already ~5 hours into finding this bug.

To wrap it all up, here's my creation routine of the GUI and the three elements in question (treeview, system menu, context menu). Maybe someone can make something out of it:

; Create the GUI
Opt("GUIOnEventMode", 1)
Opt("GUICloseOnESC", 0)
Opt("PixelCoordMode", 2)
Opt("TrayIconHide", 1)
Global $main = GUICreate("OpenClonk Editor", 800, 600, -1, -1, BitOR($WS_CAPTION, $WS_MINIMIZEBOX, $WS_SYSMENU, $DS_CONTEXTHELP, $WS_SYSMENU, $WS_VISIBLE, $WS_CLIPCHILDREN))
GUISetOnEvent($GUI_EVENT_CLOSE, "EditorClose", $main)
GUISetOnEvent($GUI_EVENT_PRIMARYUP, "EndDrag", $main)
GUISetState(@SW_DISABLE, $main)
; Show splash screen
SplashImageOn(LoadStr("General_InitMenu"), ".\res\Splash.jpg", 320, 240)

; Options menu
Global $optmenu = GUICtrlCreateMenu(LoadStr("Menu_Options"))
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Menu_Prefs"), $optmenu), "OpenPreferences")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Menu_Quit"), $optmenu), "EditorClose")
Global $infomenu = GUICtrlCreateMenu(LoadStr("Menu_Info"))
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Menu_About"), $infomenu), "ShowInfo")
Global $info_window ; To save the info window
Global $prefs_window ; To save the preferences window

; Context menu (fileview)
Local $dummy = GUICtrlCreateDummy() ; This is necessary because autoit handles context menus poorly. It won't work with _GUICtrlTreeView (TreeView created via autoit UDFs)
Global $ctxmenu = GUICtrlCreateContextMenu($dummy) ; Needs to be hooked up on the dummy
Global $contextmenu = GUICtrlGetHandle($ctxmenu) ; Saved for later use, the menu is called via this handle
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Context_New"), $ctxmenu), "NewFile")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Context_Cut"), $ctxmenu), "CutFile")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Context_Copy"), $ctxmenu), "CopyFile")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Context_Paste"), $ctxmenu), "PasteFile")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Context_Delete"), $ctxmenu), "DeleteFile")
GUICtrlCreateMenuItem("", $ctxmenu)
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Context_Refresh"), $ctxmenu), "RefreshFileViewWrapper")
If $C4GROUP_EXE <> "" Then
GUICtrlCreateMenuItem("", $ctxmenu)
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Context_C4GPack"), $ctxmenu), "PackFile")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem(LoadStr("Context_C4GUnpack"), $ctxmenu), "UnpackFile")
EndIf

SplashImageOn(LoadStr("General_InitFiles"), ".\res\Splash.jpg", 320, 240)

; Tree file view
Global $fileview = _GUICtrlTreeView_Create($main, 0,0, 200,560, BitOR($TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_SHOWSELALWAYS, $TVS_EDITLABELS, $TVIS_DROPHILITED, $WS_TABSTOP, $TVS_CHECKBOXES), $WS_EX_CLIENTEDGE)
InitFileView()
Local $hFunc
DIM $TVN_BEGINDRAG
DIM $TVN_BEGINLABELEDIT
DIM $TVN_ENDLABELEDIT
; For drag and drop behaviour inside the treeview
Global $drag_item, $next_item, $prev_item, $hover_item, $hover_time
; Drag and drop outside the treeview, explorer extension
_OLEInitialize()
GUIRegisterMsg($WM_DI_GETDRAGIMAGE, "MY_GETDRAGIMAGE")
Global $IDragSourceHelper = _ObjCoCreateInstance($CLSID_DragDropHelper,$IID_IDragSourceHelper,$IDragSourceHelper_vTable)
Global $objIDataSource
; Proper detecting of the return key is broken in AutoIt, this is used for a workaround:
Global $wProcHandle = DllCallbackRegister("_WindowProc", "int", "hwnd;uint;wparam;lparam")
Global $wProcOld = _WinAPI_SetWindowLong($fileview, $GWL_WNDPROC, DllCallbackGetPtr($wProcHandle))
Global Const $VK_RETURN = 0x0D ; Enter key
; For renaming
Global $edit_label = False
Global $just_edited = ""
Global Const $VK_F2 = 0x71 ; F2 key
; For checking .ocd
Global $checked_item = 0
Global $checked_path = ""
Global $checked_state = False
; Workaround to have the treeview "lose" its selection
Global $no_selection = False
; Temporary memory for sorting the elements (after renaming/moving/creation)
Global $sort_memory[1]
$sort_memory[0] = ""

Share this post


Link to post
Share on other sites
Zedna

For such problems is definitely good idea to use eliminating method.

Make copy of your script and then continuosly remove unneccesary code pieces until bug dissapear.

Also good idea is to create as small as possible reproducing script.

Share this post


Link to post
Share on other sites
Armag3ddon

Thanks so far. I did as you said and removed piece by piece of the code. I could narrow it down but the behaviour still looked kind of random when it occured. I then realised that it was my own behaviour that triggered the bug. If I selected an entry before rightclicking it, nothing unusual happened. It's like this:

- Item is not selected. Left click, item gets selected. Right click on selected item, context menu opens, everything's fine.

- Item is not selected. Right click on unselected item, context menu opens, weird stuff happens.

It has something to do with calling _GUICtrlTreeView_SelectItem($fileview, $selitem) in my $NM_RCLICK case in WM_NOTIFY. Still clueless how to solve though. I could of course do a workaround without reselection.

Edited by Armag3ddon

Share this post


Link to post
Share on other sites
Zedna

If you have code in WM_NOTIFY then problems may be due to "blocking" (long lasting) code.

This results in weird behaviour.

Search this forum/wiki about blocking/non-blocking code in message loop.

EDIT:

http://www.autoitscript.com/wiki/GUIRegisterMsg

Edited by Zedna

Share this post


Link to post
Share on other sites
Armag3ddon

I can't seem to find anything like this in my code but maybe my mind is just stuck. But I was able to recreate the bug in a test script!

So here it is. On startup you get a simple GUI with a treeview element in it. You can rightclick to open a context menu with several entries. Only Cut und Paste work, the others just call a dummy function that sends the message "Clicked on either New, Copy, Delete or Refresh"

To reproduce my bug open the first group ([10] New Item) and afterwards select [29] New Child using the arrow keys (it won't work by leftclicking). Whenever I select the entry (not deselecting) I get the message Clicked on either New, Copy, Delete or Refresh. This should not happen though.

Hopefully, this will lead someone to the bug. Here it is:

#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6
#include <GuiConstantsEx.au3>
#include <GuiTreeView.au3>
#include <WindowsConstants.au3>
#include <GuiMenu.au3>
Opt('MustDeclareVars', 1)
Opt("GUIOnEventMode", 1)
$Debug_TV = False ; Check ClassName being passed to functions, set to True and use a handle to another control to see it work
Global Const $TVSORTCB = "ptr Parent;ptr Compare;lparam SortParam;"
Global $Data[500]
Global $operation = False
Global $hWnd_GUI, $hTreeView, $contextmenu
_Main()
While 1
Sleep(10)
WEnd
Func _Main()
    Local $hItem[10], $hChildItem[30], $hGrandChildItem[90], $hGrandGrantChildItem[180]
    Local $iCItem = 0, $iGCItem = 0, $IGGCItem = 0, $i1 = 10, $i2 = 30, $i3 = 90, $i4 = 180
    Local $iStyle = BitOR($TVS_EDITLABELS, $TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_DISABLEDRAGDROP, $TVS_SHOWSELALWAYS)
    $hWnd_GUI = GUICreate("TreeView Sort", 400, 300)
GUISetOnEvent($GUI_EVENT_CLOSE, "Close", $hWnd_GUI)
    $hTreeView = _GUICtrlTreeView_Create($hWnd_GUI, 2, 2, 396, 268, $iStyle, $WS_EX_CLIENTEDGE)
    _GUICtrlTreeView_SetUnicodeFormat($hTreeView, True)
    GUISetState()
    _GUICtrlTreeView_BeginUpdate($hTreeView)
    Local $Count = 0
    For $a = 0 To 9
        $Data[$Count] = StringFormat("[%02d] New Item", $i1)
        $hItem[$a] = _GUICtrlTreeView_Add($hTreeView, $hTreeView, $Data[$Count])
        _GUICtrlTreeView_SetItemParam($hTreeView, $hItem[$a], $Count)
        $Count += 1
        For $b = 1 To 3
            $Data[$Count] = StringFormat("[%02d] New Child", $i2)
            $hChildItem[$iCItem] = _GUICtrlTreeView_AddChild($hTreeView, $hItem[$a], $Data[$Count])
            _GUICtrlTreeView_SetItemParam($hTreeView, $hChildItem[$iCItem], $Count)
            $Count += 1
            For $c = 1 To 3
                $Data[$Count] = StringFormat("[%02d] New Grandchild", $i3)
                $hGrandChildItem[$iGCItem] = _GUICtrlTreeView_AddChild($hTreeView, $hChildItem[$iCItem], $Data[$Count])
                _GUICtrlTreeView_SetItemParam($hTreeView, $hGrandChildItem[$iGCItem], $Count)
                $Count += 1
                For $d = 1 To 2
                    $Data[$Count] = StringFormat("[%02d] New GrandGrandChild", $i4)
                    $hGrandGrantChildItem[$IGGCItem] = _GUICtrlTreeView_AddChild($hTreeView, $hGrandChildItem[$iGCItem], $Data[$Count])
                    _GUICtrlTreeView_SetItemParam($hTreeView, $hGrandGrantChildItem[$IGGCItem], $Count)
                    $Count += 1
                    $IGGCItem += 1
                    $i4 -= 1
                Next
                $iGCItem += 1
                $i3 -= 1
            Next
            $iCItem += 1
            $i2 -= 1
        Next
        $i1 -= 1
    Next
_GUICtrlTreeView_EndUpdate($hTreeView)
; Context menu
Local $dummy = GUICtrlCreateDummy()
Local $ctxmenu = GUICtrlCreateContextMenu($dummy)
$contextmenu = GUICtrlGetHandle($ctxmenu)
GUICtrlSetOnEvent(GUICtrlCreateMenuItem("New", $ctxmenu), "Dummy")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Cut", $ctxmenu), "Cut")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Copy", $ctxmenu), "Dummy")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Paste", $ctxmenu), "Paste")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Delete", $ctxmenu), "Dummy")
GUICtrlCreateMenuItem("", $ctxmenu)
GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Refresh", $ctxmenu), "Dummy")
GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")
EndFunc   ;==>_Main
Func Close()
GUIDelete($hWnd_GUI)
Exit
EndFunc ;==>Close
Func WM_NOTIFY($hWnd, $Msg, $wParam, $lParam)
Local $struct, $hWndFrom, $hWnd_fileview
If $hWnd <> $hWnd_GUI Then Return $GUI_RUNDEFMSG
$hWnd_fileview = $hTreeView
If Not IsHWnd($hTreeView) Then $hWnd_fileview = GUICtrlGetHandle($hTreeView)
$struct = DllStructCreate($tagNMHDR, $lParam)
If @error Then Return $GUI_RUNDEFMSG
$hWndFrom = HWnd(DllStructGetData($struct, "hWndFrom"))
Switch $hWndFrom
  Case $hWnd_fileview
   Switch DllStructGetData($struct, "code")
    Case $NM_RCLICK ; right click
     Local $mouse = _WinAPI_GetMousePos(True, $hWnd)
     Local $hit_test = _GUICtrlTreeView_HitTest($hTreeView, DllStructGetData($mouse, "X"), DllStructGetData($mouse, "Y"))
     Local $selitem = _GUICtrlTreeView_HitTestItem($hTreeView, DllStructGetData($mouse, "X"), DllStructGetData($mouse, "Y"))
     If $selitem = 0 Then Return $GUI_RUNDEFMSG
     _GUICtrlTreeView_SelectItem($hTreeView, $selitem)
     RefreshContextMenu()
     _GUICtrlMenu_TrackPopupMenu($contextmenu, $hWnd_GUI)
     Return 1
   EndSwitch
EndSwitch
Return $GUI_RUNDEFMSG
EndFunc ;==>WM_NOTIFY
Func Dummy()
ConsoleWrite("Clicked on either New, Copy, Delete or Refresh" & @CRLF)
EndFunc ;==>Dummy
Func Cut()
ConsoleWrite("Cut" & @CRLF)
If $operation Then ClearOperation()
$operation = _GUICtrlTreeView_GetSelection($hTreeView)
_GUICtrlTreeView_SetCut($hTreeView, $operation, True)
EndFunc ;==>Cut
Func Paste()
ConsoleWrite("Paste" & @CRLF)
If Not $operation Then Return
Local $target = _GUICtrlTreeView_GetSelection($hTreeView)
_GUICtrlTreeView_BeginUpdate($hTreeView)
TreeItemCopy($hTreeView, $operation, $target)
_GUICtrlTreeView_Delete($hTreeView, $operation)
_GUICtrlTreeView_EndUpdate($hTreeView)
ClearOperation()
EndFunc ;==>Paste
Func RefreshContextMenu()
If Not $operation Then
  _GUICtrlMenu_SetItemDisabled($contextmenu, 3)
Else
  _GUICtrlMenu_SetItemEnabled($contextmenu, 3)
EndIf
EndFunc ;==>RefreshContextMenu
Func ClearOperation()
_GUICtrlTreeView_SetCut($hTreeView, $operation, False)
$operation = False
EndFunc ;==>ClearOperation
Func _CompareFunc($ilParam1, $ilParam2, $fAscending)
    ConsoleWrite("ilParam: " & $ilParam1 & " | ilParam2: " & $ilParam2)
    ConsoleWrite(@CRLF)
    Local $sText1, $sText2, $iCompare
    $sText1 = $Data[$ilParam1]
    $sText2 = $Data[$ilParam2]
    $iCompare = StringCompare($sText1, $sText2) ; case-insensitive
    If $fAscending Then ; Ascending case
        If $iCompare < 0 Then ; Text1 < Text2 so return -1 (Item1 should precede Item2)
            Return -1
        ElseIf $iCompare > 0 Then ; Text2 < Text1 so return 1 (Item2 should precede Item1)
            Return 1
        Else ; Text1 = Text2 so return 0 (don't swap)
            Return 0
        EndIf
    Else ; Descending case
        If $iCompare > 0 Then ; Text1 > Text2 so return -1 (Item1 should precede Item2)
            Return -1
        ElseIf $iCompare < 0 Then ; Text2 > Text1 so return 1 (Item2 should precede Item1)
            Return 1
        Else ; Text1 = Text2 so return 0 (don't swap)
            Return 0
        EndIf
    EndIf
EndFunc   ;==>_CompareFunc
Func __GUICtrlTreeView_Sort($hWnd, $hFunc, $fAscending = True)
    If $Debug_TV Then __UDF_ValidateClassName($hWnd, $__TREEVIEWCONSTANT_ClassName)
    Local $pFunc = DllCallbackGetPtr($hFunc)
    If $pFunc = 0 Then Return SetError(1, 0, False)
    Local $tSort, $pSort
    $tSort = DllStructCreate($TVSORTCB)
    $pSort = DllStructGetPtr($tSort)
    ; lparam = 1 sort ascending
    ; lparam = 0 sort descending
    DllStructSetData($tSort, "SortParam", $fAscending) ; It's up to the callback function to do whatever it wants to do with SortParam
    DllStructSetData($tSort, "Compare", $pFunc)
    If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd)
    Local $hItem = _SendMessage($hWnd, $TVM_GETNEXTITEM, $TVGN_CHILD, $TVI_ROOT, 0, "wparam", "handle", "handle")
    If Not $hItem Then Return
    While $hItem
        If _GUICtrlTreeView_GetChildren($hWnd, $hItem) Then
            DllStructSetData($tSort, "Parent", $hItem)
            _SendMessage($hWnd, $TVM_SORTCHILDRENCB, 0, $pSort, 0, "wparam", "ptr")
        EndIf
        $hItem = _GUICtrlTreeView_GetNext($hWnd, $hItem)
    WEnd
    DllStructSetData($tSort, "Parent", $TVI_ROOT)
    _SendMessage($hWnd, $TVM_SORTCHILDRENCB, 0, $pSort, 0, "wparam", "ptr")
    Return SetError(0, 0, True)
EndFunc   ;==>__GUICtrlTreeView_Sort
Func TreeItemCopy($hWnd, $hItemSource, $hItemTarget)
Local $hNew
    Local $hTest = $hItemTarget
    Do
        $hTest = _GUICtrlTreeView_GetParentHandle($hWnd, $hTest)
        If $hTest = $hItemSource Then Return 0
Until $hTest = 0
    Local $sText = _GUICtrlTreeView_GetText($hWnd, $hItemSource)
If $hItemTarget <> $hWnd Then
  $hNew = _GUICtrlTreeView_AddChild($hWnd, $hItemTarget, $sText)
Else
  $hNew = _GUICtrlTreeView_Add($hWnd, $hItemTarget, $sText)
EndIf
_GUICtrlTreeView_SetImageIndex($hWnd, $hNew, _GUICtrlTreeView_GetImageIndex($hWnd, $hItemSource))
    _GUICtrlTreeView_SetSelectedImageIndex($hWnd, $hNew, _GUICtrlTreeView_GetSelectedImageIndex($hWnd, $hItemSource))
Local $iChildCount = _GUICtrlTreeView_GetChildCount($hWnd, $hItemSource)
    If $iChildCount > 0 Then
  Local $i, $hRecSource
        For $i = 0 To $iChildCount-1
            $hRecSource = _GUICtrlTreeView_GetItemByIndex($hWnd, $hItemSource, $i)
            TreeItemCopy($hWnd, $hRecSource, $hNew)
        Next
    EndIf
    Return $hNew
EndFunc ;==>TreeItemCopy
Edited by Armag3ddon

Share this post


Link to post
Share on other sites
Armag3ddon

Apparently, it is not necessary that cut and paste serve any purpose, so here is a stripped down variant of the testing script.

Reproduction steps and behaviour stay the same.

#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6
#include <GuiConstantsEx.au3>
#include <GuiTreeView.au3>
#include <WindowsConstants.au3>
#include <GuiMenu.au3>
Opt('MustDeclareVars', 1)
Opt("GUIOnEventMode", 1)
$Debug_TV = False ; Check ClassName being passed to functions, set to True and use a handle to another control to see it work
Global Const $TVSORTCB = "ptr Parent;ptr Compare;lparam SortParam;"
Global $Data[500]
Global $operation = False
Global $hWnd_GUI, $hTreeView, $contextmenu
_Main()
While 1
Sleep(10)
WEnd
Func _Main()
    Local $hItem[10], $hChildItem[30], $hGrandChildItem[90], $hGrandGrantChildItem[180]
    Local $iCItem = 0, $iGCItem = 0, $IGGCItem = 0, $i1 = 10, $i2 = 30, $i3 = 90, $i4 = 180
    Local $iStyle = BitOR($TVS_EDITLABELS, $TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_DISABLEDRAGDROP, $TVS_SHOWSELALWAYS)
    $hWnd_GUI = GUICreate("TreeView Sort", 400, 300)
GUISetOnEvent($GUI_EVENT_CLOSE, "Close", $hWnd_GUI)
    $hTreeView = _GUICtrlTreeView_Create($hWnd_GUI, 2, 2, 396, 268, $iStyle, $WS_EX_CLIENTEDGE)
    _GUICtrlTreeView_SetUnicodeFormat($hTreeView, True)
    GUISetState()
    _GUICtrlTreeView_BeginUpdate($hTreeView)
    Local $Count = 0
    For $a = 0 To 9
        $Data[$Count] = StringFormat("[%02d] New Item", $i1)
        $hItem[$a] = _GUICtrlTreeView_Add($hTreeView, $hTreeView, $Data[$Count])
        _GUICtrlTreeView_SetItemParam($hTreeView, $hItem[$a], $Count)
        $Count += 1
        For $b = 1 To 3
            $Data[$Count] = StringFormat("[%02d] New Child", $i2)
            $hChildItem[$iCItem] = _GUICtrlTreeView_AddChild($hTreeView, $hItem[$a], $Data[$Count])
            _GUICtrlTreeView_SetItemParam($hTreeView, $hChildItem[$iCItem], $Count)
            $Count += 1
            For $c = 1 To 3
                $Data[$Count] = StringFormat("[%02d] New Grandchild", $i3)
                $hGrandChildItem[$iGCItem] = _GUICtrlTreeView_AddChild($hTreeView, $hChildItem[$iCItem], $Data[$Count])
                _GUICtrlTreeView_SetItemParam($hTreeView, $hGrandChildItem[$iGCItem], $Count)
                $Count += 1
                For $d = 1 To 2
                    $Data[$Count] = StringFormat("[%02d] New GrandGrandChild", $i4)
                    $hGrandGrantChildItem[$IGGCItem] = _GUICtrlTreeView_AddChild($hTreeView, $hGrandChildItem[$iGCItem], $Data[$Count])
                    _GUICtrlTreeView_SetItemParam($hTreeView, $hGrandGrantChildItem[$IGGCItem], $Count)
                    $Count += 1
                    $IGGCItem += 1
                    $i4 -= 1
                Next
                $iGCItem += 1
                $i3 -= 1
            Next
            $iCItem += 1
            $i2 -= 1
        Next
        $i1 -= 1
    Next
_GUICtrlTreeView_EndUpdate($hTreeView)
; Context menu
Local $dummy = GUICtrlCreateDummy()
Local $ctxmenu = GUICtrlCreateContextMenu($dummy)
$contextmenu = GUICtrlGetHandle($ctxmenu)
GUICtrlSetOnEvent(GUICtrlCreateMenuItem("New", $ctxmenu), "Dummy")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Cut", $ctxmenu), "Dummy")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Copy", $ctxmenu), "Dummy")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Paste", $ctxmenu), "Dummy")
GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Delete", $ctxmenu), "Dummy")
GUICtrlCreateMenuItem("", $ctxmenu)
GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Refresh", $ctxmenu), "Dummy")
GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")
EndFunc   ;==>_Main
Func Close()
GUIDelete($hWnd_GUI)
Exit
EndFunc ;==>Close
Func WM_NOTIFY($hWnd, $Msg, $wParam, $lParam)
Local $struct, $hWndFrom, $hWnd_fileview
If $hWnd <> $hWnd_GUI Then Return $GUI_RUNDEFMSG
$hWnd_fileview = $hTreeView
If Not IsHWnd($hTreeView) Then $hWnd_fileview = GUICtrlGetHandle($hTreeView)
$struct = DllStructCreate($tagNMHDR, $lParam)
If @error Then Return $GUI_RUNDEFMSG
$hWndFrom = HWnd(DllStructGetData($struct, "hWndFrom"))
Switch $hWndFrom
  Case $hWnd_fileview
   Switch DllStructGetData($struct, "code")
    Case $NM_RCLICK ; right click
     Local $mouse = _WinAPI_GetMousePos(True, $hWnd)
     Local $hit_test = _GUICtrlTreeView_HitTest($hTreeView, DllStructGetData($mouse, "X"), DllStructGetData($mouse, "Y"))
     Local $selitem = _GUICtrlTreeView_HitTestItem($hTreeView, DllStructGetData($mouse, "X"), DllStructGetData($mouse, "Y"))
     If $selitem = 0 Then Return $GUI_RUNDEFMSG
     _GUICtrlTreeView_SelectItem($hTreeView, $selitem)
     _GUICtrlMenu_TrackPopupMenu($contextmenu, $hWnd_GUI)
     Return 1
   EndSwitch
EndSwitch
Return $GUI_RUNDEFMSG
EndFunc ;==>WM_NOTIFY
Func Dummy()
ConsoleWrite("Clicked on either New, Copy, Delete or Refresh" & @CRLF)
EndFunc ;==>Dummy

Share this post


Link to post
Share on other sites
Armag3ddon

I can as well remove the WM_NOTIFY func and the bug persists.

Share this post


Link to post
Share on other sites
Armag3ddon

Ah, sorry. I thought I did. Was too sleepy ;)

Okay, here. Reproduction stays the same: Open first item "[10] New Item", select "[29] New Child" using arrow keys. Erroneous log message appears.

#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6
#include <GuiConstantsEx.au3>
#include <GuiTreeView.au3>
#include <WindowsConstants.au3>
#include <GuiMenu.au3>
Opt('MustDeclareVars', 1)
Opt("GUIOnEventMode", 1)
$Debug_TV = False ; Check ClassName being passed to functions, set to True and use a handle to another control to see it work
Global Const $TVSORTCB = "ptr Parent;ptr Compare;lparam SortParam;"
Global $Data[500]
Global $operation = False
Global $hWnd_GUI, $hTreeView, $contextmenu
_Main()
While 1
    Sleep(10)
WEnd
Func _Main()
    Local $hItem[10], $hChildItem[30], $hGrandChildItem[90], $hGrandGrantChildItem[180]
    Local $iCItem = 0, $iGCItem = 0, $IGGCItem = 0, $i1 = 10, $i2 = 30, $i3 = 90, $i4 = 180
    Local $iStyle = BitOR($TVS_EDITLABELS, $TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_DISABLEDRAGDROP, $TVS_SHOWSELALWAYS)
    ; Create GUI
    $hWnd_GUI = GUICreate("TreeView Sort", 400, 300)
    GUISetOnEvent($GUI_EVENT_CLOSE, "Close", $hWnd_GUI)
    $hTreeView = _GUICtrlTreeView_Create($hWnd_GUI, 2, 2, 396, 268, $iStyle, $WS_EX_CLIENTEDGE)
    _GUICtrlTreeView_SetUnicodeFormat($hTreeView, True)
    GUISetState()
    ; Create treeview and fill with some items
_GUICtrlTreeView_BeginUpdate($hTreeView)
    Local $Count = 0
    For $a = 0 To 9
        $Data[$Count] = StringFormat("[%02d] New Item", $i1)
        $hItem[$a] = _GUICtrlTreeView_Add($hTreeView, $hTreeView, $Data[$Count])
        _GUICtrlTreeView_SetItemParam($hTreeView, $hItem[$a], $Count)
        $Count += 1
        For $b = 1 To 3
            $Data[$Count] = StringFormat("[%02d] New Child", $i2)
            $hChildItem[$iCItem] = _GUICtrlTreeView_AddChild($hTreeView, $hItem[$a], $Data[$Count])
            _GUICtrlTreeView_SetItemParam($hTreeView, $hChildItem[$iCItem], $Count)
            $Count += 1
            For $c = 1 To 3
                $Data[$Count] = StringFormat("[%02d] New Grandchild", $i3)
                $hGrandChildItem[$iGCItem] = _GUICtrlTreeView_AddChild($hTreeView, $hChildItem[$iCItem], $Data[$Count])
                _GUICtrlTreeView_SetItemParam($hTreeView, $hGrandChildItem[$iGCItem], $Count)
                $Count += 1
                For $d = 1 To 2
                    $Data[$Count] = StringFormat("[%02d] New GrandGrandChild", $i4)
                    $hGrandGrantChildItem[$IGGCItem] = _GUICtrlTreeView_AddChild($hTreeView, $hGrandChildItem[$iGCItem], $Data[$Count])
                    _GUICtrlTreeView_SetItemParam($hTreeView, $hGrandGrantChildItem[$IGGCItem], $Count)
                    $Count += 1
                    $IGGCItem += 1
                    $i4 -= 1
                Next
                $iGCItem += 1
                $i3 -= 1
            Next
            $iCItem += 1
            $i2 -= 1
        Next
        $i1 -= 1
    Next
    _GUICtrlTreeView_EndUpdate($hTreeView)
; Context menu
    Local $dummy = GUICtrlCreateDummy()
    Local $ctxmenu = GUICtrlCreateContextMenu($dummy)
    $contextmenu = GUICtrlGetHandle($ctxmenu)
    GUICtrlSetOnEvent(GUICtrlCreateMenuItem("New", $ctxmenu), "Dummy")
    GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Cut", $ctxmenu), "Dummy")
    GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Copy", $ctxmenu), "Dummy")
    GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Paste", $ctxmenu), "Dummy")
    GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Delete", $ctxmenu), "Dummy")
    GUICtrlCreateMenuItem("", $ctxmenu)
    GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Refresh", $ctxmenu), "Dummy")
EndFunc   ;==>_Main
Func Close()
    GUIDelete($hWnd_GUI)
    Exit
EndFunc ;==>Close
Func Dummy()
    ConsoleWrite("Clicked on either New, Copy, Delete or Refresh" & @CRLF)
EndFunc ;==>Dummy

Share this post


Link to post
Share on other sites
Melba23

Armag3ddon,

This has come up a couple of times recently. AutoIt uses the Item Parameters in natively created ListViews and TreeViews to hold the ControlID of the particular item. You are setting the parameter to numbers used by the ControlIDs of other controls and so confusing AutoIt.

The solution is to add a suitably high number ot the parameter so that the value does not repeat a ControlID - I have used 1000 in this example:

#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6
#include <GuiConstantsEx.au3>
#include <GuiTreeView.au3>
#include <WindowsConstants.au3>
#include <GuiMenu.au3>
Opt('MustDeclareVars', 1)
Opt("GUIOnEventMode", 1)
$Debug_TV = False ; Check ClassName being passed to functions, set to True and use a handle to another control to see it work
Global Const $TVSORTCB = "ptr Parent;ptr Compare;lparam SortParam;"
Global $Data[500]
Global $operation = False
Global $hWnd_GUI, $hTreeView, $contextmenu
_Main()
While 1
    Sleep(10)
WEnd
Func _Main()
    Local $hItem[10], $hChildItem[30], $hGrandChildItem[90], $hGrandGrantChildItem[180]
    Local $iCItem = 0, $iGCItem = 0, $IGGCItem = 0, $i1 = 10, $i2 = 30, $i3 = 90, $i4 = 180
    Local $iStyle = BitOR($TVS_EDITLABELS, $TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_DISABLEDRAGDROP, $TVS_SHOWSELALWAYS)
    ; Create GUI
    $hWnd_GUI = GUICreate("TreeView Sort", 400, 300)
    GUISetOnEvent($GUI_EVENT_CLOSE, "Close", $hWnd_GUI)
    $hTreeView = _GUICtrlTreeView_Create($hWnd_GUI, 2, 2, 396, 268, $iStyle, $WS_EX_CLIENTEDGE)
    _GUICtrlTreeView_SetUnicodeFormat($hTreeView, True)
    GUISetState()
    ; Create treeview and fill with some items
_GUICtrlTreeView_BeginUpdate($hTreeView)
    Local $Count = 0
    For $a = 0 To 9
        $Data[$Count] = StringFormat("[%02d] New Item", $i1)
        $hItem[$a] = _GUICtrlTreeView_Add($hTreeView, $hTreeView, $Data[$Count])
        _GUICtrlTreeView_SetItemParam($hTreeView, $hItem[$a], 1000 + $Count) ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        $Count += 1
        For $b = 1 To 3
            $Data[$Count] = StringFormat("[%02d] New Child", $i2)
            $hChildItem[$iCItem] = _GUICtrlTreeView_AddChild($hTreeView, $hItem[$a], $Data[$Count])
            _GUICtrlTreeView_SetItemParam($hTreeView, $hChildItem[$iCItem], 1000 + $Count) ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
            $Count += 1
            For $c = 1 To 3
                $Data[$Count] = StringFormat("[%02d] New Grandchild", $i3)
                $hGrandChildItem[$iGCItem] = _GUICtrlTreeView_AddChild($hTreeView, $hChildItem[$iCItem], $Data[$Count])
                _GUICtrlTreeView_SetItemParam($hTreeView, $hGrandChildItem[$iGCItem], 1000 + $Count) ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                $Count += 1
                For $d = 1 To 2
                    $Data[$Count] = StringFormat("[%02d] New GrandGrandChild", $i4)
                    $hGrandGrantChildItem[$IGGCItem] = _GUICtrlTreeView_AddChild($hTreeView, $hGrandChildItem[$iGCItem], $Data[$Count])
                    _GUICtrlTreeView_SetItemParam($hTreeView, $hGrandGrantChildItem[$IGGCItem], 1000 + $Count) ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                    $Count += 1
                    $IGGCItem += 1
                    $i4 -= 1
                Next
                $iGCItem += 1
                $i3 -= 1
            Next
            $iCItem += 1
            $i2 -= 1
        Next
        $i1 -= 1
    Next
    _GUICtrlTreeView_EndUpdate($hTreeView)
; Context menu
    Local $dummy = GUICtrlCreateDummy()
    Local $ctxmenu = GUICtrlCreateContextMenu($dummy)
    $contextmenu = GUICtrlGetHandle($ctxmenu)
    GUICtrlSetOnEvent(GUICtrlCreateMenuItem("New", $ctxmenu), "Dummy")
    GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Cut", $ctxmenu), "Dummy")
    GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Copy", $ctxmenu), "Dummy")
    GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Paste", $ctxmenu), "Dummy")
    GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Delete", $ctxmenu), "Dummy")
    GUICtrlCreateMenuItem("", $ctxmenu)
    GUICtrlSetOnEvent(GUICtrlCreateMenuItem("Refresh", $ctxmenu), "Dummy")
EndFunc   ;==>_Main
Func Close()
    GUIDelete($hWnd_GUI)
    Exit
EndFunc ;==>Close
Func Dummy()
    ConsoleWrite("Clicked on either New, Copy, Delete or Refresh" & @CRLF)
EndFunc ;==>Dummy

No "ghost" firings now when I run it - and the sorting should still work as you have suitably differentiated parameters to work with. ;)

M23

  • Like 1

Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind._______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

 

Share this post


Link to post
Share on other sites
Armag3ddon

Oh my! Awesome, thanks! ;)

You are surely my hero of the day!

Share this post


Link to post
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
Sign in to follow this  

×