Jump to content

stop the GUI from freezing/white screen


llewxam
 Share

Recommended Posts

AutoItSetOption("TrayIconHide",1)

#include <GUIConstantsEx.au3> ;needed for $GUI_EVENT_CLOSE, $GUI_DISABLE, $GUI_UNCHECKED
#include <String.au3> ;needed for _StringAddThousandsSep
#include <WindowsConstants.au3> ;needed for $DT_End_Ellipsis which trims the path&file name to fit $fileinfo and $copyinfo
#include <Array.au3> ;needed for _ArrayDisplay
#include <ListboxConstants.au3>

Global $sourcelist, $synced, $howmany, $howbig, $newprofilelist
$temp = "" ;$temp is all file names & relative paths, see below; done to avoid _ArrayAdd
$failedlist = "" ;this is to store the file names that failed to sync
$loaded = 0 ;if a profile is loaded this is set to 1, then you will not be asked to save it as a profile
$howmany = 0 ;total file count of the source directory
$synced = 0 ;counts how many files needed to be synced
$failed = 0 ;counts how many files failed to sync

$main = GUICreate("File Syncer", 400, 335) ;main window
$box=GUICtrlCreateGraphic(195,317,380,2) ;    <--------------------------------------------|
GUICtrlSetGraphic($box, $GUI_GR_COLOR, 0xa8a8a8) ;                                      <--|
GUICtrlSetGraphic($box, $GUI_GR_MOVE, 1,1) ;                                            <--|
GUICtrlSetGraphic($box, $GUI_GR_LINE,199,1) ;                                           <--|
$maxadsoft=GUICtrlCreateLabel("MaxImuM AdVaNtAgE SofTWarE(C) 2009",195,320,380,20) ;    <--|
GUICtrlSetColor($maxadsoft,0xa8a8a8) ;        <--------------------------------------------|
$sourcemsg = GUICtrlCreateLabel("", 10, 10, 380, 20) ;shows file count for current sync job
$sourceinfo = GUICtrlCreateLabel("", 140, 10, 380, 20) ;shows file count for current sync job
$syncedinfo = GUICtrlCreateLabel("", 260, 10, 380, 20) ;just adds a message when sync has started
$fileinfo = GUICtrlCreateLabel("", 10, 70, 380, 20, $DT_END_ELLIPSIS) ;shows current file name
$fileprog = GUICtrlCreateLabel("", 10, 90, 380, 20) ;shows current file number & total file count
$copyprogress = GUICtrlCreateProgress(10, 130, 380, 20) ;progress bar for current file copy
$progressbar = GUICtrlCreateProgress(10, 200, 380, 20) ;progress bar for total files
$sourceinput = GUICtrlCreateInput("Type The Source Or Browse Above", 10, 260, 180) ;input box for source path
$targetinput = GUICtrlCreateInput("Type The Target Or Browse Above", 210, 260, 180) ;input box for target path
$sourcebutton = GUICtrlCreateButton("Source", 60, 230, 80) ;button for FileSelectFolder for source path
$targetbutton = GUICtrlCreateButton("Target", 260, 230, 80) ;button for FileSelectFolder for target path
$gobutton = GUICtrlCreateButton("GO", 120, 285, 80) ;start
$loadprofile=GUICtrlCreateButton("Profiles",200,285,80) ;button to load a saved profile
GUISetState(@SW_SHOW, $main) ;done setting up GUI

Dim $AccelKeys[1][2] = [["{ENTER}", $gobutton]] ;sets Enter to start sync
GUISetAccelerators($AccelKeys) ;sets Enter to start sync

Do
    $msg = GUIGetMsg() ;get mouse/keyboard actions
    If $msg = $sourcebutton Then GUICtrlSetData($sourceinput, FileSelectFolder("Select folder to sync", "")) ;set source
    If $msg = $targetbutton Then GUICtrlSetData($targetinput, FileSelectFolder("Select where to place the files", "", 1)) ;set target
    if $msg = $loadprofile then 
        profiles() ;load/delete a profile
        $msg="" ;when profiles() is exited $msg must be cleared or the app tries to close
    EndIf
    if $msg = $gobutton Then
        If FileExists(GUICtrlRead($sourceinput)) and GUICtrlRead($targetinput)<>"" and GUICtrlRead($targetinput)<>"Type The Target Or Browse Above" then ;check that the source path exists, and that a target path has been specified
            ExitLoop ;if the above criteria are met then leave the setup and get some work done  :)
        Else
            if not FileExists(GUICtrlRead($sourceinput)) then msgbox(48,"Bad Source!","The source path does not exist!") ;warning
            if GUICtrlRead($targetinput)="" or GUICtrlRead($targetinput)="Type The Target Or Browse Above" then msgbox(48,"No Target!","The target path has not been set yet!") ;warning
        endif
    EndIf
    If $msg = $gui_event_close Then Exit ;quit
Until 1 = 2

ControlFocus("", "", $gobutton) ;done so when all of the buttons are disabled the focus will be on a non-functional control
GUICtrlSetState($sourcebutton, $gui_disable) ;   <-----------------------|
GUICtrlSetState($targetbutton, $gui_disable) ;                           |
GUICtrlSetState($gobutton, $gui_disable) ;                               |   shut off the buttons once the sync has started
GUICtrlSetState($loadprofile, $gui_disable) ;                            |
GUICtrlSetState($sourceinput, $gui_disable) ;                            |
GUICtrlSetState($targetinput, $gui_disable) ;    <-----------------------|
$basedir = GUICtrlRead($sourceinput) ;set  $basedir to source path
$target = GUICtrlRead($targetinput) ;set $target to target path

if $loaded=0 then ;if this is not a previously loaded profile
    $profile=msgbox(4,"Save Sync?","Would you like to save the Source and Target as a profile?") ;wanna save this profile?
    if $profile=6 then ;yes
        $nameit=GUICreate("Profile Name?",225,50) ;window title to save profile name
        $profilename=GUICtrlCreateInput("",5,5,185) ;input box for the profile name
        $accept=GUICtrlCreateButton("OK",190,5,25) ;accept button
        GUISetState(@sw_show,$nameit) ;done setting up GUI
        Dim $AccelKeys[1][2] = [["{ENTER}", $accept]] ;set Enter to accept
        GUISetAccelerators($AccelKeys) ;set Enter to accept
        Do
            $msg = GUIGetMsg() ;get mouse/keyboard actions
            If $msg = $accept Then ;OK
                $thename=GUICtrlRead($profilename) ;assign the name of the profile
                ExitLoop ;done
            EndIf
        Until 1 = 2
        GUIDelete($nameit) ;remove the profile name window
        $currentprofiles=RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\MaxImuM Advantage","Sync") ;get the current saved profiles
        if $currentprofiles<>"" then $currentprofiles=$currentprofiles&"*" ;if there are preexisting profile add * delimiter
        RegWrite("HKEY_LOCAL_MACHINE\SOFTWARE\MaxImuM AdVaNtAgE","Sync","REG_SZ",$currentprofiles&$thename&"*"&$basedir&"*"&$target) ;add the new profile
    EndIf
EndIf

$trimamount = StringLen($basedir) + 1 ;used to remove the source path from the file names
GUICtrlSetData($sourcemsg,"Enumerating Source Files: ") ;
$begin = TimerInit() ;start the timer
$pause = GUICtrlCreateCheckbox("Pause", 175, 150) ;pause, then option to quit job during operation (GUI button overlap)
theheart($basedir) ;theheart() is the folder recursion routine
GUICtrlSetData($sourcemsg, "Finished Enumeration: " & _StringAddThousandsSep($howmany)) ;sets the total file count from the source path
GUICtrlDelete($sourceinfo)
$sourcelist = StringSplit($temp, Chr(1)) ;$temp is all file names & relative paths, see below; done to avoid _ArrayAdd
GUICtrlSetData($syncedinfo, "Checking For Files To Sync") ;message that work has started
;$pause = GUICtrlCreateCheckbox("Pause", 175, 150) ;pause, then option to quit job during operation (GUI button overlap)
$total = $sourcelist[0] - 1 ;should have the same result as $howmany
For $lookatsource = 1 To $total ;start with the first file found, go down the line...
    While GUICtrlRead($pause) == 1 ;look for the pause button
        $quit=msgbox(36,"Paused","Quit Now?")
        if $quit=6 then exit ;yes
        if $quit=7 then GUICtrlSetState($pause,$gui_unchecked) ;no, so uuncheck the box 
    WEnd

    GUICtrlSetData($fileinfo, $sourcelist[$lookatsource]) ;show file name
    GUICtrlSetData($fileprog, "(" & _StringAddThousandsSep($lookatsource) & "/" & _StringAddThousandsSep($total) & ")") ;show progress
    GUICtrlSetData($progressbar, $lookatsource / $total * 99) ;I use 99 cuz I hate seeing a full progress bar when something isn't done yet!!!

    If Not FileExists($target & "\" & $sourcelist[$lookatsource]) Then ;file does need to be synced
        $copied=_CopyWithProgress($basedir & "\" & $sourcelist[$lookatsource], $target & "\" & $sourcelist[$lookatsource]) ;copy the file
        if $copied=1 then $synced += 1 ;file synced, increment the synced file count
        if $copied=0 then ;file did not sync properly
            $failedlist=$failedlist&$basedir & "\" & $sourcelist[$lookatsource]&"*" ;add the file name to the list of failures
            $failed+=1 ;increment the failed counter
        EndIf
    EndIf
Next

$seconds=TimerDiff($begin) / 1000
if $seconds<60 then 
    $time=round($seconds,2)
    $type=" Seconds"
Else
    $time=round($seconds/60,2)
    $type=" Minutes"
EndIf

MsgBox(0, "Finished in " & _StringAddThousandsSep($time)&$type, "Synced " & _StringAddThousandsSep($synced) & " files with "&$failed&" errors.") ;shos some stats when done
if $failed>0 then 
    $showfailed=msgbox(4,"See Failed?","Do you want to see the files that failed?")
    if $showfailed=6 Then
        StringTrimRight($failedlist,1) ;take off the trailing * delimeter
        $failedarray=StringSplit($failedlist,"*") ;break up the list of failed files
        $failedwindow=GUICreate("Failed Files",@DesktopWidth,300) ;to display the failed files
        $failedlist=GUICtrlCreateList("",5,5,@DesktopWidth-10,290) ;to display the failed files
        GUISetState(@sw_show,$failedwindow) ;done setting up GUI
        for $show=1 to $failed
            GUICtrlSetData($failedlist,$failedarray[$show]) ;list the failed files
        Next
        Do
            $msg=GUIGetMsg()
            if $msg=$gui_event_close then exit ;when the failed window is closed, quit the program
        until 1=2
;   _ArrayDisplay($failedarray,"Failed Files")
    EndIf
EndIf
Exit




Func profiles()
    $currentprofiles=RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\MaxImuM Advantage","Sync") ;get list of current profiles
    if $currentprofiles="" Then
        msgbox(48,"No Profiles!","There are no profiles to load!")
    Else
        $selectprofile=GUICreate("Select A Profile",200,202)
        $showprofiles=GUICtrlCreateList("",5,5,190,170)
        $selectprofilebutton=GUICtrlCreateButton("Load",5,171,60)
        $profiledelete=GUICtrlCreateButton("Delete",135,171,60)
        GUISetState(@sw_show,$selectprofile)
        GUICtrlSetState($showprofiles,$gui_disable)     
        $profilelist=StringSplit($currentprofiles,"*")
        for $cycle=1 to $profilelist[0] step 3
            GUICtrlSetData($showprofiles,$profilelist[$cycle])          
        Next
        GUICtrlSetState($showprofiles,$gui_enable)
    EndIf

        Do
            $msg = GUIGetMsg()
            if $msg=$gui_event_close Then
                ExitLoop
            EndIf
            If $msg = $selectprofilebutton Then
                $selectedprofile=GUICtrlRead($showprofiles)
                for $cycle=1 to $profilelist[0]
                    if $profilelist[$cycle]=$selectedprofile Then
                        GUICtrlSetData($sourceinput,$profilelist[$cycle+1])
                        GUICtrlSetData($targetinput,$profilelist[$cycle+2])
                        ExitLoop
                    EndIf   
                Next
                $loaded=1
                ExitLoop
            EndIf
            If $msg = $profiledelete Then
                $selectedprofile=GUICtrlRead($showprofiles)
                $reallydelete=msgbox(4,"Really Delete?","Are you sure you want to delete the """&$selectedprofile&""" profile?")
                if $reallydelete=6 Then
                    for $cycle=1 to $profilelist[0]
                        if $profilelist[$cycle]<>$selectedprofile Then
                            $newprofilelist=$newprofilelist&$profilelist[$cycle]&"*"
                        Else
                            $cycle+=2
                        EndIf
                    Next
                    $len=StringLen($newprofilelist)
                    $newprofilelist=stringmid($newprofilelist,1,$len-1)
                    RegWrite("HKEY_LOCAL_MACHINE\SOFTWARE\MaxImuM Advantage","Sync","REG_SZ",$newprofilelist)
                    $newprofilelist=""
                    ExitLoop
                EndIf
            EndIf
        Until 1 = 2
GUIDelete($selectprofile)
EndFunc



Func theheart($basedir)

    While GUICtrlRead($pause) == 1 ;look for the pause button
        $quit=msgbox(36,"Paused","Quit Now?")
        if $quit=6 then exit ;yes
        if $quit=7 then GUICtrlSetState($pause,$gui_unchecked) ;no, so uuncheck the box 
    WEnd

    $basesearch = FileFindFirstFile($basedir & "\*.*")
    If $basesearch == -1 Then Return 0 ; specified folder is empty!
    While @error <> 1
        $basefile = FileFindNextFile($basesearch)
        ; skip these
        If $basefile == "." Or $basefile == ".." Or $basefile == "" Then
            ContinueLoop
        EndIf
        ; if it's a dir then call this function again (nesting the function is clever ;)
        $formattedfile = $basedir & "\" & $basefile
        If StringInStr(FileGetAttrib($formattedfile), "D") > 0 Then

            theheart($formattedfile)

        Else

            ; Files we need to deal with

            $filename = StringTrimLeft($formattedfile, $trimamount)
            $temp = $temp & $filename & Chr(1) ;makes a string with all results mashed together
            $howmany += 1
            GUICtrlSetData($sourceinfo,_StringAddThousandsSep($howmany))
        EndIf
    WEnd
    FileClose($basesearch)
    Return 1
EndFunc   ;==>theheart





Func _CopyWithProgress($inSource, $inDest)

    ; Function to copy a file showing a realtime progress bar
    ;
    ; By Paul Niven (NiVZ)
    ;
    ; 05/11/2008
    ;
    ; Parameters:
    ;
    ; $inSource  -   Full path to source file
    ; $inDest   -    Full path to Target file (structure will be created if it does not exist
    ;
    ; Returns 0 if copy is successful and 1 if it fails
    ;
    ;
    ; Updates:
    ;
    ; 06/11/2008 - Now shows SOURCE and DEST on seperate lines while copying
    ;           - Now uses Kernal32 and DllCallBackRegister to provide faster copying
    ;             Kernal32 and DllCallBackRegister taken from _MultiFileCopy.au3
    ;             See: http://www.autoit.de/index.php?page=Thread&postID=58875


    If Not IsDeclared("callback") Then Local $callback = DllCallbackRegister('__Progress', 'int', 'uint64;uint64;uint64;uint64;dword;dword;ptr;ptr;str')
    If Not IsDeclared("ptr") Then Local $ptr = DllCallbackGetPtr($callback)

    $DestDir = StringLeft($inDest, StringInStr($inDest, "\", "", -1))

    If Not FileExists($DestDir) Then DirCreate($DestDir)

    $ret = DllCall('kernel32.dll', 'int', 'CopyFileExA', 'str', $inSource, 'str', $inDest, 'ptr', $ptr, 'str', '', 'int', 0, 'int', 0)

    If $ret[0] <> 0 Then
        ; Success
        Return 1
    Else
        ; Fail
        Return 0
    EndIf

EndFunc   ;==>_CopyWithProgress


Func __Progress($FileSize, $BytesTransferred, $StreamSize, $StreamBytesTransferred, $dwStreamNumber, $dwCallbackReason, $hSourceFile, $hTargetFile, $lpData)

    GUICtrlSetData($copyprogress, Round($BytesTransferred / $FileSize * 100, 0))

EndFunc   ;==>__Progress

While running and working on a large copy, if the GUI is clicked on or if another window is opened and covers this and is then minimized, basically any interruption/interaction the GUI goes mental. Is there a way to make the GUI refresh every 100ms or something? Or a flag to keep it alive? I can't lock the GUI or the progressbar will not work. Also, is this just being caused by the _CopyWithProgress? Like, while that routine is busy the GUI isn't active?

Thanks

Ian

My projects:

  • IP Scanner - Multi-threaded ping tool to scan your available networks for used and available IP addresses, shows ping times, resolves IPs in to host names, and allows individual IPs to be pinged.
  • INFSniff - Great technicians tool - a tool which scans DriverPacks archives for INF files and parses out the HWIDs to a database file, and rapidly scans the local machine's HWIDs, searches the database for matches, and installs them.
  • PPK3 (Persistent Process Killer V3) - Another for the techs - suppress running processes that you need to keep away, helpful when fighting spyware/viruses.
  • Sync Tool - Folder sync tool with lots of real time information and several checking methods.
  • USMT Front End - Front End for Microsoft's User State Migration Tool, including all files needed for USMT 3.01 and 4.01, 32 bit and 64 bit versions.
  • Audit Tool - Computer audit tool to gather vital hardware, Windows, and Office information for IT managers and field techs. Capabilities include creating a customized site agent.
  • CSV Viewer - Displays CSV files with automatic column sizing and font selection. Lines can also be copied to the clipboard for data extraction.
  • MyDirStat - Lists number and size of files on a drive or specified path, allows for deletion within the app.
  • 2048 Game - My version of 2048, fun tile game.
  • Juice Lab - Ecigarette liquid making calculator.
  • Data Protector - Secure notes to save sensitive information.
  • VHD Footer - Add a footer to a forensic hard drive image to allow it to be mounted or used as a virtual machine hard drive.
  • Find in File - Searches files containing a specified phrase.
Link to comment
Share on other sites

AutoIt is single threaded. So if you enter a long and laborious processing routine where Sleep() is not called (GuiGetMsg() has sleep built in / MsgBox() does not cause this problem, etc there are other exceptions), then your app cannot process any window messages (GUI updates and other stuff). If this goes on for long enough (5-10 seconds maybe?) Windows thinks the program has hung and you get the whitish GUI and the (Not Responding) window title. If your app must do long processing tasks, and you're only using AutoIt, then you're out of luck. Other programming languages get around this by putting the interface code into another thread, or the processing into another thread. Obviously AutoIt cannot do this. If you have written the processing functions yourself, try to build in a short sleep somewhere so the GUI can process window messages, and see if that helps.

EDIT -

Can you post a smaller script showing your problem?

Edited by wraithdu
Link to comment
Share on other sites

Thanks for your reply. I wrote this sync script but did not write the _CopyWithProgress function. I don't think that I can do anything with that function, it's above my understanding :D

As for a shorter script, I've experienced it on other apps I've written and kinda thought it was something to do with AutoIt's threads. Soooooo, I guess it just is what it is.

If I get VERY ambitious I may look in to some of the multithreading threads in General Help, maybe something can be done to multithread this app.

Thanks again!!

Ian

My projects:

  • IP Scanner - Multi-threaded ping tool to scan your available networks for used and available IP addresses, shows ping times, resolves IPs in to host names, and allows individual IPs to be pinged.
  • INFSniff - Great technicians tool - a tool which scans DriverPacks archives for INF files and parses out the HWIDs to a database file, and rapidly scans the local machine's HWIDs, searches the database for matches, and installs them.
  • PPK3 (Persistent Process Killer V3) - Another for the techs - suppress running processes that you need to keep away, helpful when fighting spyware/viruses.
  • Sync Tool - Folder sync tool with lots of real time information and several checking methods.
  • USMT Front End - Front End for Microsoft's User State Migration Tool, including all files needed for USMT 3.01 and 4.01, 32 bit and 64 bit versions.
  • Audit Tool - Computer audit tool to gather vital hardware, Windows, and Office information for IT managers and field techs. Capabilities include creating a customized site agent.
  • CSV Viewer - Displays CSV files with automatic column sizing and font selection. Lines can also be copied to the clipboard for data extraction.
  • MyDirStat - Lists number and size of files on a drive or specified path, allows for deletion within the app.
  • 2048 Game - My version of 2048, fun tile game.
  • Juice Lab - Ecigarette liquid making calculator.
  • Data Protector - Secure notes to save sensitive information.
  • VHD Footer - Add a footer to a forensic hard drive image to allow it to be mounted or used as a virtual machine hard drive.
  • Find in File - Searches files containing a specified phrase.
Link to comment
Share on other sites

I looked up the CopyFileEx function, and this is exactly your problem. AutoIt sits waiting for the DllCall() to return and does not process window messages, causing the 'stuck' GUI. I had this same problem with a file hashing (MD5, SHA, etc) app I wrote. I ended up modifying the hash.dll source and writing my own helper DLL to thread the hash calls. Then I could use WaitForSingleObject() in a loop to know when the process had finished in my main app, while still allowing the GUI to update via short Sleep() calls. It worked really well.

Frankly, I'm surprised the progress bar actually updates, since that would imply that window messages are getting through, at least to some degree. Maybe I'll play with this function some time.

Link to comment
Share on other sites

I looked at the original function, and this happens there as well with really big files, esp if you click on the progress box during the copy. The _MultiFileCopy script will have the same issue for a really big file for the same reason. That script copies files one by one, so if none are too big, then you won't get this problem since the script is active. I've noticed this happens most with blocked DllCall() calls.

Link to comment
Share on other sites

Thanks for the extra info :D

Ian

My projects:

  • IP Scanner - Multi-threaded ping tool to scan your available networks for used and available IP addresses, shows ping times, resolves IPs in to host names, and allows individual IPs to be pinged.
  • INFSniff - Great technicians tool - a tool which scans DriverPacks archives for INF files and parses out the HWIDs to a database file, and rapidly scans the local machine's HWIDs, searches the database for matches, and installs them.
  • PPK3 (Persistent Process Killer V3) - Another for the techs - suppress running processes that you need to keep away, helpful when fighting spyware/viruses.
  • Sync Tool - Folder sync tool with lots of real time information and several checking methods.
  • USMT Front End - Front End for Microsoft's User State Migration Tool, including all files needed for USMT 3.01 and 4.01, 32 bit and 64 bit versions.
  • Audit Tool - Computer audit tool to gather vital hardware, Windows, and Office information for IT managers and field techs. Capabilities include creating a customized site agent.
  • CSV Viewer - Displays CSV files with automatic column sizing and font selection. Lines can also be copied to the clipboard for data extraction.
  • MyDirStat - Lists number and size of files on a drive or specified path, allows for deletion within the app.
  • 2048 Game - My version of 2048, fun tile game.
  • Juice Lab - Ecigarette liquid making calculator.
  • Data Protector - Secure notes to save sensitive information.
  • VHD Footer - Add a footer to a forensic hard drive image to allow it to be mounted or used as a virtual machine hard drive.
  • Find in File - Searches files containing a specified phrase.
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...