Popular Post Gianni Posted January 28, 2018 Popular Post Share Posted January 28, 2018 (edited) This is a simple and practical way to start and manage many processes. I use it to quickly query many remote clients for varyous informations. For processes I mean external programs that can run autonomously and that allows I/O redirection. Here the advantage is that when you start a process, you can also determine which function in your main program should receive results when the external process finishes executing. So you can have different processes and different "callback" functions. The whole works nearly autonomously and asynchronously. While spawned processes works, you can perform other tasks in the main script and leave the worry of receiving and managing the results, to the pre-established functions. As I sayd, everything is almost completely automatic and the processes do their work asynchronously and independently of the flow of the main script. To check and manage processes status, you have only to call as often as possible the _TasksCheckStatus() function that will quickly handle the running processes. AdlibRegister() could be used for this ripetitive "polling". In short: 1) setup single commandlines that will "generate" your wanted result 2) establish wich function of your main script should receive the above result 3) an optional Timeout in seconds is allowed (task from point 1 will be killed at timeout) see function header for details. The provided example of use, needs to be executed in a network environment with preferably many clients, and you have to be administrator. Populate the $aIPList[] array with ClientNames (or ip addresses), and this example script will query all the clients about some informations by running many processes asincronously. Then it will collect results from the processes as soon as "answers" are available and will populate the ListView with that data. I used a ListView so that you can see that all the cells will fill (quite quickly) in an asynchronous way. If you are not in a network with many clients, then this example is nearly useless... (sorry) Hope it can be of use... (suggestions, bug reports and emrovements are welcome) MultiTask.udf expandcollapse popup#include-once ; #include "Array.au3" ; #INDEX# ======================================================================================================================= ; Title .........: Multi Task Management (tasks spawned by the user via the _TaskRun function of this udf) ; AutoIt Version : ; Language ......: ; Description ...: Functions that assists to execute and manage user's spawned tasks. ; Author(s) .....: Chimp ; =============================================================================================================================== ; #CURRENT# ===================================================================================================================== ; _TaskRun Run a new task and stacks its references for later use ; _TasksCheckStatus Check the status of all running tasks and call the CallBack function when a task completed ; =============================================================================================================================== ; #INTERNAL_USE_ONLY#============================================================================================================ ; __TaskRemove frees task's stacked references ; __NOP No OPeration function ; =============================================================================================================================== Global $aTasks[1][7] = [[0]] ; Create and initialize the task reference stack to 0 ; #FUNCTION# ==================================================================================================================== ; Name ..........: _TaskRun ; Description ...: Runs a new task as specified in $sCommand ; Syntax ........: _TaskRun($vIndex, $sCommand[, $sParser = "__NOP"[, $vTimeout = False]]) ; Parameters ....: $vIndex - A variant value passed by the caller as a reference to this task. ; this value will be returned to the callback function along with results. ; ; $sCommand - A string value containing the full command to be executed ; ; $sParser - [optional] The name of the function to be called at the end of this task. ; Default is __NOP that is a "do nothing" function. ; P.S. ; the $aParser function will be called by the _TasksCheckStatus() function when ; this task will finish to run: ; an 1D array with 4 elements will be passed to the called function: ; element [0] the caller's index reference of this task ; element [1] the StdOut result of this task ; element [2] the StdErr result of this task ; element [3] the time spent by this task ; ; $vTimeout - [optional] A variant value. Default is False. ; if specified is the number of seconds for the Timeout. ; After this amount of seconds, if this task is still running, it is killed ; ; Return values .: the total number of stacked tasks ; Author ........: Chimp ; Modified ......: ; Remarks .......: ; Related .......: ; Link ..........: ; Example .......: ; =============================================================================================================================== Func _TaskRun($vIndex, $sCommand, $sParser = "__NOP", $vTimeout = False) ; ; Stack structure: ; ; +-----+ ; 0 | nr. | <-- [0][0] number of running tasks ; +-----+-----+-----+-----+-----+-----+-----+ ; 1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | ; +-----+-----+-----+-----+-----+-----+-----+ ; n | * | * | * | * | * | * | * | ; | | | | | | | ; | | | | | | [n][6] setted with the TimerInit() value at the start of this task. ; | | | | | | ; | | | | | [n][5] contains the required Timeout in seconds (Default is set to False = no timeout) ; | | | | | ; | | | | [n][4] The CallBack function name that will receive the results of this task ; | | | | ; | | | [n][3] the error message returned by the StdErr stream of this task ; | | | ; | | [n][2] the result of the command returned by the StdOut stream of this task ; | | ; | [n][1] the reference of this task passed by the user (will be passed back to the caller along with results) ; | ; [n][0] the PID of this running task ; ReDim $aTasks[$aTasks[0][0] + 2][7] ; add a new element to the stack $aTasks[0][0] += 1 ; add 1 to the number of running tasks ; Run the passed command with the I/O streams redirected $aTasks[$aTasks[0][0]][0] = Run($sCommand, "", @SW_HIDE, 6) ; 6 -> $STDOUT_CHILD (0x2) + $STDERR_CHILD (0x4) ; store references of this task to the stack $aTasks[$aTasks[0][0]][1] = $vIndex $aTasks[$aTasks[0][0]][4] = $sParser $aTasks[$aTasks[0][0]][5] = $vTimeout ; can be False or the number of seconds for the timeout of this task $aTasks[$aTasks[0][0]][6] = TimerInit() ; store the TimerInit() value for this task Return $aTasks[0][0] ; return the total number of running tasks EndFunc ;==>_TaskRun ; #FUNCTION# ==================================================================================================================== ; Name ..........: _TasksCheckStatus ; Description ...: Scans the status of all active tasks and checks if some task has finished its job ; This function should be called as aften as possible ; Syntax ........: _TaskCheckStatus() ; Parameters ....: None ; Return values .: number of still running tasks; * see remarks ; Author ........: Chimp ; Modified ......: ; Remarks .......: When a task finish, it is removed from the stack and results are passed to the callback function. ; An 1D array with 4 elements will be passed to the called function: ; element [0] the caller's index reference of this task ; element [1] the StdOut result of this task ; element [2] the StdErr result of this task ; element [3] the time spent by this task (approximative time) ; is an approximate datum because it also includes the delay ; added by the main loop before calling _TasksCheckStatus() ; Related .......: ; Link ..........: ; Example .......: ; =============================================================================================================================== Func _TasksCheckStatus() If $aTasks[0][0] = 0 Then Return 0 ; if no tasks then return Local $bEndTask ; will be setted to True if the checked task has finished or is killed by timeout Local $iPointer = 1 ; start checking from first task in the stack Local $aResults[4] ; this Array will be loaded with the results of the task Local $sCallBack ; the name of the function to call when a task ends its job Local $aArgs[2] = ["CallArgArray", ""] ; "special" array will be loaded with parameters for the CallBack function While $iPointer <= $aTasks[0][0] $bEndTask = False $aTasks[$iPointer][2] &= StdoutRead($aTasks[$iPointer][0]) ; read and store the StdOut stream $aTasks[$iPointer][3] &= StderrRead($aTasks[$iPointer][0]) ; read and store the StdErr stream ; If @error Then ; if there is an @error is because this task has finished its job If Not ProcessExists($aTasks[$iPointer][0]) Then ; if this task has finished its job $bEndTask = True ; flag the end of the work ElseIf $aTasks[$iPointer][5] <> False Then ; if task is still running see if a Timeout check was rquired and if so if is been reached If Int(TimerDiff($aTasks[$iPointer][6]) / 1000) >= $aTasks[$iPointer][5] Then ; timeout reached or exceeded!! $aTasks[$iPointer][3] = "@error Timeout " & $aTasks[$iPointer][3] ; insert an error message at the beginning of the StdErr stream StdioClose($aTasks[$iPointer][0]) ; close I/O streams ProcessClose($aTasks[$iPointer][0]) ; kill this process $bEndTask = True ; flag the end of this task EndIf EndIf If $bEndTask Then ; if this task has finished, get its results and send to the CallBack function $aResults[0] = $aTasks[$iPointer][1] ; ............. Index (the caller reference) $aResults[1] = $aTasks[$iPointer][2] ; ............. the StdOut generated by this task $aResults[2] = $aTasks[$iPointer][3] ; ............. the StdErr generated by this task $aResults[3] = TimerDiff($aTasks[$iPointer][6]) ; .. time spent by this task $sCallBack = $aTasks[$iPointer][4] ; the name of the function to be called $aArgs[1] = $aResults ; second element of the "CallArgArray" special array contains the $aResults array ; loaded with the parameters to be send to the CallBack function. (array in array) ; (the CallBack function will receive only the 1D 4 elements array $aResults) __TaskRemove($iPointer) ; remove references of this task from the stack ; call the CallBack function and pass the results of this task. ; (CallBack function should return as soon as possible because it stops the CheckStatus for the other tasks) ; Call($sCallBack, $aArgs) ; Call CallBack function --->---+ ; | ; <--- and return here ---------------------------------+ EndIf $iPointer += 1 ; check next task WEnd Return $aTasks[0][0] EndFunc ;==>_TasksCheckStatus ; Internal use. Remove the task references from the stack Func __TaskRemove($iElement) #cs If $iElement > $aTasks[0][0] Or $iElement < 1 Then Return StdioClose($aTasks[$iElement][0]) _ArrayDelete($aTasks, $iElement) #ce ; - new -------------------------------------------------- ; remove element without the _Array* udf If $iElement > 0 And $iElement <= $aTasks[0][0] Then StdioClose($aTasks[$iElement][0]) If $aTasks[0][0] > 1 Then For $i = 0 To UBound($aTasks, 2) - 1 $aTasks[$iElement][$i] = $aTasks[$aTasks[0][0]][$i] Next EndIf Else Return ; queue is empty or the required element is out of bound EndIf $aTasks[0][0] -= 1 ReDim $aTasks[$aTasks[0][0] + 1][UBound($aTasks, 2)] Return $aTasks[0][0] ; returns the number of tasks still running EndFunc ;==>__TaskRemove ; Internal use. An empty function Func __NOP($aDummy) ; NOP (No OPeration) EndFunc ;==>__NOP Example of use expandcollapse popup; #RequireAdmin #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <GuiListView.au3> #include '.\MultiTask.au3' ; ; IMPORTANT following array should be populated with many real HostNames ; here are just nonsense items used as placeholders Global $aIPList[] = [@ComputerName, @IPAddress1, @ComputerName, @ComputerName, 'InexistentClient', @ComputerName] Global $hGrid ; The ListView Handle Global $ahGrid[1] ; An array to keep handles of any listview row Example() MsgBox(0, "Debug:", "Done" & @CRLF & "Hit OK to exit") Func Example() Local $Form1 = GUICreate("Clients status", 760, 400) ; ; Create the ListView $hGrid = GUICtrlCreateListView("HostName|IP|Info|Last reboot|CPU|Last logon|Current logon", 0, 0, 760, 400) _GUICtrlListView_SetColumnWidth($hGrid, 0, 140) ; HostName _GUICtrlListView_SetColumnWidth($hGrid, 1, 100) ; IP _GUICtrlListView_SetColumnWidth($hGrid, 2, 80) ; Ping info ms or Off or Unknown or Timeout) _GUICtrlListView_SetColumnWidth($hGrid, 3, 120) ; Last Reboot _GUICtrlListView_SetColumnWidth($hGrid, 4, 40) ; cpu load ; last 2 columns a mutually exclusive. If there is a user logged it's shown on the last column ; if there is not a user logged then the last user that was logged is shown on column 4 instead _GUICtrlListView_SetColumnWidth($hGrid, 5, 140) ; Last logged UserId (if nobody is logged now) _GUICtrlListView_SetColumnWidth($hGrid, 6, 140) ; Currently logged UserId _GUICtrlListView_SetExtendedListViewStyle($hGrid, BitOR($LVS_EX_GRIDLINES, $LVS_EX_FULLROWSELECT)) ; show grid; select whole row ; GUISetState(@SW_SHOW) ; following line is needed if you have to refill the listview ; _GUICtrlListView_DeleteAllItems(GUICtrlGetHandle($hGrid)) ; empty the listview ReDim $ahGrid[UBound($aIPList)] ; this loop will run all needed tasks at once. ; The results of the tasks will be managed by the callback functions (nearly) autonomously and asynchronously For $i = 0 To UBound($aIPList) - 1 $ahGrid[$i] = _GUICtrlListView_AddItem($hGrid, "") ; create a listview row ; ; ... for each client ... ; ; spawn ping commands. result will be send to the _PingParse() function _TaskRun($i, "Ping -a -n 1 -4 " & $aIPList[$i], "_PingParse") ; spawn commands to recall LoggedOn User. Result will be send to the _LoggedOn() function _TaskRun($i, "wmic /node:" & $aIPList[$i] & " computersystem get username /value", "_LoggedOn", 5) ; 5 = timeout in 5 seconds ; spawn commands to recall Last reboot time. result will be send to the _LastReboot() function _TaskRun($i, "wmic /node:" & $aIPList[$i] & " os get lastbootuptime /value", "_LastReboot", 5) ; spawn commands to recall % of CPU load. result will be send to the _CPU_load() function _TaskRun($i, "wmic /node:" & $aIPList[$i] & " cpu get loadpercentage /value", "_CPU_load", 7) Next ; now, while all tasks are running ; we could perform other activities in the meantime ; or we can wait the end of all the tasks Do Sleep(250) ; you could perform other jobs here while waiting for the tasks to finish Until Not _TasksCheckStatus() ; <-- this function performs management of running tasks ; and should be called as often as possible EndFunc ;==>Example ; below are CallBack functions ; #FUNCTION# ==================================================================================================================== ; Name ..........: _PingParse (a callback function) ; Description ...: this function analize the output of a ping command and "extract" needed infos ; it fills columns 0 1 and 2 of the list view ($aTarget[0] is the line number of the listview to be filled) ; Syntax ........: _PingParse($aTarget[, $bDomain = True]) ; Parameters ....: $sTarget - An array with Ping results passed by the MultiTasks as soon as any ping task ends ; the passed array contains following data: ; $aTarget[0] Index for this task ; $aTarget[1] StdOut from ping (the whole output of the ping) ; $aTarget[2] StdErr from ping ; $aTarget[3] Time spent by this task to complete (is NOT the ping roundtrip time) ; $bDomain - [optional] A binary value. Default is True. (keep domain info in host name) ; ; Return values .: None. It Fills Listview columns 0, 1 and 2 ; column 0 : resolved HostName or "" ; column 1 : IP address or "" (this can contain last known IP even if now is offline) ; column 2 : roundtrip time or "Unknown" or "timeout" or "Off" ; ; Author ........: Chimp ; =============================================================================================================================== Func _PingParse($aTarget, $bDomain = True) ; $aTarget contains 4 elements: [0] Index, [1] StdOut, [2] StdErr, [3] time spent by this task Local $sOutput = $aTarget[1] ; stdout Local $0, $1, $2, $3, $aMs ; Local $iAnswer = -1, $iName = -1 Local $aResult[3] = ["", "", ""] ; [0]ms, [1]HostName, [2]IP $aMs = StringRegExp($sOutput, "([0-9]*)ms", 3) If Not @error Then ; Ping replayed $aResult[0] = $aMs[UBound($aMs) - 1] ; average ms Else ; $aResult[0] = "off" EndIf $0 = StringInStr($sOutput, "Ping") $1 = StringInStr($sOutput, "[") ; HostName decoded? If $1 Then ; HostName decoded $2 = StringInStr($sOutput, "]") $3 = StringInStr($sOutput, " ", 0, -2, $1) $aResult[1] = StringMid($sOutput, $3 + 1, $1 - $3 - 1) ; HostName $aResult[2] = StringMid($sOutput, $1 + 1, $2 - $1 - 1) ; IP Else If $0 Then ; pinging an IP address? ; $aResult[1] = "" ; no HostName Local $aFindIP = StringRegExp($sOutput, "\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", 3) If Not @error Then $aResult[2] = $aFindIP[0] Else ; unknown HostName $aResult[0] = "Unknown" $aResult[1] = $aIPList[$aTarget[0]] ; retrieve HostName from the $aIPList array EndIf EndIf If $bDomain = False Then Local $aSplit = StringSplit($aResult[1], ".", 2) ; 2 = $STR_NOCOUNT $aResult[1] = $aSplit[0] ; romove .domain part from the HostName EndIf If StringLeft($aTarget[2], 14) = "@error Timeout" Then $aResult[0] = "Timeout" ; Now that we have the infos, we compile related cells in ListView ; grid row-handle data column _GUICtrlListView_SetItemText($hGrid, $ahGrid[$aTarget[0]], $aResult[1], 0) ; first column "HostName" _GUICtrlListView_SetItemText($hGrid, $ahGrid[$aTarget[0]], $aResult[2], 1) ; second column "IP address" _GUICtrlListView_SetItemText($hGrid, $ahGrid[$aTarget[0]], $aResult[0], 2) ; third column "Infos about the ping" ; ConsoleWrite("Debug: " & "-> " & $aResult[0] & @TAB & $aResult[1] & @TAB & $aResult[2] & @CRLF) EndFunc ;==>_PingParse Func _LastReboot($aParameters) ; Last reboot DateTime $aParameters[1] = StringStripWS($aParameters[1], 8) Local $equal = StringInStr($aParameters[1], "=") If $equal Then _GUICtrlListView_AddSubItem($hGrid, $ahGrid[$aParameters[0]], WMIDateStringToDate(StringMid($aParameters[1], $equal + 1)), 3) ; column 3 EndIf EndFunc ;==>_LastReboot Func _CPU_load($aParameters) ; % of CPU load $aParameters[1] = StringStripWS($aParameters[1], 8) Local $equal = StringInStr($aParameters[1], "=") If $equal Then _GUICtrlListView_AddSubItem($hGrid, $ahGrid[$aParameters[0]], StringMid($aParameters[1], $equal + 1), 4) ; column 4 EndIf EndFunc ;==>_CPU_load Func _LoggedOn($aParameters) ; User now logged $aParameters[1] = StringStripWS($aParameters[1], 8) ; if none is logged, then find the user that was last logged (also using _TaskRun) If $aParameters[1] = "" Or $aParameters[1] = "UserName=" Then ; following syntax is by @iamtheky (thanks) ; https://www.autoitscript.com/forum/topic/189845-regexp-pattern-in-findstr-dos-command/?do=findComment&comment=1363106 Local $sCmd = 'cmd /c FOR /F "usebackq skip=2 tokens=1-3" %A IN (`REG QUERY ' & '"\\' & $aIPList[$aParameters[0]] & _ '\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI" /REG:64 /v LastLoggedOnUser 2^>nul`) Do @echo %C' _TaskRun($aParameters[0], $sCmd, "_LastLogged", 5) ; find last logged user and, when ready, send result to _LastLogged() Else ; if someone is logged then write username to column 6 Local $aUser = StringSplit($aParameters[1], "=", 2) ; 2 = $STR_NOCOUNT _GUICtrlListView_AddSubItem($hGrid, $ahGrid[$aParameters[0]], $aUser[UBound($aUser) - 1], 6) ; column 6 EndIf EndFunc ;==>_LoggedOn Func _LastLogged($aParameters) _GUICtrlListView_AddSubItem($hGrid, $ahGrid[$aParameters[0]], $aParameters[1], 5) ; column 5 EndFunc ;==>_LastLogged Func WMIDateStringToDate($dtmDate) ; thanks to @kylomas ; https://www.autoitscript.com/forum/topic/169252-wmi-password-age-issue/?do=findComment&comment=1236082 ; reformat date to mm/dd/yyyy hh:mm:ss and zero fill single digit values Return StringRegExpReplace(StringRegExpReplace($dtmDate, '(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}).*', '$2/$3/$1 $4:$5:$6'), '(?<!\d)(\d/)', '0$1') EndFunc ;==>WMIDateStringToDate Edited January 30, 2018 by Chimp changed topic tile (added "run" Xandy, Danyfirex, philpw99 and 9 others 10 2 Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt.... Link to comment Share on other sites More sharing options...
RooperGee Posted June 8, 2018 Share Posted June 8, 2018 (edited) Loving this code you came up with! Have you made any mods to it lately? I'll be testing it out over the next week and provide feedback. Thanks! Edited June 8, 2018 by RooperGee Progress lies not in enhancing what is, but in advancing towards what will be. - Kahlil Gibran Link to comment Share on other sites More sharing options...
wuruoyu Posted April 2, 2020 Share Posted April 2, 2020 @Chimp Thank you for sharing this powerful UDF! It’s like MAGIC! Is it possible to have a function to terminate remaining tasks when required result is obtained. E.g. performing search of a user session on multiple terminal servers, how to make sure all remaining task when user is found. Also I find that it uses 100% of cpu when it’s running, are there methods to reduce or limit the cpu usage? Thanks again for your fantastic work! Link to comment Share on other sites More sharing options...
Gianni Posted April 5, 2020 Author Share Posted April 5, 2020 Hi @wuruoyu and thanks for your kind comment . I'm glad you found this script useful. When I wrote this udf I had in mind to add a way to limit the number of tasks running but, since I didn't have an urgent need, I haven't done it yet. Now I think it can be a good opportunity to implement the helpful features you have proposed. Here is a new version of the udf, which maintains full compatibility with the previous one, but which adds two functions:_TaskLimit(): This function can set a limit to the number of tasks that can be spawn simultaneously. You can use it at the beginning of the script to estabilsh the number of tasks that will be run simultaneusly, or you can also use it at any time during the script execution, so to reduce or increase the number of runned tasks on the fly. You could start with a lower number and increase it by steps, checking the cpu load in the while so to be able to calibrate it. Note: you can still freely send all tasks you need also if the imposed limit is reached, the incoming requests are queued and their run is managed automatically so to stay below the max allowed._TaskRevokeAll(): This function allows to kill all running tasks and flush those queued still waiting to be run. I hope that this new functions can be useful and hopefully free of bugs. The example is nearly the same as the previous (sorry for the lazyness), the only differeces are, I've increased the number of tasks run; at the beginning (see line 14 of the script) I set the number of allowed tasks to only 1 _TaskLimit(1) so to emphatizate the difference in speed when, after 7 seconds of slow running, I will set the allowed MultiTasks to 20 (see line 68 of the scipt). You will see that is like if you insert the "turbo". Any bug report and/or improvements are welcome. Have fun MultiTask_v2.au3 expandcollapse popup#include-once ; #include "Array.au3" ; #INDEX# ======================================================================================================================= ; Title .........: Multi Task Management (tasks spawned by the user via the _TaskRun function of this udf) ; AutoIt Version : ; Language ......: ; Description ...: Functions that assists to execute and manage user's spawned tasks. ; Version 2 (beta) - includes 2 new functions: ; _TaskLimit This function can set a limit to the number of tasks that can be spawn simultaneously. ; You can still freely call _TaskRun() without limits, the only difference with a limit setted is ; that requests exceeding the imposed limit are queued and ran gradually while other running tasks ; ends so to not overstep the TaskLimit. ; _TaskRevokeAll This function allows to kill all running tasks and flush those queued still waiting to be run ; ; Author(s) .....: Chimp (Gianni Addiego) ; =============================================================================================================================== ; #CURRENT# ===================================================================================================================== ; _TaskLimit This function can set a limit to the number of tasks that can be run simultaneously ; _TaskRun Run a new task and stacks its references for later use ; _TasksCheckStatus Check the status of all running tasks and call the CallBack function when a task completed ; _TaskRevokeAll This function allows to kill all running tasks and flush those queued still waiting to be run ; =============================================================================================================================== ; #INTERNAL_USE_ONLY#============================================================================================================ ; __FIFO a simple implementation of a FIFO stack ; __Dequeue run a queued task (only if free slots are available) ; __TaskRemove frees task's stacked references ; __NOP No OPeration function ; =============================================================================================================================== Global $aTasks[1][7] = [[0]] ; Create and initialize the task reference stack to 0 ; #FUNCTION# ==================================================================================================================== ; Name ..........: _TaskRun ; Description ...: Runs a new task as specified in $sCommand ; Syntax ........: _TaskRun($vIndex, $sCommand[, $sParser = "__NOP"[, $vTimeout = False]]) ; Parameters ....: $vIndex - A variant value passed by the caller as a reference to this task. ; this value will be returned to the callback function along with results. ; ; $sCommand - A string value containing the full command to be executed ; ; $sParser - [optional] The name of the function to be called at the end of this task. ; Default is __NOP that is a "do nothing" function. ; P.S. ; the $aParser function will be called by the _TasksCheckStatus() function when ; this task will finish to run: ; an 1D array with 4 elements will be passed to the called function: ; element [0] the caller's index reference of this task ; element [1] the StdOut result of this task ; element [2] the StdErr result of this task ; element [3] the time spent by this task ; ; $vTimeout - [optional] A variant value. Default is False. ; if specified is the number of seconds for the Timeout. ; After this amount of seconds, if this task is still running, it is killed ; ; Return values .: the total number of stacked tasks ; Author ........: Chimp (Gianni Addiego) ; Modified ......: ; Remarks .......: ; Related .......: ; Link ..........: ; Example .......: ; =============================================================================================================================== Func _TaskRun($vIndex, $sCommand, $sParser = "__NOP", $vTimeout = False) ; ; Stack structure: ; ; +-----+ ; 0 | nr. | <-- [0][0] number of running tasks ; +-----+-----+-----+-----+-----+-----+-----+ ; 1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | ; +-----+-----+-----+-----+-----+-----+-----+ ; n | * | * | * | * | * | * | * | ; | | | | | | | ; | | | | | | [n][6] setted with the TimerInit() value at the start of this task. ; | | | | | | ; | | | | | [n][5] contains the required Timeout in seconds (Default is set to False = no timeout) ; | | | | | ; | | | | [n][4] The CallBack function name that will receive the results of this task ; | | | | ; | | | [n][3] the error message returned by the StdErr stream of this task ; | | | ; | | [n][2] the result of the command returned by the StdOut stream of this task ; | | ; | [n][1] the reference of this task passed by the user (will be passed back to the caller along with results) ; | ; [n][0] the PID of this running task ; If _TaskLimit() And ($aTasks[0][0] = _TaskLimit()) Then ; queue this request Local $aPush[4] = [$vIndex, $sCommand, $sParser, $vTimeout] ; __FIFO($aPush) Return SetError(0, __FIFO($aPush), $aTasks[0][0]) EndIf ; --------------------------------------------------------------------------------------------------------------- ReDim $aTasks[$aTasks[0][0] + 2][7] ; add a new element to the stack $aTasks[0][0] += 1 ; add 1 to the number of running tasks ; Run the passed command with the I/O streams redirected $aTasks[$aTasks[0][0]][0] = Run($sCommand, "", @SW_HIDE, 6) ; 6 -> $STDOUT_CHILD (0x2) + $STDERR_CHILD (0x4) ; store references of this task to the stack $aTasks[$aTasks[0][0]][1] = $vIndex $aTasks[$aTasks[0][0]][4] = $sParser $aTasks[$aTasks[0][0]][5] = $vTimeout ; can be False or the number of seconds for the timeout of this task $aTasks[$aTasks[0][0]][6] = TimerInit() ; store the TimerInit() value for this task Return $aTasks[0][0] ; return the total number of running tasks ; --------------------------------------------------------------------------------------------------------------- EndFunc ;==>_TaskRun ; #FUNCTION# ==================================================================================================================== ; Name ..........: _TasksCheckStatus ; Description ...: Scans the status of all active tasks and checks if some task has finished its job ; This function should be called as aften as possible ; Syntax ........: _TaskCheckStatus() ; Parameters ....: None ; Return values .: number of still running tasks; * see remarks ; Author ........: Chimp (Gianni Addiego) ; Modified ......: ; Remarks .......: When a task finish, it is removed from the stack and results are passed to the callback function. ; An 1D array with 4 elements will be passed to the called function: ; element [0] the caller's index reference of this task ; element [1] the StdOut result of this task ; element [2] the StdErr result of this task ; element [3] the time spent by this task (approximative time) ; is an approximate datum because it also includes the delay ; added by the main loop before calling _TasksCheckStatus() ; Related .......: ; Link ..........: ; Example .......: ; =============================================================================================================================== Func _TasksCheckStatus() ; first check if there are tasks waiting to be executed. __Dequeue() If $aTasks[0][0] = 0 Then Return 0 ; if no tasks then return Local $bEndTask ; will be setted to True if the checked task has finished or is killed by timeout Local $iPointer = 1 ; start checking from first task in the stack Local $aResults[4] ; this Array will be loaded with the results of the task Local $sCallBack ; the name of the function to call when a task ends its job Local $aArgs[2] = ["CallArgArray", ""] ; "special" array will be loaded with parameters for the CallBack function While $iPointer <= $aTasks[0][0] $bEndTask = False $aTasks[$iPointer][2] &= StdoutRead($aTasks[$iPointer][0]) ; read and store the StdOut stream $aTasks[$iPointer][3] &= StderrRead($aTasks[$iPointer][0]) ; read and store the StdErr stream ; If @error Then ; if there is an @error is because this task has finished its job If Not ProcessExists($aTasks[$iPointer][0]) Then ; if this task has finished its job $bEndTask = True ; flag the end of the work ElseIf $aTasks[$iPointer][5] <> False Then ; if task is still running see if a Timeout check was rquired and if so if is been reached If Int(TimerDiff($aTasks[$iPointer][6]) / 1000) >= $aTasks[$iPointer][5] Then ; timeout reached or exceeded!! $aTasks[$iPointer][3] = "@error Timeout " & $aTasks[$iPointer][3] ; insert an error message at the beginning of the StdErr stream StdioClose($aTasks[$iPointer][0]) ; close I/O streams ProcessClose($aTasks[$iPointer][0]) ; kill this process $bEndTask = True ; flag the end of this task EndIf EndIf If $bEndTask Then ; if this task has finished, get its results and send to the CallBack function $aResults[0] = $aTasks[$iPointer][1] ; ............. Index (the caller reference) $aResults[1] = $aTasks[$iPointer][2] ; ............. the StdOut generated by this task $aResults[2] = $aTasks[$iPointer][3] ; ............. the StdErr generated by this task $aResults[3] = TimerDiff($aTasks[$iPointer][6]) ; .. time spent by this task $sCallBack = $aTasks[$iPointer][4] ; the name of the function to be called $aArgs[1] = $aResults ; second element of the "CallArgArray" special array contains the $aResults array ; loaded with the parameters to be send to the CallBack function. (array in array) ; (the CallBack function will receive only the 1D 4 elements array $aResults) __TaskRemove($iPointer) ; remove references of this task from the stack ; call the CallBack function and pass the results of this task. ; (CallBack function should return as soon as possible because it stops the CheckStatus for the other tasks) ; Call($sCallBack, $aArgs) ; Call CallBack function --->---+ ; | ; <--- and return here ---------------------------------+ EndIf $iPointer += 1 ; check next task WEnd ; check again if there are tasks waiting to be executed. __Dequeue() Return $aTasks[0][0] EndFunc ;==>_TasksCheckStatus Func _TaskLimit($vLimit = Default) Local Static $iLoadLimit = 0 If IsKeyword($vLimit) Then Return $iLoadLimit $iLoadLimit = Abs($vLimit) EndFunc ;==>_TaskLimit Func __Dequeue() ; check if there are tasks waiting to be executed. ; if so, pull the queued tasks from the FIFO stack and run it If _TaskLimit() And __FIFO('', True) And ($aTasks[0][0] < _TaskLimit()) Then Local $aPull ; [4] = [$vIndex, $sCommand, $sParser, $vTimeout] While __FIFO('', True) And ($aTasks[0][0] < _TaskLimit()) $aPull = __FIFO() ; pull next queued task _TaskRun($aPull[0], $aPull[1], $aPull[2], $aPull[3]) ; and run it ; ConsoleWrite('*') ; Debug purpose WEnd ; ConsoleWrite(@CRLF & @TAB & $aTasks[0][0] & @TAB & _TaskLimit() & @CRLF) ; Debug purpose EndIf EndFunc ;==>__Dequeue ; #FUNCTION# ==================================================================================================================== ; Name ..........: __FIFO ; Description ...: A simple implementation of a FIFO stack (can be used to pull or push data from/to the stack) ; ; Syntax ........: _FIFO([$vItem = ''[, $vServiceCall = False]]) ; Parameters ....: $vItem - [optional] A variant value. Default is ''. ; this parameter can be empty or filled with data and behaves like: ; - if empty: the function pulls and return data from the stack ; - if filled: the function push that data to te stack ; $vServiceCall - [optional] A KeyWord or a value. Default is False. ; this parameter allows to perform some internal "methods" ; 'Default' or 1 : Reindex the Key Names starting from 1 (internal use) ; 'Null' or 2 : Remove all items from the stack ; 'True' or 3 : Return the number of elements stored in the stack (returns 0 if stack is empty) ; When this parameter is used the previous parameter $vItem is simply ignored. ; ; Return values .: - when the function is used to Push data: ; returns nothing and set @extended with the total number of elements in the stack ; - when the function is used to Pull data: ; returns the pulled data and set @extended with the number of elements still in the stack. ; If you try to pull data from an empty stack you get an empty string and @error is set to 1 ; ; For each function call the @extended flag is set with the number of items still in the stack ; Author ........: Chimp (Gianni Addiego) ; Modified ......: ; Remarks .......: ; Related .......: ; Link ..........: ; Example .......: ; =============================================================================================================================== Func __FIFO($vItem = '', $vServiceCall = False) Static Local $oFIFOStack = ObjCreate("Scripting.Dictionary") Static Local $iNDX = 1, $KEYWORD_DEFAULT = 1, $KEYWORD_NULL = 2, $KEYWORD_True = 3 Local $vPull = '' If $vServiceCall Or IsKeyword($vServiceCall) Then ; check second parameter Local $iService = IsKeyword($vServiceCall) ? IsKeyword($vServiceCall) : $vServiceCall ; Default = 1, Null = 2 If String($vServiceCall) = "True" Then $iService = $KEYWORD_True Switch $iService Case $KEYWORD_DEFAULT ; 1 or 'Default' Keyword was passed ; we use the Default Keyword to "normalize" all keys index starting from 1 If $oFIFOStack.Count Then Local $aKeys = $oFIFOStack.keys For $i = 0 To UBound($aKeys) - 1 $oFIFOStack.key($aKeys[$i]) = $i + 1 Next $iNDX = 1 EndIf Return SetExtended($oFIFOStack.Count, '') Case $KEYWORD_NULL ; 2 or 'Null' Keyword was passed WARNING: the stack is being emptied ; we use the Null KeyWord or value 2 to remove all items from the stack $oFIFOStack.RemoveAll Return SetExtended($oFIFOStack.Count, '') Case $KEYWORD_True ; 3 or 'True' Keyword was passed just return items count Return SetExtended($oFIFOStack.Count, $oFIFOStack.Count) Case Else ; Return SetError(0, $oFIFOStack.Count, $oFIFOStack.Count) Return SetError(1, $oFIFOStack.Count) EndSwitch EndIf If $vItem = '' Then ; no Item to stack, just pull from stack then If $oFIFOStack.Count Then ; ensure stack is not empty $vPull = $oFIFOStack.Item($iNDX) ; get the first item still in stack $oFIFOStack.Remove($iNDX) ; remove item from stack $iNDX += 1 ; raise the bottom index Return SetError(0, $oFIFOStack.Count, $vPull) ; return item to caller Else Return SetError(1, 0, '') EndIf Else $oFIFOStack.ADD($oFIFOStack.Count + $iNDX, $vItem) ; Add item SetExtended($oFIFOStack.Count) EndIf EndFunc ;==>__FIFO ; Internal use. Removes all task (running and queued) and relative references from the stack Func _TaskRevokeAll() ; a countermand on all tasks (running and queued) ; If $aTasks[0][0] = 0 Then Return 0 ; if no tasks then return Local $iPid For $i = $aTasks[0][0] To 1 Step -1 $iPid = $aTasks[$i][0] __TaskRemove($i) ; remove references of this task from the stack ProcessClose($iPid) ; kill this process Next __FIFO('', Null) ; empty the stack EndFunc ;==>_TaskRevokeAll ; Internal use. Remove the task references from the stack Func __TaskRemove($iElement) #cs If $iElement > $aTasks[0][0] Or $iElement < 1 Then Return StdioClose($aTasks[$iElement][0]) _ArrayDelete($aTasks, $iElement) #ce ; - new -------------------------------------------------- ; remove element without the _Array* udf If $iElement > 0 And $iElement <= $aTasks[0][0] Then StdioClose($aTasks[$iElement][0]) If $aTasks[0][0] > 1 Then For $i = 0 To UBound($aTasks, 2) - 1 $aTasks[$iElement][$i] = $aTasks[$aTasks[0][0]][$i] Next EndIf Else Return SetError(1) ; queue is empty or the required element is out of bound EndIf $aTasks[0][0] -= 1 ReDim $aTasks[$aTasks[0][0] + 1][UBound($aTasks, 2)] Return $aTasks[0][0] ; returns the number of tasks still running EndFunc ;==>__TaskRemove ; Internal use. An empty function Func __NOP($aDummy) ; NOP (No OPeration) EndFunc ;==>__NOP Example_v2 expandcollapse popup; #RequireAdmin #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <GuiListView.au3> #include '.\MultiTask_v2.au3' ; ; IMPORTANT following array should be populated with many real HostNames ; here are just nonsense items used as placeholders Global $aIPList[] = [@ComputerName, @IPAddress1, @ComputerName, @ComputerName, 'InexistentClient', @ComputerName, _ @ComputerName, @IPAddress1, @ComputerName, @ComputerName, 'InexistentClient', @ComputerName] Global $hGrid ; The ListView Handle Global $ahGrid[1] ; An array to keep handles of any listview row _TaskLimit(1) Example() MsgBox(0, "Debug:", "Done" & @CRLF & "Hit OK to exit") Func Example() Local $Form1 = GUICreate("Clients status", 760, 400) ; ; Create the ListView $hGrid = GUICtrlCreateListView("HostName|IP|Info|Last reboot|CPU|Last logon|Current logon", 0, 0, 760, 400) _GUICtrlListView_SetColumnWidth($hGrid, 0, 140) ; HostName _GUICtrlListView_SetColumnWidth($hGrid, 1, 100) ; IP _GUICtrlListView_SetColumnWidth($hGrid, 2, 80) ; Ping info ms or Off or Unknown or Timeout) _GUICtrlListView_SetColumnWidth($hGrid, 3, 120) ; Last Reboot _GUICtrlListView_SetColumnWidth($hGrid, 4, 40) ; cpu load ; last 2 columns a mutually exclusive. If there is a user logged it's shown on the last column ; if there is not a user logged then the last user that was logged is shown on column 4 instead _GUICtrlListView_SetColumnWidth($hGrid, 5, 140) ; Last logged UserId (if nobody is logged now) _GUICtrlListView_SetColumnWidth($hGrid, 6, 140) ; Currently logged UserId _GUICtrlListView_SetExtendedListViewStyle($hGrid, BitOR($LVS_EX_GRIDLINES, $LVS_EX_FULLROWSELECT)) ; show grid; select whole row ; GUISetState(@SW_SHOW) ; following line is needed if you have to refill the listview ; _GUICtrlListView_DeleteAllItems(GUICtrlGetHandle($hGrid)) ; empty the listview ReDim $ahGrid[UBound($aIPList)] ; this loop will run all needed tasks at once. ; The results of the tasks will be managed by the callback functions (nearly) autonomously and asynchronously For $i = 0 To UBound($aIPList) - 1 $ahGrid[$i] = _GUICtrlListView_AddItem($hGrid, "") ; create a listview row ; ; ... for each client ... ; ; spawn ping commands. result will be send to the _PingParse() function _TaskRun($i, "Ping -a -n 1 -4 " & $aIPList[$i], "_PingParse") ; spawn commands to recall LoggedOn User. Result will be send to the _LoggedOn() function _TaskRun($i, "wmic /node:" & $aIPList[$i] & " computersystem get username /value", "_LoggedOn", 5) ; 5 = timeout in 5 seconds ; spawn commands to recall Last reboot time. result will be send to the _LastReboot() function _TaskRun($i, "wmic /node:" & $aIPList[$i] & " os get lastbootuptime /value", "_LastReboot", 5) ; spawn commands to recall % of CPU load. result will be send to the _CPU_load() function _TaskRun($i, "wmic /node:" & $aIPList[$i] & " cpu get loadpercentage /value", "_CPU_load", 7) Next ; now, while all tasks are running ; we could perform other activities in the meantime ; or we can wait the end of all the tasks Local $Timer = TimerInit() Do Sleep(250) ; you could perform other jobs here while waiting for the tasks to finish If TimerDiff($Timer) > 7000 Then _TaskLimit(20) ; after 7 seconds increase the allowed tasks to 20 (insert the turbo) Until Not _TasksCheckStatus() ; <-- this function performs management of running tasks ; and should be called as often as possible EndFunc ;==>Example ; below are CallBack functions ; #FUNCTION# ==================================================================================================================== ; Name ..........: _PingParse (a callback function) ; Description ...: this function analize the output of a ping command and "extract" needed infos ; it fills columns 0 1 and 2 of the list view ($aTarget[0] is the line number of the listview to be filled) ; Syntax ........: _PingParse($aTarget[, $bDomain = True]) ; Parameters ....: $sTarget - An array with Ping results passed by the MultiTasks as soon as any ping task ends ; the passed array contains following data: ; $aTarget[0] Index for this task ; $aTarget[1] StdOut from ping (the whole output of the ping) ; $aTarget[2] StdErr from ping ; $aTarget[3] Time spent by this task to complete (is NOT the ping roundtrip time) ; $bDomain - [optional] A binary value. Default is True. (keep domain info in host name) ; ; Return values .: None. It Fills Listview columns 0, 1 and 2 ; column 0 : resolved HostName or "" ; column 1 : IP address or "" (this can contain last known IP even if now is offline) ; column 2 : roundtrip time or "Unknown" or "timeout" or "Off" ; ; Author ........: Chimp ; =============================================================================================================================== Func _PingParse($aTarget, $bDomain = True) ; $aTarget contains 4 elements: [0] Index, [1] StdOut, [2] StdErr, [3] time spent by this task Local $sOutput = $aTarget[1] ; stdout Local $0, $1, $2, $3, $aMs ; Local $iAnswer = -1, $iName = -1 Local $aResult[3] = ["", "", ""] ; [0]ms, [1]HostName, [2]IP $aMs = StringRegExp($sOutput, "([0-9]*)ms", 3) If Not @error Then ; Ping replayed $aResult[0] = $aMs[UBound($aMs) - 1] ; average ms Else ; $aResult[0] = "off" EndIf $0 = StringInStr($sOutput, "Ping") $1 = StringInStr($sOutput, "[") ; HostName decoded? If $1 Then ; HostName decoded $2 = StringInStr($sOutput, "]") $3 = StringInStr($sOutput, " ", 0, -2, $1) $aResult[1] = StringMid($sOutput, $3 + 1, $1 - $3 - 1) ; HostName $aResult[2] = StringMid($sOutput, $1 + 1, $2 - $1 - 1) ; IP Else If $0 Then ; pinging an IP address? ; $aResult[1] = "" ; no HostName Local $aFindIP = StringRegExp($sOutput, "\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", 3) If Not @error Then $aResult[2] = $aFindIP[0] Else ; unknown HostName $aResult[0] = "Unknown" $aResult[1] = $aIPList[$aTarget[0]] ; retrieve HostName from the $aIPList array EndIf EndIf If $bDomain = False Then Local $aSplit = StringSplit($aResult[1], ".", 2) ; 2 = $STR_NOCOUNT $aResult[1] = $aSplit[0] ; romove .domain part from the HostName EndIf If StringLeft($aTarget[2], 14) = "@error Timeout" Then $aResult[0] = "Timeout" ; Now that we have the infos, we compile related cells in ListView ; grid row-handle data column _GUICtrlListView_SetItemText($hGrid, $ahGrid[$aTarget[0]], $aResult[1], 0) ; first column "HostName" _GUICtrlListView_SetItemText($hGrid, $ahGrid[$aTarget[0]], $aResult[2], 1) ; second column "IP address" _GUICtrlListView_SetItemText($hGrid, $ahGrid[$aTarget[0]], $aResult[0], 2) ; third column "Infos about the ping" ; ConsoleWrite("Debug: " & "-> " & $aResult[0] & @TAB & $aResult[1] & @TAB & $aResult[2] & @CRLF) EndFunc ;==>_PingParse Func _LastReboot($aParameters) ; Last reboot DateTime $aParameters[1] = StringStripWS($aParameters[1], 8) Local $equal = StringInStr($aParameters[1], "=") If $equal Then _GUICtrlListView_AddSubItem($hGrid, $ahGrid[$aParameters[0]], WMIDateStringToDate(StringMid($aParameters[1], $equal + 1)), 3) ; column 3 EndIf EndFunc ;==>_LastReboot Func _CPU_load($aParameters) ; % of CPU load $aParameters[1] = StringStripWS($aParameters[1], 8) Local $equal = StringInStr($aParameters[1], "=") If $equal Then _GUICtrlListView_AddSubItem($hGrid, $ahGrid[$aParameters[0]], StringMid($aParameters[1], $equal + 1), 4) ; column 4 EndIf EndFunc ;==>_CPU_load Func _LoggedOn($aParameters) ; User now logged $aParameters[1] = StringStripWS($aParameters[1], 8) ; if none is logged, then find the user that was last logged (also using _TaskRun) If $aParameters[1] = "" Or $aParameters[1] = "UserName=" Then ; following syntax is by @iamtheky (thanks) ; https://www.autoitscript.com/forum/topic/189845-regexp-pattern-in-findstr-dos-command/?do=findComment&comment=1363106 Local $sCmd = 'cmd /c FOR /F "usebackq skip=2 tokens=1-3" %A IN (`REG QUERY ' & '"\\' & $aIPList[$aParameters[0]] & _ '\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI" /REG:64 /v LastLoggedOnUser 2^>nul`) Do @echo %C' _TaskRun($aParameters[0], $sCmd, "_LastLogged", 5) ; find last logged user and, when ready, send result to _LastLogged() Else ; if someone is logged then write username to column 6 Local $aUser = StringSplit($aParameters[1], "=", 2) ; 2 = $STR_NOCOUNT _GUICtrlListView_AddSubItem($hGrid, $ahGrid[$aParameters[0]], $aUser[UBound($aUser) - 1], 6) ; column 6 EndIf EndFunc ;==>_LoggedOn Func _LastLogged($aParameters) _GUICtrlListView_AddSubItem($hGrid, $ahGrid[$aParameters[0]], $aParameters[1], 5) ; column 5 EndFunc ;==>_LastLogged Func WMIDateStringToDate($dtmDate) ; thanks to @kylomas ; https://www.autoitscript.com/forum/topic/169252-wmi-password-age-issue/?do=findComment&comment=1236082 ; reformat date to mm/dd/yyyy hh:mm:ss and zero fill single digit values Return StringRegExpReplace(StringRegExpReplace($dtmDate, '(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}).*', '$2/$3/$1 $4:$5:$6'), '(?<!\d)(\d/)', '0$1') EndFunc ;==>WMIDateStringToDate taurus905 1 Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt.... Link to comment Share on other sites More sharing options...
wuruoyu Posted April 5, 2020 Share Posted April 5, 2020 @Chimp Amazing work! Thank you for the update! I will test it out. My honest opinion, this UDF is so underrated, I hope more people will discover this and can benefit from it. Gianni 1 Link to comment Share on other sites More sharing options...
wuruoyu Posted April 5, 2020 Share Posted April 5, 2020 (edited) Hi @Chimp forgot to ask in my previous post, the "Callback" function Call($sCallBack, $aArgs), with #Au3Stripper_Parameters=/pe /rm /so (also using #Au3Stripper_Ignore_Funcs=$sCallBack), the script compiled successfully, but the function "_TaskRun" doesn't do seem to do anything. No big deal if there is no workaround. Thanks again! Edited April 5, 2020 by wuruoyu Link to comment Share on other sites More sharing options...
Developers Jos Posted April 6, 2020 Developers Share Posted April 6, 2020 7 hours ago, wuruoyu said: but the function "_TaskRun" doesn't do seem to do anything. Pretty sure you got a warning when running au3stripper about this might happen! ...right? Jos SciTE4AutoIt3 Full installer Download page - Beta files Read before posting How to post scriptsource Forum etiquette Forum Rules Live for the present, Dream of the future, Learn from the past. Link to comment Share on other sites More sharing options...
wuruoyu Posted April 6, 2020 Share Posted April 6, 2020 @Jos I am not getting warning regarding _TaskRun function. I do get a warning about “unresolvable func - $sCallBack” if I don’t use #Au3Stripper_Ignore_Funcs=$sCallBack https://www.autoitscript.com/autoit3/scite/docs/SciTE4AutoIt3/Au3Stripper.html The Au3stripper manual did state that call() is one of the affected functions. Just wondering if there are other replaceable methods/functions for call(). Link to comment Share on other sites More sharing options...
Developers Jos Posted April 6, 2020 Developers Share Posted April 6, 2020 I understand that the warning/error is about the Call statement as au3stripper has no way of knowing what the value of $sCallBack is. The #Au3Stripper_Ignore_Funcs= statement should contain the target Func name, not a variable, so in this case this might help avoiding issues: #Au3Stripper_Ignore_Funcs= _TaskRun Jos SciTE4AutoIt3 Full installer Download page - Beta files Read before posting How to post scriptsource Forum etiquette Forum Rules Live for the present, Dream of the future, Learn from the past. Link to comment Share on other sites More sharing options...
marko001 Posted December 29, 2021 Share Posted December 29, 2021 Hi @Chimp and thanks for sharing this interesting UDF. I have a question, maybe you can support me in implementing this .au3 in my code: I developed a tool to real-time monitor certain crypto assets, as in the image. Right now I simply cycle For $i = 0 To $MaxPairs - 1 _RealTimePublicValue($s_TradingPlatform, $a_Pairs[$i][0], $a_Pairs[$i][1], $i) Next and Func _RealTimePublicValue($s_TradingPlatform, $Crypto, $LocalValue, $index) ; OHLC If $s_TradingPlatform = "Crypto.com" Then Local $pstring = $Crypto & "_" & $LocalValue Local $sResult = CryptoQueryPublic("get-ticker", "instrument_name=" & $pstring) ;Ticker $ask = _StringBetween($sResult, '"a":', ',') If IsArray($ask) Then $last = _StringBetween($sResult, '"a":', ',') $High = _StringBetween($sResult, '"h":', ',') $Low = _StringBetween($sResult, '"l":', ',') $Change = _StringBetween($sResult, '"c":', '}') Else _send("Pair " & $pstring & " not allowed, change currency", $giallo) Return EndIf _WriteToGui($pstring, $last[0] - $Change[0], $High[0], $Low[0], $last[0], $index) ElseIf $s_TradingPlatform = "Coinbase Pro" Then Local $pstring = $Crypto & "-" & $LocalValue Local $sResult = CoinbaseProQueryPublic("GET", "products/" & $pstring & "/stats") ; Stats $opening = _StringBetween($sResult, '"open":"', '"') If IsArray($opening) Then ;~ $opening = _StringBetween($sResult, '"open":"', '"') $High = _StringBetween($sResult, '"high":"', '"') $Low = _StringBetween($sResult, '"low":"', '"') $last = _StringBetween($sResult, '"last":"', '"') Else _send("Pair " & $pstring & " not allowed, change currency", $giallo) Return EndIf _WriteToGui($pstring, $opening[0], $High[0], $Low[0], $last[0], $index) EndIf EndFunc ;==>_RealTimePublicValue and use _WriteToGui() to write all data into the GUI. In this way the process is done in sequence. I would like to have it done in parallel for all my assets, since I would like to perform other actions (i.e. selling or buying) and now I have to wait all the cycle to complete. This leads in being stuck waiting the cycle. And it's not immediate since the call to Exchange may cost around .2 to .5 sec each pair i look for. I would like also to add @Danyfirex to this thread, maybe he can be of support as well. Thanks Marco Link to comment Share on other sites More sharing options...
Gianni Posted December 30, 2021 Author Share Posted December 30, 2021 Hi @marko001 more or less, as far as I can see from that snippet, the delay would be caused by the CryptoQueryPublic() and/or CoinbaseProQueryPublic() functions, which have to wait for a response from a site (I assume). Therefore, to "parallelize" those "queries" you should create a standalone version of these functions so that multiple instances can be "run" without the need to wait for the returned response and "take" the responses and display them only when those are available ( doing something else in the meantime). This, long story short, is what I can say, but just from that snippet, I can't help you that much. Couldn't you post the whole list? or at least those two functions and also the _WriteToGui function? (but the whole listing would be better) Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt.... Link to comment Share on other sites More sharing options...
marko001 Posted December 30, 2021 Share Posted December 30, 2021 (edited) Hi @Chimp and thanks for reply. This is part of the While: expandcollapse popupCase $FRM_Dashboard Switch $nMsg[0] Case $SaveButton _send("Saving Cache", $azzurro) _SaveCache() Case $LoadButton _send("Loading Cache", $azzurro) _LoadCache() Case $GUI_EVENT_CLOSE ;~ FileDelete(@ScriptDir & "\Cache.txt") ExitLoop Case $B_Telegram GUISetState(@SW_SHOW, $FRM_TelegramConfig) Case $B_Return2Assets AdlibUnRegister("_MACDANALYSIS") AdlibUnRegister("_LASTVALUES") _CloseOpen($FRM_Dashboard, $FRM_Assets) GUIDelete($FRM_Dashboard) _CreateDashboardForm() Case $B_Simulation _TradeOrSimulate() Case $B_ShowBuyOptions GUISetState(@SW_DISABLE, $FRM_Dashboard) GUISetState(@SW_SHOW, $FRM_PurchaseOptions) Case $B_MonitorAll _MonitorAllPairs() Case Else For $i = 0 To 14 If $nMsg[0] = $B_Activate[$i] Then If $a_Pairs[$i][4] = 0 Then _StartMonitor($i) Else _StopMonitor($i) EndIf ElseIf $nMsg[0] = $CB_ShowGraph[$i] Then _MACDANALYSIS() ElseIf $nMsg[0] = $R_LowRisk[$i] Then _SetLowRisk($i) ElseIf $nMsg[0] = $R_MidRisk[$i] Then _SetMidRisk($i) ElseIf $nMsg[0] = $R_HighRisk[$i] Then _SetHighRisk($i) ElseIf $nMsg[0] = $R_CustomRisk[$i] Then GUISetState(@SW_DISABLE, $FRM_Dashboard) _OpenTradeSettingsGUI($i) ElseIf $nMsg[0] = $CB_BuyButton[$i] Then _ReplyBuyButtonStatus($i) EndIf Next EndSwitch where basically, during constant assets update, I can buy/sell/create graphs... This is the _WriteToGui() Func _WriteToGui($pstring, $opening, $High, $Low, $last, $index) GUISwitch($FRM_Dashboard) Local $DeltaY = 24 If $createitems Then _send("Filling OHLC values for " & $pstring, $verde) $I_Currency[$index] = GUICtrlCreateInput($pstring, 40, 32 + $DeltaY * $index, 60, 21, BitOR($ES_READONLY, $ES_RIGHT)) _SetCtrlFontAndColor(-1, 0, 0xFFFFFF, 0x161616) $I_Open[$index] = GUICtrlCreateInput(Round($opening, 3), 110, 32 + $DeltaY * $index, 57, 21, BitOR($ES_READONLY, $ES_RIGHT)) _SetCtrlFontAndColor(-1, 0, 0xFFFFFF, 0x161616) $I_High[$index] = GUICtrlCreateInput(Round($High, 3), 110 + 62, 32 + $DeltaY * $index, 57, 21, BitOR($ES_READONLY, $ES_RIGHT)) _SetCtrlFontAndColor(-1, 0, 0xFFFFFF, 0x161616) $I_Low[$index] = GUICtrlCreateInput(Round($Low, 3), 110 + 62 + 62, 32 + $DeltaY * $index, 57, 21, BitOR($ES_READONLY, $ES_RIGHT)) _SetCtrlFontAndColor(-1, 0, 0xFFFFFF, 0x161616) $I_Current[$index] = GUICtrlCreateInput(Round($last, 3), 110 + 62 + 62 + 62, 32 + $DeltaY * $index, 57, 21, BitOR($ES_READONLY, $ES_RIGHT)) _SetCtrlFontAndColor(-1, 0, 0xFFFFFF, 0x161616) $I_DailyPerc[$index] = GUICtrlCreateInput(Round((($last / $opening) - 1) * 100, 2), 110 + 62 + 62 + 62 + 62, 32 + $DeltaY * $index, 57, 21, BitOR($ES_READONLY, $ES_CENTER)) _SetCtrlFontAndColor(-1, 0, 0xFFFFFF, 0x161616) Else GUICtrlSetData($I_Open[$index], Round($opening, 3)) GUICtrlSetData($I_High[$index], Round($High, 3)) GUICtrlSetData($I_Low[$index], Round($Low, 3)) GUICtrlSetData($I_Current[$index], Round($last, 3)) GUICtrlSetData($I_DailyPerc[$index], Round((($last / $opening) - 1) * 100, 2)) EndIf If $last >= $I_CurrentTemp[$index] Then GUICtrlSetColor($I_Current[$index], $verde) Else GUICtrlSetColor($I_Current[$index], 0xED7D31) ; RED EndIf If GUICtrlRead($I_DailyPerc[$index]) >= 0 Then GUICtrlSetColor($I_DailyPerc[$index], $verde) Else GUICtrlSetColor($I_DailyPerc[$index], 0xED7D31) ; RED EndIf $I_CurrentTemp[$index] = $last EndFunc ;==>_WriteToGui The whole GUI has more data than these, they are filled by other funcs, but they are just calc: So, basically, I have different functions that should run in parallel: 1) Data retrieving from Exchange (1 to n where n is the number of assets I have on GUI) - every 5 secs 2) Candlestick retrieving (again for every asset in GUI) - every 5 mins Based on these the system will decide (based on settings) to buy/sell/stay More, the checkboxes on the left enable a graph for the checked pair. If enabled it load, for every enabled graph, candlesticks for 100 tics and last values and also this should run in parallel. That's it. It would be nice if I could have, in parallel N tasks running to call APIs to retrieve data for the N pairs (or 1 doing the cycle) N tasks running to monitor values of the N pairs and perform actions (or 1 doing the cycle) M tasks running to draw the graphs, the checked ones Availability to click any button on the Dashboard Thanks and let me know if you need further details, Marco Edited December 30, 2021 by marko001 Link to comment Share on other sites More sharing options...
Gianni Posted December 30, 2021 Author Share Posted December 30, 2021 Hi @marko001 , seems an interesting program..... well, ok for the _WriteToGui() function, but I don't see the CryptoQueryPublic() and CoinbaseProQueryPublic() functions which I think are the ones to run in multitask..... however, although it seems to me that this program lends itself well to exploiting the multitask features offered by my udf, I believe that a small rewriting of some parts or at least a reorganization of the overall flow is necessary to allow the execution of the "multitasking" and relative parsing of the returned data .... it is not a trivial change I think ... and for sure an overview of the whole script is needed. You can send it to me if you want, or maybe (better yet) post it here if you prefer, so I or maybe others who are interested too can hopefully try to edit and adapt it. Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt.... Link to comment Share on other sites More sharing options...
marko001 Posted December 30, 2021 Share Posted December 30, 2021 @Chimp I wrote you Link to comment Share on other sites More sharing options...
marko001 Posted November 19, 2022 Share Posted November 19, 2022 (edited) Hi Gianni I have a problem with _Taskrun() This call, if file is compiled, is not executed. On the contrary, if I run it from .au3, it works smoothly. Can you help me on this? The function() I use is the following: Func _CHF() If $go Then If @Compiled Then If $EndedTaskCHF Then _send("CHF Cycle N. " & $RunsCHF, $verdechiaro) _TaskRun("CHF", @ScriptDir & '\Kraken_ValueGatherer.exe CHF 10', "_ParserCHF") $EndedTaskCHF = False EndIf Else If $EndedTaskCHF Then _send("CHF Cycle N. " & $RunsCHF, $verdechiaro) _TaskRun("CHF", @AutoItExe & " /AutoIt3ExecuteScript " & 'Kraken_ValueGatherer.au3 CHF 10', "_ParserCHF") $EndedTaskCHF = False EndIf EndIf Else _send("Remote analysis stopped", $giallo) EndIf _TasksCheckStatus() EndFunc ;==>_CHF questo è il parser Func _ParserCHF($aParameters) If $aParameters[2] <> "" Then ; an error occurred _send("Error: " & $aParameters[2], $rosso) EndIf _send($aParameters[0] & " analysis completed in " & $aParameters[1] & "''", $verdechiaro) $EndedTaskCHF = True $RunsCHF += 1 EndFunc ;==>_ParserCHF and this is the program i call expandcollapse popup#Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_Icon=..\BITBOT\Resources\15660_wheel_gear_system_icon.ico #AutoIt3Wrapper_Outfile=Kraken_ValueGatherer.exe #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** #include <File.au3> #include <MsgBoxConstants.au3> #include <Date.au3> #include <String.au3> #include <ButtonConstants.au3> #include <EditConstants.au3> #include <GUIConstantsEx.au3> #include <StaticConstants.au3> #include <WindowsConstants.au3> #include <GuiRichEdit.au3> #include <..\_________OWL Resources\PersonalBBAPI.au3> Global $oMyError = ObjEvent("AutoIt.Error", "_MyErrFunc2") Global $aPublicAssets[0] Global $aValues[0][14] ;x,0 - Asset ;x,1 - Last Value ;x,2-10 - Close Values ;x,11 - Condition ;x,12 - 24h Diff % ;x,13 - Asset Name Func _MyErrFunc2() ConsoleWriteError("Error: Kraken API Communication Error") ; received by main script in element[2] EndFunc ;==>_MyErrFunc Global $iDefaultExchange = "Kraken" Global $KTimer Local $t_Currency = $CmdLine[1];"USDT" Local $t_ID = $CmdLine[2]; ID on server _RetrievePublicKraken() _MACDCalculation($t_Currency, 26, 12, 9) Func _RetrievePublicKraken() $KTimer = TimerInit() $sResult = KrakenQueryPublic("AssetPairs") $a_result = _StringBetween($sResult, '"result":{', '}}') $a_resultsplit = StringSplit($a_result[0], "},", 3) For $k = 0 To UBound($a_resultsplit) - 1 ; per tutte le righe If StringInStr($a_resultsplit[$k], '"quote":"' & $t_Currency & '"') > 0 Then $found = _StringBetween($a_resultsplit[$k], '"base":"', '"') _ArrayAdd($aPublicAssets, $found[0]) EndIf Next _ArraySort($aPublicAssets) EndFunc ;==>_RetrievePublicKraken Func _MACDCalculation($LocalValue, $long, $short, $macd = 9) If $iDefaultExchange = "Kraken" Then Local $Crypto For $i = 0 to UBound($aPublicAssets)-1 $Crypto = $aPublicAssets[$i] $Asset = $Crypto & "-" & $LocalValue Local $sResult = KrakenQueryPublic("OHLC", "&pair=", $Asset, "&interval=5&since=" & _Timestamp(-12)) ;OHLC $a_resultBetween = _StringBetween($sResult, "[[", "]]") If IsArray($a_resultBetween) Then $a_resultsplit = StringSplit($a_resultBetween[0], "],[", $STR_ENTIRESPLIT) _ArrayDelete($a_resultsplit, UBound($a_resultsplit) - 1) _ArrayDelete($a_resultsplit, 0) If IsArray($a_resultsplit) And UBound($a_resultsplit)>10 Then _ArrayAdd($aValues,$Asset) $aValues[UBound($aValues)-1][13] = $Crypto For $k = 0 to 8 ;UBound($a_resultsplit)-1 To UBound($a_resultsplit)-10 Step -1 Local $j = UBound($a_resultsplit)-1 - $k $a_resultsplit[$j] = StringReplace($a_resultsplit[$j], '"', "") Local $ColSplit = StringSplit($a_resultsplit[$j], ",", 2) ;4 = close $aValues[UBound($aValues)-1][$k+2] = $ColSplit[4] Next ;Last + 24h Diff $sResult = KrakenQueryPublic("Ticker", "&pair=", $Asset) ;Ticker $last = _StringBetween($sResult, 'c":["', '",') If Not IsArray($last) Then $aValues[UBound($aValues)-1][1] = $aValues[UBound($aValues)-1][2] $aValues[UBound($aValues)-1][12] = 0 Else $opening = _StringBetween($sResult, 'o":"', '"') Local $delta = Round((($last[0] / $opening[0]) - 1) * 100, 2) $aValues[UBound($aValues)-1][1] = _TrimZeros($last[0]) $aValues[UBound($aValues)-1][12] = $delta EndIf _AddCalc3($iDefaultExchange, UBound($aValues)-1) EndIf EndIf Next $sAsset = _ArrayToString($aValues,"|",-1,-1,"|",13,13) $sStreak = _ArrayToString($aValues,"|",-1,-1,"|",11,11) $sWholeDay = _ArrayToString($aValues,"|",-1,-1,"|",12,12) _UpdateStreams($iDefaultExchange,$LocalValue,$sAsset, $sStreak, $sWholeDay,$t_ID*1000) ;~ _CW("Time AFTER update: " & Round(TimerDiff($KTimer)/1000,2)) EndIf EndFunc ;==>_MACDCalculation Func _AddCalc3($iDefaultExchange, $index) Local $TickResult = 0 If $aValues[$index][1] > $aValues[$index][2] Then $TickResult = 1 If $aValues[$index][1] < $aValues[$index][2] Then $TickResult = -1 If $TickResult = 1 Then If $aValues[$index][2] > $aValues[$index][3] Then $TickResult = 2 If $TickResult = 2 Then If $aValues[$index][3] > $aValues[$index][4] Then $TickResult = 3 If $TickResult = 3 Then If $aValues[$index][4] > $aValues[$index][5] Then $TickResult = 4 If $TickResult = 4 Then If $aValues[$index][5] > $aValues[$index][6] Then $TickResult = 5 If $TickResult = 5 Then If $aValues[$index][6] > $aValues[$index][7] Then $TickResult = 6 If $TickResult = 6 Then If $aValues[$index][7] > $aValues[$index][8] Then $TickResult = 7 If $TickResult = 7 Then If $aValues[$index][8] > $aValues[$index][9] Then $TickResult = 8 If $TickResult = 8 Then If $aValues[$index][9] > $aValues[$index][10] Then $TickResult = 9 EndIf EndIf EndIf EndIf EndIf EndIf EndIf ElseIf $TickResult = -1 Then If $aValues[$index][2] < $aValues[$index][3] Then $TickResult = -2 If $TickResult = -2 Then If $aValues[$index][3] < $aValues[$index][4] Then $TickResult = -3 If $TickResult = -3 Then If $aValues[$index][4] < $aValues[$index][5] Then $TickResult = -4 If $TickResult = -4 Then If $aValues[$index][5] < $aValues[$index][6] Then $TickResult = -5 If $TickResult = -5 Then If $aValues[$index][6] < $aValues[$index][7] Then $TickResult = -6 If $TickResult = -6 Then If $aValues[$index][7] < $aValues[$index][8] Then $TickResult = -7 If $TickResult = -7 Then If $aValues[$index][8] < $aValues[$index][9] Then $TickResult = -8 If $TickResult = -8 Then If $aValues[$index][9] < $aValues[$index][10] Then $TickResult = -9 EndIf EndIf EndIf EndIf EndIf EndIf EndIf EndIf $aValues[$index][11] = $TickResult EndFunc Func KrakenQueryPublic($sMethod, $sParameter1 = "", $sParameter2 = "", $sParameter3 = ""); &pair=, $Asset, "&interval=5&since=" & _Timestamp(-12)) ;OHLC Local $try = 0 Local $sURL = StringFormat("/%s/public/%s", 0, $sMethod) If $sParameter1 <> "" Then $aP2 = StringSplit($sParameter2,"-",2) ;~ $aP2[0] = StringTrimLeft($aP2[0],1) $sParameter2 = $aP2[0] & $aP2[1] EndIf While 1 Local $oHTTP = ObjCreate("winhttp.winhttprequest.5.1") $oHTTP.Open("POST", "https://api.kraken.com" & $sURL, False) $oHTTP.Send(Binary($sParameter1 & $sParameter2 & $sParameter3)) ;~ $oHTTP.Send(Binary($sParameters)) Local $sReceived = $oHTTP.ResponseText If StringInStr($sReceived, "Unknown asset pair") > 0 Or StringInStr($sReceived, "Invalid arguments") Then ;~ _CW("Unknown: " & $sParameter2) $try+=1 If $try = 1 Then $sParameter2 = $aP2[0] & StringTrimLeft($aP2[1], 1) If $try = 2 Then $sParameter2 = StringTrimLeft($aP2[0], 1) & $aP2[1] If $try = 3 Then $sParameter2 = StringTrimLeft($aP2[0], 1) & StringTrimLeft($aP2[1], 1) If $try > 3 Then _CW("Third error") ExitLoop EndIf Else Return $sReceived EndIf WEnd EndFunc ;==>KrakenQueryPublic Func _Epoch_encrypt($date) Local $main_split = StringSplit($date, " ") If $main_split[0] - 2 Then Return SetError(1, 0, "") ; invalid time format EndIf Local $asDatePart = StringSplit($main_split[1], "/") Local $asTimePart = StringSplit($main_split[2], ":") If $asDatePart[0] - 3 Or $asTimePart[0] - 3 Then Return SetError(1, 0, "") ; invalid time format EndIf If $asDatePart[2] < 3 Then $asDatePart[2] += 12 $asDatePart[1] -= 1 EndIf Local $i_aFactor = Int($asDatePart[1] / 100) Local $i_bFactor = Int($i_aFactor / 4) Local $i_cFactor = 2 - $i_aFactor + $i_bFactor Local $i_eFactor = Int(1461 * ($asDatePart[1] + 4716) / 4) Local $i_fFactor = Int(153 * ($asDatePart[2] + 1) / 5) Local $aDaysDiff = $i_cFactor + $asDatePart[3] + $i_eFactor + $i_fFactor - 2442112 Local $iTimeDiff = $asTimePart[1] * 3600 + $asTimePart[2] * 60 + $asTimePart[3] Return $aDaysDiff * 86400 + $iTimeDiff EndFunc ;==>_Epoch_encrypt Func _Timestamp($DeltaTime) ; _Timestamp(-12) Local $curdate = @YEAR & "/" & @MON & "/" & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC Local $NewDate = _DateAdd('h', $DeltaTime, $curdate) Return _Epoch_encrypt($NewDate) EndFunc ;==>_Timestamp Func _CW($CWText) ConsoleWrite(_NewData2() & " >" & $CWText & @CRLF) EndFunc ;==>_CW Func _NewData2() Return "[" & @HOUR & ":" & @MIN & ":" & @SEC & "]" EndFunc ;==>_NewData Func _TrimZeros($str) $str = StringRegExpReplace($str, '(\A.+\.)(.*[^0])?(0+\z)', '\1\2') $str = StringRegExpReplace($str, '(\A.+)(\.\z)', '\1') Return $str EndFunc ;==>_TrimZeros ConsoleWrite(Round(TimerDiff($KTimer)/1000,2)) ;~ _CW("Total Time for " & $t_Currency & ": " & Round(TimerDiff($KTimer)/1000,2)) _updatestreams() function is a Winhttp call with json to a .php page on my server that updates a table on Db with realtime values of selected assets. You can easily remove that function and last #include. I noticed that also removing these the program, compiled, it's not launched. Thanks in advance, Marco Edited November 20, 2022 by Jos Please use standard codeboxes for lengthy scripts Link to comment Share on other sites More sharing options...
Developers Jos Posted November 19, 2022 Developers Share Posted November 19, 2022 (edited) Parlare Inglese perfavore! Grazie. Edited November 19, 2022 by Jos Gianni 1 SciTE4AutoIt3 Full installer Download page - Beta files Read before posting How to post scriptsource Forum etiquette Forum Rules Live for the present, Dream of the future, Learn from the past. Link to comment Share on other sites More sharing options...
marko001 Posted November 19, 2022 Share Posted November 19, 2022 @Jos done! Sorry for that! Link to comment Share on other sites More sharing options...
Gianni Posted November 20, 2022 Author Share Posted November 20, 2022 Hi @marko001, I assume both scripts are compiled, i.e. both the calling script (where the _CHF() function is contained) and the 'Kraken_ValueGatherer' script which is called by the main script. Both compiled right? ... sorry for this trivial question but ... Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt.... Link to comment Share on other sites More sharing options...
marko001 Posted November 20, 2022 Share Posted November 20, 2022 Sure Gianni, I both compiled them. Link to comment Share on other sites More sharing options...
marko001 Posted November 20, 2022 Share Posted November 20, 2022 (edited) Well, by the way, there's no secret here! Here is the main script Aim of the program: for selected currencies retrieve the instant value of ALL cryptos (i.e. if I select EUR it will get all BTC-EUR, MNR-EUR, ETH-EUR....) and store them in remote DB tables. P.s. Only function Func _CHF() is updated so once this is working obviously other will work as well (they are all same calls to .exe) P.P.s. If you have a better idea on how to manage it, free to update it!! Thanks in advance, Marco BitBotRemote - Kraken.au3 Edited November 20, 2022 by marko001 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