tk1 Posted June 6, 2018 Share Posted June 6, 2018 Hello, I've been away for a long time but working on fixing some things in a script I wrote a long time ago to monitor changes in the external IP address of my Internet modem. Here's some sample code that my question is based on: ;GUI create code to create buttons, labels, etc. ;Part of the GUI has the following button: Global $btnRefresh = GUICtrlCreateButton("Refresh", 88, 112, 75, 29, $BS_CENTER, -1) GUICtrlSetOnEvent(-1, "fuRefresh") GUICtrlSetFont(-1, 10, 400, 0, "Arial") GUICtrlSetTip(-1, "Click to refresh the last known IP and last time it was checked.") ;More GUI code ;End of GUI code, begin of the rest of the script. fuRefresh() Func fuRefresh() ;Refresh the labels in the GUI. ;code that gets info and makes updates fuWaitForChange() EndFunc Func fuWaitForChange() While 1 ;Code that checks for a change, then sleeps 60 times for 1 second each time. WEnd EndFunc I hope that's not too stripped down that it prevents you from understanding my question. Steps: 1. I start the script, the GUI comes up, displays data, and periodically checks for changes. 2. At some point, I want to manually update the data, so I click the Refresh button. 3. The data is refreshed on the GUI and the script continues to run and make periodic updates, however now all buttons and the red X (to close the GUI) will not respond. At this point, reclicking the refresh button does nothing and the only way to close the GUI is to end task it (I have an Exit button that also ceases to function). The function "fuWaitForChange()" has a While loop in it. I'm thinking when I click the Refresh button, it again calls the fuWaitForChange() function creating another instance of a While loop inside the first. Is this correct, and if so is this the problem causing the lack of response to clicks (the rest of the script continues to run --checking for changes, updating labels, etc.)? When I tried putting just an ExitLoop in the beginning of the fuRefresh() function, I get a syntax error because it's 'outside a loop'. I gamed it by creating a simple While 1 loop inside the beginning of the fuRefresh() function, with only an ExitLoop 2 inside that While loop (surprisingly I don't get a syntax error when I do a syntax check!). Func fuRefresh() ;Refresh the labels in the GUI. While 1 ExitLoop 2 WEnd ;code that gets info and makes updates fuWaitForChange() EndFunc That just doesn't feel right, though. I'd appreciate any help with this. Thanks, tk1 Link to comment Share on other sites More sharing options...
careca Posted June 6, 2018 Share Posted June 6, 2018 Did you ever heard of adlib function? could be good for that. Spoiler Renamer - Rename files and folders, remove portions of text from the filename etc. GPO Tool - Export/Import Group policy settings. MirrorDir - Synchronize/Backup/Mirror Folders BeatsPlayer - Music player. Params Tool - Right click an exe to see it's parameters or execute them. String Trigger - Triggers pasting text or applications or internet links on specific strings. Inconspicuous - Hide files in plain sight, not fully encrypted. Regedit Control - Registry browsing history, quickly jump into any saved key. Time4Shutdown - Write the time for shutdown in minutes. Power Profiles Tool - Set a profile as active, delete, duplicate, export and import. Finished Task Shutdown - Shuts down pc when specified window/Wndl/process closes. NetworkSpeedShutdown - Shuts down pc if download speed goes under "X" Kb/s. IUIAutomation - Topic with framework and examples Au3Record.exe Link to comment Share on other sites More sharing options...
czardas Posted June 6, 2018 Share Posted June 6, 2018 (edited) It may be acceptable in some languages, including AutoIt, to code the ExitLoop instruction inside a second function. Unfortunately AU3Check cannot determine that a function was called from within a loop without running (interpreting) the code. I personally always call ExitLoop directly from within the loop depending on a condition of some sort: it makes it easier to understand the code. There are many ways to set and check a condition. I give one simple example using ByRef. You could also set a global flag, or return a value from the second function, to trigger such an action. I hope this helps. CallingFunc() Func CallingFunc() Local $iCondition = 0 While 1 ModifyAction($iCondition) If $iCondition = 5 Then ExitLoop WEnd EndFunc Func ModifyAction(ByRef $iCondition) $iCondition += 1 ConsoleWrite($iCondition & @LF) EndFunc Edited June 6, 2018 by czardas operator64 ArrayWorkshop Link to comment Share on other sites More sharing options...
tk1 Posted June 6, 2018 Author Share Posted June 6, 2018 5 hours ago, careca said: Did you ever heard of adlib function? could be good for that. Hi careca. I had not heard or nor used adlib. From the help, it says to keep it simple since it runs so often and has the potential to create CPU load. The actual code is not as simple as I made it in my example above. It's an interesting function, though, and I'll have to keep in mind for future projects. Thank you for pointing it out! 4 hours ago, czardas said: It may be acceptable in some languages, including AutoIt, to code the ExitLoop instruction inside a second function. Unfortunately AU3Check cannot determine that a function was called from within a loop without running (interpreting) the code. I personally always call ExitLoop directly from within the loop depending on a condition of some sort: it makes it easier to understand the code. There are many ways to set and check a condition. I give one simple example using ByRef. You could also set a global flag, or return a value from the second function, to trigger such an action. I hope this helps. CallingFunc() Func CallingFunc() Local $iCondition = 0 While 1 ModifyAction($iCondition) If $iCondition = 5 Then ExitLoop WEnd EndFunc Func ModifyAction(ByRef $iCondition) $iCondition += 1 ConsoleWrite($iCondition & @LF) EndFunc I'm sorry czardas, I don't see how this would help in my situation. In your example, you're calling one function from within the While loop of another. In my situation/example, the only time the Refresh is called another time is when the user clicks it, so it is random if at all. Maybe I'm missing something in your suggestion? Thank you both of you, tk1 Link to comment Share on other sites More sharing options...
Subz Posted June 6, 2018 Share Posted June 6, 2018 Here is an example using AdlibRegister and Button, remember when using a While Loop you need to force an exit, other #include <Inet.au3> #include <GUIConstantsEx.au3> ;~ You could change the "192.168.1.1" to _GetIp(), but wanted to give you a visual example Global $idIpAddress, $sIpAddress = "192.168.1.1" Example() Func Example() ; Create a GUI with various controls. Local $hGUI = GUICreate("Example", 250, 50) $idIpAddress = GUICtrlCreateLabel($sIpAddress, 10, 10, 145, 30) GUICtrlSetFont(-1, 14, 800, 0, "Arial") Local $idRefresh = GUICtrlCreateButton("Refresh", 155, 10, 85, 25) GUICtrlSetFont(-1, 10, 400, 0, "Arial") GUICtrlSetTip(-1, "Click to refresh the last known IP and last time it was checked.") GUISetState(@SW_SHOW, $hGUI) AdlibRegister("_GetIpAddress", 3000) ;~ Check IP Address every 3 seconds While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop Case $idRefresh _GetIpAddress() EndSwitch WEnd GUIDelete($hGUI) EndFunc ;==>Example Func _GetIpAddress() $sIpAddress = _GetIP() If $sIpAddress = GUICtrlRead($idIpAddress) Then Return GUICtrlSetData($idIpAddress, $sIpAddress) EndFunc If you want use GUIOnEventMode (don't mix the two modes) you could use something like: #include <Inet.au3> #include <GUIConstantsEx.au3> #include <MsgBoxConstants.au3> Global $idIpAddress, $sIpAddress Opt("GUIOnEventMode", 1) GUICreate("Hello World", 200, 100) GUISetOnEvent($GUI_EVENT_CLOSE, "CLOSEButton") $idIpAddress = GUICtrlCreateLabel("192.168.1.1", 30, 10) Local $iOKButton = GUICtrlCreateButton("OK", 70, 50, 60) GUICtrlSetOnEvent($iOKButton, "OKButton") GUISetState(@SW_SHOW) While 1 Sleep(100) WEnd Func OKButton() While 1 $sIpAddress = _GetIP() If $sIpAddress = GUICtrlRead($idIpAddress) Then ExitLoop GUICtrlSetData($idIpAddress, $sIpAddress) Sleep(100) WEnd EndFunc Func CLOSEButton() MsgBox($MB_OK, "GUI Event", "You selected CLOSE! Exiting...") Exit EndFunc Link to comment Share on other sites More sharing options...
czardas Posted June 7, 2018 Share Posted June 7, 2018 (edited) 6 hours ago, tk1 said: I'm sorry czardas, I don't see how this would help in my situation. In your example, you're calling one function from within the While loop of another. In my situation/example, the only time the Refresh is called another time is when the user clicks it, so it is random if at all. Maybe I'm missing something in your suggestion? I assume the function will be called more than once and I can only guess what code might have been stripped away. In your attempted patch you wrote ExitLoop 2. That means that you exit two loops, one of which does not exist within the function. For you to have written this implies that you probably need some clarification regarding syntax. 12 hours ago, tk1 said: When I tried putting just an ExitLoop in the beginning of the fuRefresh() function, I get a syntax error because it's 'outside a loop'. Func fuRefresh() ;Refresh the labels in the GUI. While 1 ExitLoop 2 WEnd ;code that gets info and makes updates fuWaitForChange() EndFunc Understanding basic syntax should help you to solve this and other problems in the future, which is why I gave the example above (3rd post): to try and help clarify things for you. From your original description of this problem, it sounds like you got stuck inside an infinite loop somewhere. You need to find it and get out when the right condition has been met. Edited June 7, 2018 by czardas operator64 ArrayWorkshop Link to comment Share on other sites More sharing options...
careca Posted June 7, 2018 Share Posted June 7, 2018 15 hours ago, tk1 said: From the help, it says to keep it simple since it runs so often and has the potential to create CPU load. That really depends on the timer you put in place, of course, if the time is too low it keeps calling the function, but if it's more than a second you'll be fine. Spoiler Renamer - Rename files and folders, remove portions of text from the filename etc. GPO Tool - Export/Import Group policy settings. MirrorDir - Synchronize/Backup/Mirror Folders BeatsPlayer - Music player. Params Tool - Right click an exe to see it's parameters or execute them. String Trigger - Triggers pasting text or applications or internet links on specific strings. Inconspicuous - Hide files in plain sight, not fully encrypted. Regedit Control - Registry browsing history, quickly jump into any saved key. Time4Shutdown - Write the time for shutdown in minutes. Power Profiles Tool - Set a profile as active, delete, duplicate, export and import. Finished Task Shutdown - Shuts down pc when specified window/Wndl/process closes. NetworkSpeedShutdown - Shuts down pc if download speed goes under "X" Kb/s. IUIAutomation - Topic with framework and examples Au3Record.exe Link to comment Share on other sites More sharing options...
tk1 Posted June 7, 2018 Author Share Posted June 7, 2018 (edited) Ok, apparently I'm not making myself clear with the example I gave so I'll go ahead and put the whole code here so you can see what exactly I'm doing. I apologize for not doing that from the get-go. I agree that I seem to be creating an infinite loop, but I'm just not sure how to prevent it in this situation (I'm sure there's a way, there always is). BTW, some of the comments in the code are just notes to myself. expandcollapse popup#Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_Icon=IP1.ico #AutoIt3Wrapper_Res_Fileversion=0.0.3.6 #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** ; To Do: ; Backup/clear history ; Create file for web ; Upload ip information to website ; Check for hung ftp process ; Implement ini - use for folder locations, ftp profile, etc. ; if no ini, create one with default settings and locations (search and discover) ; ; Required extras: ; PuTTY ; SFTPEx.au3 #include <Inet.au3> #include <StaticConstants.au3> #include <GUIConstantsEx.au3> #include <GuiButton.au3> #include <ProgressConstants.au3> #include <EditConstants.au3> #include <WindowsConstants.au3> #include <FileConstants.au3> #include <SFTPEx.au3> ;Not included in normal AutoIt install #include <Constants.au3> #include <WMI_PingPlus.au3> ;Not included in normal AutoIt install, I modified WMI_Ping.au3 Global $sExtIPFullPath = "C:\Data\extIP\extIP.txt" Global $sExtIPHistoryFullPath = "C:\Data\extIP\extIPhist.txt" Global $sIPChangedTime Global $sWINIPcurr Global $sWANIPlast Global $sPingCheck = "C:\Data\extIP\PingCheck.txt" Global $sAppVersion = FileGetVersion(@ScriptFullPath) Opt("GUIOnEventMode", 1) ;Change to OnEvent mode Global $extIP = GUICreate("Ext IP Mon " & $sAppVersion, 254, 150, 285, 264, -1, -1) GUISetOnEvent($GUI_EVENT_CLOSE, "fuClose") GUICtrlCreateLabel("Last known IP:", 14, 16, 90, 15, $SS_LEFT, -1) ; Include StaticConstants.au3, Left-aligns text in a simple rectangle. GUICtrlSetFont(-1, 10, 400, 0, "Arial") GUICtrlSetBkColor(-1, "-2") Global $sLastKnownIP = GUICtrlCreateLabel("0.0.0.0", 128, 16, 110, 15, $ES_RIGHT) ; Include EditConstants.au3, Right-aligns text in a multiline edit control. GUICtrlSetFont(-1, 10, 600, 0, "Arial") GUICtrlCreateLabel("Changed: ", 14, 42, 90, 23) GUICtrlSetFont(-1, 10, 400, 0, "Arial") Global $sLastChanged = GUICtrlCreateLabel($sIPChangedTime, 105, 42, 132, 23, $ES_RIGHT) GUICtrlSetFont(-1, 10, 400, 0, "Arial") GUICtrlCreateLabel("Verified:", 14, 69, 90, 15, -1, -1) GUICtrlSetFont(-1, 10, 400, 0, "Arial") GUICtrlSetBkColor(-1, "-2") Global $sTimeChecked = GUICtrlCreateLabel("", 105, 69, 132, 23, $ES_RIGHT) GUICtrlSetFont(-1, 10, 400, 0, "Arial") GUICtrlSetBkColor(-1, "-2") GUICtrlSetTip(-1, "This is the last time the known IP was checked.") Global $sStatus = GUICtrlCreateLabel("--> Begin <--", 85, 90, 150, 15, -1, -1) GUICtrlSetBkColor(-1, "-2") Global $oMinuteBar = GUICtrlCreateProgress(85, 106, 73, 1, $pbs_smooth) GUICtrlSetBkColor(-1, "-2") Global $btnShowHist = GUICtrlCreateButton("History", 8, 112, 75, 29, $BS_CENTER, -1) ; Include ButtonConstants.au3, Centers text horizontally in the button rectangle. GUICtrlSetOnEvent(-1, "fuShowHist") GUICtrlSetFont(-1, 10, 400, 0, "Arial") GUICtrlSetTip(-1, "Click to open the history file in Notepad.") Global $btnRefresh = GUICtrlCreateButton("Refresh", 88, 112, 75, 29, $BS_CENTER, -1) GUICtrlSetOnEvent(-1, "fuRefresh") GUICtrlSetFont(-1, 10, 400, 0, "Arial") GUICtrlSetTip(-1, "Click to refresh the last known IP and last time it was checked.") Global $btnExit = GUICtrlCreateButton("Exit", 168, 112, 75, 29, -1, -1) GUICtrlSetOnEvent(-1, "fuClose") GUICtrlSetFont(-1, 10, 400, 0, "Arial") GUISetState(@SW_SHOW, $extIP) $sWANIPlast = fuReadPubIP() ;Get the last known IP from the file. While 1 fuRefresh() WEnd Func fuRefresh() ;Refresh the labels in the GUI. GUICtrlSetData($sStatus, "Getting current ext. IP.") fuGetWANIP() ;Get current external IP. fuCheckWANIP() ;Verify external IP is valid. GUICtrlSetData($sStatus, "Last known vs. current.") fuUpdateIP() ;Updates the current external IP address. GUICtrlSetData($sStatus, "Begin ping cycle.") fuWaitForIPChange() ;Ping current IP, until unpingable. EndFunc ;==>fuRefresh Func fuReadPubIP() ;Get the last known IP from the file. GUISetCursor(1) ;Set the cursor to APPSTARTING Local $sIPaddr = FileReadLine($sExtIPFullPath, 1) ;Reads the first line of the file. GUICtrlSetData($sLastKnownIP, $sIPaddr) ;Updates IP on the GUI. GUICtrlSetData($sTimeChecked, @YEAR & "." & @MON & "." & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC) $sIPChangedTime = FileReadLine($sExtIPFullPath, 3) & " " & FileReadLine($sExtIPFullPath, 4) GUICtrlSetData($sLastChanged, $sIPChangedTime) GUISetCursor(2) ;Set the cursor to ARROW If StringInStr($sIPaddr, ".") = "0" Or $sIPaddr = "-1" Then Return "86" Else Return $sIPaddr EndIf EndFunc ;==>fuReadPubIP Func fuGetWANIP() ;Get current external IP. GUISetCursor(1) ;Set the cursor to APPSTARTING $sWINIPcurr = _GetIP() ;Call to the external function in Inet.au3 GUISetCursor(2) ;Set the cursor to ARROW EndFunc ;==>fuGetWANIP Func fuCheckWANIP() ;Verify $sWINIPcurr is a valid external IP. While $sWINIPcurr = "" Or $sWINIPcurr = "-1" Or $sWINIPcurr = "0" Or StringLeft($sWINIPcurr, 7) = "192.168" Sleep(20000) fuGetWANIP() WEnd EndFunc ;==>fuCheckWANIP Func fuUpdateIP() ;Updates the current external IP address. GUISetCursor(1) ;Set the cursor to APPSTARTING. ;Get the IP if it has changed or is missing. If $sWANIPlast = "" Or $sWANIPlast <> $sWINIPcurr Then FileDelete($sExtIPFullPath) FileWriteLine($sExtIPFullPath, $sWINIPcurr) ;Creates the current IP file, & writes the new IP to it. Local $sDateChanged = @YEAR & "." & @MON & "." & @MDAY Local $sTimeChanged = @HOUR & ":" & @MIN & ":" & @SEC ;Add the date and time to the current IP file. FileWriteLine($sExtIPFullPath, @CRLF & $sDateChanged & @CRLF & $sTimeChanged) ;Update the history file with the date and time. FileWriteLine($sExtIPHistoryFullPath, $sDateChanged & @TAB & $sTimeChanged & @TAB & $sWINIPcurr) $sWANIPlast = fuReadPubIP() EndIf GUICtrlSetData($sStatus, "Update ext. IP.") GUISetCursor(2) ;Set the cursor to ARROW EndFunc ;==>fuUpdateIP Func fuWaitForIPChange() ;Ping current IP, until unpingable. Local $sPingTime Local $sResult GUICtrlSetData($sStatus, $sWANIPlast) While 1 $sResult = WMI_Ping($sWANIPlast, 2) If Not @error Then GUISetCursor(1) ;Set the cursor to APPSTARTING. For $i = 1 To 5 GUICtrlSetData($sStatus, "Minute " & $i & " of 5.") $sPingTime = @HOUR & ":" & @MIN & ":" & @SEC & @TAB & @YEAR & "." & @MON & "." & @MDAY & _ @TAB & "Pinged OK" FileWriteLine($sPingCheck, $sPingTime) fuMinuteBarUpdate() ;Wait for 1 minute. $sWANIPlast = fuReadPubIP() Next GUICtrlSetData($sStatus, "Publish to web.") ; RETHINK: Is this the correct place to publish to web? fuPubToWeb() Else GUICtrlSetData($sStatus, $sWANIPlast & " is unpingable.") GUISetCursor(2) ;Set the cursor to ARROW ExitLoop EndIf WEnd GUICtrlSetData($sStatus, "Restart IP check cycle.") EndFunc ;==>fuWaitForIPChange Func fuPubToWeb() ;Publish to the website. ;_SFTP_Open("C:\PROGRA~1\Internet\PuTTY\putty.exe") ; publish the IP address, and the date/time of the last ping. FileDelete($sPingCheck) ;**** WHEN READY, MOVE TO fuWaitForIPChange **** EndFunc ;==>fuPubToWeb Func fuMinuteBarUpdate() For $u = 1 To 60 ;increment status bar by 1/60 GUICtrlSetData($oMinuteBar, (100 - ($u / 60) * 100)) Sleep(1000) Next EndFunc ;==>fuMinuteBarUpdate Func fuShowHist() ;Open the history file in Notepad. Run("notepad.exe " & $sExtIPHistoryFullPath) EndFunc ;==>fuShowHist Func fuClose() Exit EndFunc ;==>fuClose I also attached my modified WMI_PingPlus.au3. WMI_PingPlus.au3 Edited June 7, 2018 by tk1 Link to comment Share on other sites More sharing options...
Subz Posted June 7, 2018 Share Posted June 7, 2018 You use the following query which always appears to fail, I'm not sure what you're expecting. SELECT * FROM Win32_PingStatus WHERE address = '86' AND TimeToLive = 2 AND BufferSize = 32 AND NoFragmentation = False AND RecordRoute = 0 AND ResolveAddressNames = False AND SourceRoute = '' Your Gui is freezing because of the call to fuMinuteBarUpdate it won't continue until its finished the loop, you need to use adlibregister or some other function to remove that loop., Link to comment Share on other sites More sharing options...
tk1 Posted June 7, 2018 Author Share Posted June 7, 2018 47 minutes ago, Subz said: You use the following query which always appears to fail, I'm not sure what you're expecting. SELECT * FROM Win32_PingStatus WHERE address = '86' AND TimeToLive = 2 AND BufferSize = 32 AND NoFragmentation = False AND RecordRoute = 0 AND ResolveAddressNames = False AND SourceRoute = '' Your Gui is freezing because of the call to fuMinuteBarUpdate it won't continue until its finished the loop, you need to use adlibregister or some other function to remove that loop., So if the IP is anything but a good, real, external IP address (e.g. "86") it is supposed to fail. That is the desired outcome, which will then trigger attempts to get the new valid external IP of the modem. Sometimes I was getting a successful ping on a valid external IP because the ISP had already reassigned that IP to another node, which for my purposes, was undesirable. To fix that, I added the TTL to the WMI_Ping so I could stop the ping at the WAN port of the modem. Your last statement caused me to realize the While loop in my fuWaitForIPChange() function is not pinging as often as I thought it was (once every 5 minutes vs. once a minute). Thanks, tk1 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