abberration Posted September 21, 2020 Share Posted September 21, 2020 Hi, everyone. I am writing a script to help organize files in folders. I have hit a snag using _Arr ayDisplay. It works fine until I press a button that triggers a WM_Command. After a button is clicked, _ArrayDisplay locks up and crashes the script. I could continue without seeing a list, but it would be a lot easier if _ArrayDisplay worked. Also, I just would like to know why it's crashing. Any thoughts? expandcollapse popup#include <ButtonConstants.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <Array.au3> #include <WinAPI.au3> #include <WinAPIConstants.au3> #include <WinAPISys.au3> #include <GuiButton.au3> #include <File.au3> $guiForm1 = GUICreate("Form1", 305, 356) GUISetState(@SW_SHOW) $arrWinList = _GetListOfOpenExplorerWindows(1, 3) ; creates an array of windows that are open _ArraySort($arrWinList, 0, 1) If IsArray($arrWinList) Then ; List the open If $arrWinList[0][0] > 12 Then WinMove($guiForm1, "", @DesktopWidth/2 - 305, Default, 610, 378) EndIf For $i = 1 To $arrWinList[0][0] If $i < 13 Then $y = (($i - 1) * 28) + 8 Assign("guiButton" & $i, GUICtrlCreateButton($arrWinList[$i][0], 8, $y , 283, 25, $WS_GROUP)) ElseIf $i > 12 Then $y = (($i - 13) * 28) + 8 Assign("guiButton" & $i, GUICtrlCreateButton($arrWinList[$i][0], 305, $y, 283, 25, $WS_GROUP)) If $i = 24 Then ExitLoop EndIf EndIf Next EndIf GUIRegisterMsg($WM_COMMAND, "WM_COMMAND") ; _ArrayDisplay($arrWinList) ; _arraydisplay works before a window is selected While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE Exit EndSwitch WEnd Func WM_COMMAND($hWnd, $iMsg, $iwParam, $ilParam) ; if button is clicked, give popup confirming it. ;_ArrayDisplay($arrWinList) ; once a button is pressed, _arraydisplay locks up Local $hWndFrom, $iIDFrom, $iCode, $hWndEdit $hWndFrom = $ilParam $iIDFrom = _WinAPI_LoWord($iwParam) $iCode = _WinAPI_HiWord($iwParam) If $iCode = $BN_CLICKED Then $text = _GUICtrlButton_GetText ( $hWndFrom ) $arrPos = _ArraySearch($arrWinList, $text) _OrganizeFolder($arrWinList[$arrPos][1]) EndIf EndFunc Func _GetListOfOpenExplorerWindows($vIncludeArrayCount = 0, $vColumnReturn = 3) ; (0 = No, 1 = Yes), (1 = Short Names Only, 2 = Long Addresses, 3 or 1 + 2 = Return Both) Local $aWinList, $sGetText, $aWinText, $varStrLen, $sFormat, $i, $j, $k, $vRowsToDel $aWinList = WinList() Local $aOutput[0][2] $k = 0 For $i = 1 To $aWinList[0][0] $sGetText = WinGetText($aWinList[$i][0]) $aWinText = StringSplit($sGetText, @LF) For $j = 1 To $aWinText[0] If StringInStr($aWinText[$j], "Address:") Then $sFormat = StringReplace($aWinText[$j], "Address: ", "") If DirGetSize($sFormat) > -1 Then ReDim $aOutput[UBound($aOutput) + 1][2] $aOutput[$k][0] = $aWinList[$i][0] $aOutput[$k][1] = $sFormat $k += 1 EndIf EndIf Next Next For $i = 1 To UBound($aOutput) $varStrLen = StringLen($aOutput[$i - 1][0]) If $varStrLen = 0 Then $vRowsToDel = $vRowsToDel & $i - 1 & ";" EndIf Next $vRowsToDel = StringTrimRight($vRowsToDel, 1) _ArrayDelete($aOutput, $vRowsToDel) If $vIncludeArrayCount = 1 Then _ArrayInsert($aOutput, 0, UBound($aOutput)) EndIf If $vColumnReturn = 1 Then _ArrayColDelete($aOutput, 1) ElseIf $vColumnReturn = 2 Then If $vIncludeArrayCount = 1 Then If UBound($aOutput) > 0 Then $aOutput[0][1] = UBound($aOutput) EndIf EndIf _ArrayColDelete($aOutput, 0) EndIf If UBound($aOutput) = 0 Then Return -1 Else Return $aOutput EndIf EndFunc Func _OrganizeFolder($strFolderName) ; code to organize the content of the folder $arrFileList = _FileListToArrayRec($strFolderName & "\", "*", 1, 0) _ArrayDisplay($arrFileList) EndFunc Easy MP3 | Software Installer | Password Manager Link to comment Share on other sites More sharing options...
pixelsearch Posted September 21, 2020 Share Posted September 21, 2020 (edited) Hi abberration It freezes/crash because you spend too long time inside Func WM_COMMAND() Help file GUIRegisterMsg : Warning: blocking of running user functions which executes window messages with commands such as "MsgBox()" can lead to unexpected behavior, the return to the system should be as fast as possible !!! _ArrayDisplay() takes some time to display its content, and as it's (indirectly) called from WM_COMMAND() ... also _FileListToArrayRec() takes time too. A solution (successfully tested) is to move code from WM_COMMAND to While... Wend loop, by modifying this part of code : ... Global $text, $arrPos, $bButton_Clicked = False While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_NONE If $bButton_Clicked Then _OrganizeFolder($arrWinList[$arrPos][1]) $bButton_Clicked = False EndIf Case $GUI_EVENT_CLOSE Exit EndSwitch WEnd Func WM_COMMAND($hWnd, $iMsg, $iwParam, $ilParam) ; if button is clicked, give popup confirming it. ;_ArrayDisplay($arrWinList) ; once a button is pressed, _arraydisplay locks up Local $hWndFrom, $iIDFrom, $iCode, $hWndEdit $hWndFrom = $ilParam $iIDFrom = _WinAPI_LoWord($iwParam) $iCode = _WinAPI_HiWord($iwParam) If $iCode = $BN_CLICKED Then $text = _GUICtrlButton_GetText ( $hWndFrom ) $arrPos = _ArraySearch($arrWinList, $text) ; _OrganizeFolder($arrWinList[$arrPos][1]) ; <======== commented $bButton_Clicked = True ; <======== a flag, read within While... Wend loop EndIf EndFunc ... That's the 2nd time I use $GUI_EVENT_NONE (first time was a couple of days ago), seems to work fine Edited September 21, 2020 by pixelsearch abberration 1 Link to comment Share on other sites More sharing options...
abberration Posted September 21, 2020 Author Share Posted September 21, 2020 Hi, pixelsearch Ah, yes, I remember reading that in the help file in the past. I didn't think about that. Also, I was working on this in my car waiting for someone, so I wasn't well equipped for programming at the time. Thanks for your answer. I will play with the code to learn more. Easy MP3 | Software Installer | Password Manager Link to comment Share on other sites More sharing options...
pixelsearch Posted September 22, 2020 Share Posted September 22, 2020 (edited) 7 hours ago, abberration said: Thanks for your answer. I will play with the code to learn more. You're welcome. Here are some personal suggestions : 1) Where to place the line $bButton_Clicked = False ? Because In case you click again on a GUI button while _ArrayDisplay is active, then you'll notice a different behavior with these 2 scripts, it's always instructive to experiment them both : If $bButton_Clicked Then _OrganizeFolder($arrWinList[$arrPos][1]) $bButton_Clicked = False EndIf versus If $bButton_Clicked Then $bButton_Clicked = False _OrganizeFolder($arrWinList[$arrPos][1]) EndIf 2) As WM_COMMAND message is not needed while _ArrayDisplay is active, then Unregistering/Re-registering the message before/after _ArrayDisplay seems a good option , i.e in your case before/after _OrganizeFolder($arrWinList[$arrPos][1]) GUIRegisterMsg($WM_COMMAND, "") ... GUIRegisterMsg($WM_COMMAND, "WM_COMMAND") 3) Disabling/Enabling the GUI while _ArrayDisplay is active, that's another good option to prevent clicking GUI buttons (no need to cumulate it with previous option) : GUISetState(@SW_DISABLE, $guiForm1) _OrganizeFolder($arrWinList[$arrPos][1]) GUISetState(@SW_ENABLE, $guiForm1) 4) Using a dummy control (instead of $GUI_EVENT_NONE) would work too. It should be triggered during the function WM_COMMAND and tested within the While... Wend loop Good luck Edited September 22, 2020 by pixelsearch deleted superfluous comments in 4) Link to comment Share on other sites More sharing options...
abberration Posted September 24, 2020 Author Share Posted September 24, 2020 Hi, pixelsearch. Those are some really great suggestions. I will indeed use some of them. I really like the idea of unregistering the WM_Command and re-registering it. I never understood the purpose of a dummy until now. I played with sending a dummy the text of the button that was clicked in the WM_Command and in the Case, I sent it nothing and then called my function. That seems to work. I'm not an expert on dummies, but I think I understand them better now. Thanks again for all the great advice! Easy MP3 | Software Installer | Password Manager Link to comment Share on other sites More sharing options...
pixelsearch Posted September 25, 2020 Share Posted September 25, 2020 19 hours ago, abberration said: I never understood the purpose of a dummy until now. Hi abberration Just like you, I didn't use dummy controls when I started to learn AutoIt (2.5 years now) But they really can be useful at times and your script is a perfect example for using them, because you don't get specific controls (in the While... Wend loop) corresponding to each button created dynamically by your script. Applied to your script, here are the 3 steps to create, activate, then check the dummy control : $guiForm1 = GUICreate("Form1", 305, 356) $idDummy = GUICtrlCreateDummy() ; <========== 1) Create the dummy control GUISetState(@SW_SHOW) GUIRegisterMsg($WM_COMMAND, "WM_COMMAND") ... Func WM_COMMAND($hWnd, $iMsg, $iwParam, $ilParam) Local $hWndFrom, $iIDFrom, $iCode, $hWndEdit $hWndFrom = $ilParam $iIDFrom = _WinAPI_LoWord($iwParam) $iCode = _WinAPI_HiWord($iwParam) If $iCode = $BN_CLICKED Then $text = _GUICtrlButton_GetText ( $hWndFrom ) GUICtrlSendToDummy($idDummy, $text) ; <========== 2) Activate the dummy control EndIf EndFunc ... While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE Exit Case $idDummy ; <========== 3) Check the dummy control GUIRegisterMsg($WM_COMMAND, "") $text_read = GUICtrlRead($idDummy) $arrPos = _ArraySearch($arrWinList, $text_read) _OrganizeFolder($arrWinList[$arrPos][1]) GUIRegisterMsg($WM_COMMAND, "WM_COMMAND") EndSwitch WEnd See how easy it is ? Even the important string "button text" has been sent to the main loop with that single line, avoiding a global variable. GUICtrlSendToDummy($idDummy, $text) Also, there's no need to use _ArraySearch() within the WM_COMMAND function, because _ArraySearch() takes time too (in case of a big array) and it may freeze the script when used within a registered message (as we discussed earlier). It's now moved to the main loop too. As you seemed to like the unregistration/re-registration phase, I added it to the script above. Now you can click on any button in the GUI (while ArrayDisplay is active), nothing will happen after ArrayDisplay is ended (tested) GUIRegisterMsg($WM_COMMAND, "") ... GUIRegisterMsg($WM_COMMAND, "WM_COMMAND") By the way, I succeeded to create dynamically 12 buttons (it fits the original GUI) then a 13th => 24th button would appear in your enlarged GUI, well done abberration abberration 1 Link to comment Share on other sites More sharing options...
abberration Posted September 25, 2020 Author Share Posted September 25, 2020 (edited) You have been extremely helpful. I finished the script and my code is extremely similar to yours. Exactly what it does is organizes TV episodes into folders and sub-folders (the way I prefer them). The show must have something like S01E14 in it (or 1x14, which I added code to convert it). It will create a folder for that show if it doen't exist and create a sub-folder like S01. Finally, it moves the file to that folder. If anyone is interested in the final code, here it is (hey, maybe someone else might be able to use it): expandcollapse popup#Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_Icon=Custom-Icon-Design-Pretty-Office-5-Folder-Add.ico #AutoIt3Wrapper_UseX64=y #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** #include <ButtonConstants.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <Array.au3> #include <WinAPI.au3> #include <WinAPIConstants.au3> #include <WinAPISys.au3> #include <GuiButton.au3> #include <File.au3> #include <String.au3> $strSupportedFormats = "*.avi;*.mpg;*.mpeg;*.mp4;*.mkv;*.wmv" $guiForm1 = GUICreate("Create Folders And Organize", 305, 356) GUISetState(@SW_SHOW) $guiDummy = GUICtrlCreateDummy() $arrWinList = _GetListOfOpenExplorerWindows(1, 3) ; creates an array of windows that are open _ArraySort($arrWinList, 0, 1) If IsArray($arrWinList) Then ; List the open If $arrWinList[0][0] > 12 Then WinMove($guiForm1, "", @DesktopWidth/2 - 305, Default, 610, 378) EndIf For $i = 1 To $arrWinList[0][0] If $i < 13 Then $y = (($i - 1) * 28) + 8 Assign("guiButton" & $i, GUICtrlCreateButton($arrWinList[$i][0], 8, $y , 283, 25, $WS_GROUP)) ElseIf $i > 12 Then $y = (($i - 13) * 28) + 8 Assign("guiButton" & $i, GUICtrlCreateButton($arrWinList[$i][0], 305, $y, 283, 25, $WS_GROUP)) If $i = 24 Then ExitLoop EndIf EndIf Next EndIf GUIRegisterMsg($WM_COMMAND, "WM_COMMAND") While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE Exit Case $guiDummy $strText = GUICtrlRead($guiDummy) GUICtrlSendToDummy($strText) $arrPos = _ArraySearch($arrWinList, $strText) _OrganizeFolder($arrWinList[$arrPos][1]) EndSwitch WEnd Func WM_COMMAND($hWnd, $iMsg, $iwParam, $ilParam) ; if button is clicked, give popup confirming it. Local $hWndFrom, $iIDFrom, $iCode, $hWndEdit $hWndFrom = $ilParam $iIDFrom = _WinAPI_LoWord($iwParam) $iCode = _WinAPI_HiWord($iwParam) If $iCode = $BN_CLICKED Then $text = _GUICtrlButton_GetText ($hWndFrom) GUICtrlSendToDummy($guiDummy, $text) EndIf EndFunc Func _GetListOfOpenExplorerWindows($vIncludeArrayCount = 0, $vColumnReturn = 3) ; (0 = No, 1 = Yes), (1 = Short Names Only, 2 = Long Addresses, 3 or 1 + 2 = Return Both) Local $aWinList, $sGetText, $aWinText, $varStrLen, $sFormat, $i, $j, $k, $vRowsToDel $aWinList = WinList() Local $aOutput[0][2] $k = 0 For $i = 1 To $aWinList[0][0] $sGetText = WinGetText($aWinList[$i][0]) $aWinText = StringSplit($sGetText, @LF) For $j = 1 To $aWinText[0] If StringInStr($aWinText[$j], "Address:") Then $sFormat = StringReplace($aWinText[$j], "Address: ", "") If DirGetSize($sFormat) > -1 Then ReDim $aOutput[UBound($aOutput) + 1][2] $aOutput[$k][0] = $aWinList[$i][0] $aOutput[$k][1] = $sFormat $k += 1 EndIf EndIf Next Next For $i = 1 To UBound($aOutput) $varStrLen = StringLen($aOutput[$i - 1][0]) If $varStrLen = 0 Then $vRowsToDel = $vRowsToDel & $i - 1 & ";" EndIf Next $vRowsToDel = StringTrimRight($vRowsToDel, 1) _ArrayDelete($aOutput, $vRowsToDel) If $vIncludeArrayCount = 1 Then _ArrayInsert($aOutput, 0, UBound($aOutput)) EndIf If $vColumnReturn = 1 Then _ArrayColDelete($aOutput, 1) ElseIf $vColumnReturn = 2 Then If $vIncludeArrayCount = 1 Then If UBound($aOutput) > 0 Then $aOutput[0][1] = UBound($aOutput) EndIf EndIf _ArrayColDelete($aOutput, 0) EndIf If UBound($aOutput) = 0 Then Return -1 Else Return $aOutput EndIf EndFunc Func _OrganizeFolder($strFolderName) ; code to organize the content of the folder $arrFileList = _FileListToArrayRec($strFolderName & "\", $strSupportedFormats, 1, 0) For $i = 1 To $arrFileList[0] $strFileName = $arrFileList[$i] $arrSeason = StringRegExp($strFileName, "[S|s]\d\d[E|e]\d\d", 1) $arrSeasonx = StringRegExp($strFileName, "\d{1,2}[x|X]\d{1,2}", 1) ; If the show is formatted as 1x13 then convert to S01E14 If IsArray($arrSeasonx) Then Dim $arrSeason[1] $arrSeason[0] = StringRegExpReplace($arrSeasonx[0], "\d{1,2}[x|X]\d{1,2}", _ConvertXToS($arrSeasonx[0])) $strFileNameOrig = $strFileName $strFileName = StringRegExpReplace($strFileName, "\d{1,2}[x|X]\d{1,2}", $arrSeason[0]) ; going back to the original file name, substitute 1x13 for S01E14. FileMove($strFolderName & "\" & $strFileNameOrig, $strFolderName & "\" & $strFileName) EndIf If IsArray($arrSeason) Then $arrShowName = StringRegExp($strFileName, ".+?(?=.[S]\d\d[E]\d\d)", 1) $strShowName = StringRegExpReplace($arrShowName[0], "\.", " ") $strFullPath = $strFolderName & "\" & _StringProper($strShowName) & "\" & _StringProper(StringLeft($arrSeason[0], 3)) If FileExists($strFullPath) = 0 Then DirCreate($strFullPath) EndIf FileMove($strFolderName & "\" & $strFileName, $strFullPath & "\" & $strFileName) Endif Next Exit EndFunc Func _ConvertXToS($varExp) $varExp = StringLower($varExp) ; Force the x to be lowercase becasue stringsplit is case sensitive $arrStrBreakdown = StringSplit($varExp, "x", 2) $varStrReconstructed = "S" & StringFormat("%02d", $arrStrBreakdown[0]) & "E" & StringFormat("%02d", $arrStrBreakdown[1]) Return $varStrReconstructed EndFunc I didn't put any safeguards because I only wrote it for myself and if a file doesn't match the season/episode specific expression, it ignores files. I didn't un-register the WM_Command because for this specific program, I will only ever run it one time and automatically quit. In the future (and for older programs that I keep running), I will make that change. Thanks for your help once again! Edited September 25, 2020 by abberration pixelsearch 1 Easy MP3 | Software Installer | Password Manager Link to comment Share on other sites More sharing options...
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