Jump to content

Coroutine Multithreading Udf Library


neogia
 Share

Recommended Posts

Update 7/20/06: Get v1.1.0 with a bunch of important patches.

Released Version: 1.0.4

7/19/06 (1.0.4) - Fixed bug where using the substring "func" anywhere in a

line of code caused it to be truncated. Now the only

limitation is that the substrings "func" and "endfunc"

cannot be found at the beginning of a line of code.

Thanks for a very fast solution to the problem! However, it's not a fix as it breaks the code completely! Did you even check it? Your new code ONLY includes the Func() and EndFunc() of the exported function, and the entire body of code is left out! The resulting temp-file is empty of all the instructions of the exported function!

This is because you've changed the line from "If the line ISN'T the start/end of a function, then write it to the tempfile", to "If the line IS the start/end of a function, then write it to the tempfile". I've corrected your code, below. And as a token of gratitude I've spent some time creating a function for you that correctly classifies lines.

The function is called _LineIsFunction($line), and returns 0 = regular line or 1 = line is func/endfunc. It also sets @error to 1 = start of function (func xxx(args)), or 2 = end of function (endfunc), so that you can work with that value aswell if you want to. How did I do it? It uses regular expressions to 100% accurately classify *any* line. :D

Here is a sample file with very bad code quality (full of errors), to check the accuracy of my function:

Func f1($arg1,$arg2) ; start of function )<
Func f2() ; !!! func f3 blah blah
       Func f1($arg1,$arg2) ; start of function
           Func     f2() ; !!! func f3()?? blah blah
RegularLineofCodeWithFunc()
; Another Line of Code with Func f1()
Function Start?
While $RunFunction = 1 ;this is the line that caused trouble with your script
fUnc f3($RunFunction)
FUNC f4($RunFunction = 1)
How about this line? Is it a Func funcname($argument)? Or is it correctly seen as a regular line?
EndFunc
EndFunc ;==>end of f1 ;D
    EndFunc ; this one has whitespace at the start
    EndFUNC ; this one has a tab and mixed case
        EndfUnc ; this one has two tabs and mixed case
       Endfunc ;==> End of function
; Endfunc ; End of function with invalid code preceding it
$str = "EndFunc" ; This isn't the end of a function, but your code thinks it is, since you're only
; checking if Func/EndFunc is in the string, and not the actual validity of the string.
Func ; this is an invalid function
Func f5 ; this is another invalid function
Func  f6     (  ) ; this is a valid function full of spaces and tabs
    Func f7 ( ; no matching end bracketoÝ÷ Ø Ý¶¬Ëaz·¬º[Z~׫ºÈ§lºw-í¡ÉZ²ÈÉæX§y«­¢+ÙlÅum ¥¹¹¥¹½Õ¹Ñ¥½¹tÕ¹Ä ÀÌØíÉÄ°ÀÌØíÉȤìÍÑÉнչѥ½¸¤±Ðì)lÉum  ¥¹¹¥¹½Õ¹Ñ¥½¹tÕ¹È ¤ìÌÌìÌÌìÌÌìչ̱ ± )lÍum    ¥¹¹¥¹½Õ¹Ñ¥½¹t$Õ¹Ä ÀÌØíÉÄ°ÀÌØíÉȤìÍÑÉнչѥ½¸)lÑum ¥¹¹¥¹½Õ¹Ñ¥½¹t$Õ¹%È ¤ìÌÌìÌÌìÌÌìÕ¹Ì ¤üü± ± )lÕumIÕ±È1¥¹tIÕ±É1¥¹½
½]¥Ñ¡Õ¹ ¤)lÙumIÕ±È1¥¹t칽ѡÈ1¥¹½
½Ý¥Ñ Õ¹Ä ¤)lÝumIÕ±È1¥¹tչѥ½¸MÑÉÐü)láumIÕ±È1¥¹t]¡¥±ÀÌØíIչչѥ½¸ôÄíÑ¡¥Ì¥ÌÑ¡±¥¹Ñ¡ÐÕÍÑÉ½Õ±Ý¥Ñ å½ÕÈÍÉ¥ÁÐ)låum    ¥¹¹¥¹½Õ¹Ñ¥½¹tU¹Ì ÀÌØíIչչѥ½¸¤)lÄÁum  ¥¹¹¥¹½Õ¹Ñ¥½¹tU9Ð ÀÌØíIչչѥ½¸ôĤ)lÄÅumIÕ±È1¥¹t!½Ü½ÕÐÑ¡¥Ì±¥¹ü%Ì¥ÐÕ¹Õ¹¹µ ÀÌØíÉÕµ¹Ð¤ü=ȥ̥нÉÉѱä͸ÌÉձȱ¥¹ü)lÄÉum¹½Õ¹Ñ¥½¹t¹Õ¹)lÄÍum¹½Õ¹Ñ¥½¹t¹Õ¹ìôôÐí¹½Äí)lÄÑum¹½Õ¹Ñ¥½¹t¹Õ¹ìÑ¡¥Ì½¹¡ÌÝ¡¥ÑÍÁÐÑ¡ÍÑÉÐ)lÄÕum¹½Õ¹Ñ¥½¹t%¹U9ìÑ¡¥Ì½¹¡Ìѹµ¥áÍ)lÄÙum¹½Õ¹Ñ¥½¹t$%¹U¹ìÑ¡¥Ì½¹¡ÌÑݼÑ̹µ¥áÍ)lÄÝum¹½Õ¹Ñ¥½¹t$¹Õ¹ìôôÐ칽չѥ½¸)lÄáumIÕ±È1¥¹tì¹Õ¹ì¹½Õ¹Ñ¥½¸Ý¥Ñ ¥¹Ù±¥½ÁÉ¥¹¥Ð)lÄåumIÕ±È1¥¹tÀÌØíÍÑÈôÅÕ½Ðí¹Õ¹ÅÕ½ÐììQ¡¥Ì¥Í¸ÌäíÐÑ¡¹½Õ¹Ñ¥½¸°ÕÐå½ÕȽѡ¥¹­Ì¥Ð¥Ì°Í¥¹å½ÔÌäíɽ¹±ä)lÈÁumIÕ±È1¥¹tì¡­¥¹¥Õ¹½¹Õ¹¥Ì¥¸Ñ¡ÍÑÉ¥¹°¹¹½ÐÑ¡ÑÕ°Ù±¥¥Ñä½Ñ¡ÍÑÉ¥¹¸)lÈÅumIÕ±È1¥¹tÕ¹ìÑ¡¥Ì¥Ì¸¥¹Ù±¥Õ¹Ñ¥½¸)lÈÉumIÕ±È1¥¹tÕ¹ÔìÑ¡¥Ì¥Ì¹½Ñ¡È¥¹Ù±¥Õ¹Ñ¥½¸)lÈÍum  ¥¹¹¥¹½Õ¹Ñ¥½¹tÕ¹Ø$ $¤ìÑ¡¥Ì¥ÌÙ±¥Õ¹Ñ¥½¸Õ±°½ÍÁ̹ÑÌ)lÈÑumIÕ±È1¥¹t%Õ¹Ü ì¹¼µÑ¡¥¹¹É­ÐoÝ÷ Ù.®«¨µø±yÚ®¢×('¶¨¶«©Ý«©àyÛajÛhªê-!ûazX§x­ë-j»zwh}§îËb¢p
Ø^ÂÝÆ­¶¬µªí¡ûazX§{
+uú.Ö­r«iË^¯ußÛazwh¶¢YhÂ)àªê-jëh×6ElseIf StringInStr($asFuncLines[$i], "func") == 1 Or StringInStr($asFuncLines[$i], "endfunc") == 1 ThenoÝ÷ Ù8b²+0«[­æ¤±ëÞ¯+ax²+g£
''±¦í²Ø^~éÜýéÝ~éÜ)Þ±©Ýmæèw*¶ºw-Óë(ëb¢x¬¶¬zlw°[§qû§rبÚ/z¸ÊØZ¶Ø^)Þ!#wõ;azËZ®ßÞÚiû§rب©Ýû(¥êߢ¹­êk¢«Â¸­zƧwZ¶z-N­Çhæ¬~e£ºÚ"µÍ[ÙRYÝÓ[RÑ[Ý[Û ÌÍØÑ[Ó[ÖÉÌÍÚWJH[

That's all there is to it! The function will classify each line, and perform the "Then"-actions on everything but the function start/end (func/endfunc). This is just like your previous StringInStr but with 100% accuracy! :wacko:

Best Regards,

Chris H.

_LineIsFunction.zip

Edited by darkthorn
Link to comment
Share on other sites

Update 7/20/06: Get v1.1.0 with a bunch of important patches.

I should clarify that the original bug is NOT fixed. The function I wrote fixed another bug that had to do with the word "func/endfunc" being matched at invalid places, such as While $RunFunction = 1 being seen as a "Func".

Putting that aside, there is STILL a bug in your code! What we fixed wasn't the real problem. Try the script I sent you again, using my patched Coroutine.au3 1.0.5 in the post below. Then look at the (still truncated) output of YOUR functions. :/

It seems that the bug might have to do with the length of the code I am exporting to _CoStart, or perhaps the length of the final output file. It could even be a bug of AutoIt's file write functions.

Best Regards,

Chris H.

Edited by darkthorn
Link to comment
Share on other sites

Update 7/20/06: Get v1.1.0 with a bunch of important patches.

I've changed every occurance of the StringInStr() Func/EndFunc scans to use _LineIsFunction() instead. So that part of your code is now bug free! :wacko: However, the output is still truncated, and in some parts jumbled aswell. It seems *VERY* unlikely that the bug is in AutoIt's FileWriteLine function, as I've run loads of tests, and even managed to get your script to write non-truncated data by changing the function I was sending to CoCreate.

The output is truncated in most cases, and it always changes with every single character difference in _CoCreate, if you add a character, it changes the output. If you remove a character, it changes the output. The truncate-bug only appears to happen beyond a certain length of the _CoCreate()'d function, but could also depend on WHAT is in the function. Certain words or characters might be causing your code to bug out? No matter what happens though, the full function is copied to the output. It's always the FileWriteLine's for your functions that fail. And I don't understand how, since they're just in one massive blob, one after the other.

As you know your code best, you should take a look at why my example is causing your _CoCreate write actions to end prematurely. I haven't been able to figure it out. Perhaps it has something to do with AutoIt anyway. A rare bug caused by the output of some character, perhaps? :D

Although, I've already verified that FileWriteLine can be called thousands of times in a loop, and create files of seemingly endless size. I've also verified that there is no limit on the number of lines in the output, so that's not why it breaks either. I have no idea why your code breaks during output. In one case, I got 137 lines in the output. Then I changed the function I was sending to _CoCreate a bit, and got 187 lines. It's completely random and seems like it's something in your code, not AutoIt's fault. Unless there is some rare bug in the FileWrite* function...

Either way, until that's solved at least I've attached my bug-fixed version of your Coroutine.au3 UDF, version 1.0.5. If you have a file comparison program you can easily see my changes. If not, I'll point them out to you if you want. Now we just have to figure out why the FileWriteLine actions end prematurely, and why they do it at completely different locations in your functions, depending on the length of the text you feed to _CoCreate.

Best Regards,

Chris H.

Edit: I looked at the return-values of all the FileWriteLine calls, and every single one of them succeeds (returns 1), so that's strange. Not only do they return 1 (successful write), but I noticed that every call gets carried out, even past the point where the output is truncated. That means that your code is NOT prematurely ending the output, and that the bug is in AUTOIT! Phew. That took a lot of investigating to find out. I am going to file a bug report.

Edited by darkthorn
Link to comment
Share on other sites

Update 7/20/06: Get v1.1.0 with a bunch of important patches.

Released Coroutine v1.0.6, ignore previous references to v1.0.5. Use the link at the bottom of the previous post download the archive. It contains the original 1.0.3 source, my patched version (1.0.6), and a Patches.htm file that shows *everything* that has been changed. There is really *no* reason to use the old, bugged version (for those that are reading this and wondering).

Edited by darkthorn
Link to comment
Share on other sites

As it turns out, the bug *was* in your code after all, and not in AutoIt. I've fixed it and applied all my other patches, and bumped the version number to 1.1.0.

I've decided to keep the previous posts for historical purposes, since some people might enjoy reading them. However, if you are new here just download the zip file attached to this post to get my latest patched version. I've also included a file called "Patches-1.1.0.html", which shows you in detail what was modified. Check it out.

And without further ado, here's the changelog for v1.1.0:

[ Patched by Darkthorn ]
7/20/06 (1.1.0) * Wrote _LineIsFunction() to correctly identify the start/end of functions.
                - Replaced the previous, buggy StringInStr() matches with the above function.
                    This means that your functions will no longer get incorrectly cut off
                    when they contain the words "Func" or "EndFunc" inside the function
                    definition.
                * Fixed a bug in _CoChangeWorkingDir(), it didn't work before as the variable 
                    wasn't being used anywhere. The following changes were made:
                - Changed _CoChangeWorkingDir() from $workingDir = $sDir to $sWorkingDir = $sDir.
                - Changed _RandomFileName() from FileGetShortName(@TempDir) to FileGetShortName($sWorkingDir)
                * The documentation for _CoInclude() says that it returns 1 on success, but it
                    didn't return anything on success, this has been fixed so that it returns 1.
                * Added documentation for _LineIsFunction(), and added it to the "Miscellaneous Function List".
                * Fixed an extremely severe bug where the very important FileClose() command was
                    omitted by the _CoInclude() function which appends functions to the output
                    script. This in turn makes AutoIt go crazy and causes it to truncate/jumble
                    the file contents whenever you try to access the file again. Not only that,
                    but any subsequent attempts to reopen the file for writing (such as another
                    call to _CoInclude()), would cause AutoIt to hang since the file would still be
                    locked from before. This fixed two bugs:
                - Output files are no longer truncated/jumbled when you use _CoInclude() to append functions.
                - _CoInclude() no longer hangs AutoIt on subsequent calls, since the file is now free to reopen.
[ Patched by Darkthorn ]

Enjoy!

Chris H.

Coroutine_1.1.0.zip

Link to comment
Share on other sites

  • 1 year later...

I havent used AutoIT since 2006 but it seems like since then they changed the return array of the StringRegExp function to not include the Array length at element 0, therefore all the sizing using $array[0] in this script needs to be replaces with Ubound($array) - 1.

Link to comment
Share on other sites

  • 3 months later...
  • Moderators

I havent used AutoIT since 2006 but it seems like since then they changed the return array of the StringRegExp function to not include the Array length at element 0, therefore all the sizing using $array[0] in this script needs to be replaces with Ubound($array) - 1.

Someone fixed it?

StringRegExp has never to my knowledge ever contained the UBound of the returned size of the arrays in element [0].

After looking at the source briefly, the [0] elemental size is due to using StringSplit() which seems to be used properly.

If you are referring to something else, or you feel that something is broken that you yourself cannot fix, please start a new thread in Support to continue the conversation.

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Link to comment
Share on other sites

  • 4 months later...

StringRegExp has never to my knowledge ever contained the UBound of the returned size of the arrays in element [0].

After looking at the source briefly, the [0] elemental size is due to using StringSplit() which seems to be used properly.

If you are referring to something else, or you feel that something is broken that you yourself cannot fix, please start a new thread in Support to continue the conversation.

What's more?

Link to comment
Share on other sites

  • 11 months later...
  • 4 weeks later...

Hi

I am trying Coroutine, but i dont understand it realy.

Could someone help me a little bit?

I am just trying to start two timers separated by buttons:

#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <Coroutine.au3>

$Form1 = GUICreate("Form1", 156, 88, -1, -1)
$Input1 = GUICtrlCreateInput("0", 24, 16, 25, 21, BitOR($ES_AUTOHSCROLL,$ES_READONLY))
$Input2 = GUICtrlCreateInput("0", 24, 48, 25, 21, BitOR($ES_AUTOHSCROLL,$ES_READONLY))
$Button1 = GUICtrlCreateButton("Button1", 72, 14, 75, 25, 0)
$Button2 = GUICtrlCreateButton("Button2", 72, 46, 75, 25, 0)
GUISetState(@SW_SHOW)

Global $pid1, $pid2

$thread1=_CoCreate('Func timer1()|For $i=1 To 10|Return $i|Sleep(1000)|Next|EndFunc')
$thread2=_CoCreate('Func timer2()|For $i=1 To 10|Return $i|Sleep(1000)|Next|EndFunc')

While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $Button1
            $pid1=_CoStart($thread1)
        Case $Button2
            $pid1=_CoStart($thread2)
        Case $GUI_EVENT_CLOSE
            _CoCleanup()
            Exit
    EndSwitch
    $status1=_CoStatus($pid1)
    If $status1 <> '' Then GUICtrlSetData($Input1, $status1)
    If $status2 <> '' Then GUICtrlSetData($Input2, $status2)
WEnd
Link to comment
Share on other sites

  • 2 months later...

Hi

I am trying Coroutine, but i dont understand it realy.

Could someone help me a little bit?

I am just trying to start two timers separated by buttons:

#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <Coroutine.au3>

$Form1 = GUICreate("Form1", 156, 88, -1, -1)
$Input1 = GUICtrlCreateInput("0", 24, 16, 25, 21, BitOR($ES_AUTOHSCROLL,$ES_READONLY))
$Input2 = GUICtrlCreateInput("0", 24, 48, 25, 21, BitOR($ES_AUTOHSCROLL,$ES_READONLY))
$Button1 = GUICtrlCreateButton("Button1", 72, 14, 75, 25, 0)
$Button2 = GUICtrlCreateButton("Button2", 72, 46, 75, 25, 0)
GUISetState(@SW_SHOW)

Global $pid1, $pid2

$thread1=_CoCreate('Func timer1()|For $i=1 To 10|Return $i|Sleep(1000)|Next|EndFunc')
$thread2=_CoCreate('Func timer2()|For $i=1 To 10|Return $i|Sleep(1000)|Next|EndFunc')

While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $Button1
            $pid1=_CoStart($thread1)
        Case $Button2
            $pid1=_CoStart($thread2)
        Case $GUI_EVENT_CLOSE
            _CoCleanup()
            Exit
    EndSwitch
    $status1=_CoStatus($pid1)
    If $status1 <> '' Then GUICtrlSetData($Input1, $status1)
    If $status2 <> '' Then GUICtrlSetData($Input2, $status2)
WEnd
Same here, It doesn`t work for me as well. Edited by bleed
Link to comment
Share on other sites

  • 1 year later...

I fixed some of the errors I've talked about back in 2007 but never published them. Then I wanted to use this UDF again and I forgot which errors I fixed. Lucky for me, I never delete anything and found my old fixes. Long behold, I had to make a few more, but I got it working up to date.

;===============================================================================
;
; File: Coroutine.au3
; Description: UDF Library used for coroutine multithreading
; Version: 1.1.1
; Date: 8/13/07
; Author: Ben Brightwell
; Credit:
;   BrianH (paroxsitic) for updating compliance to the newer StringRegExp, ConsoleRead, and StdoutRead
;   ChrisH (darkthorn) for a huge amount of severe bug fixes.
;   ChrisL for the base idea of deploying files as child scripts.
;       Topic can be found here:
;       http://www.autoitscript.com/forum/index.php?showtopic=22048
;
;===============================================================================

;===============================================================================
#cs
    Changelog:

    [ Patched by ParoXsitiC ]
    7/12/10 (1.1.2) - Fixed bug with ConsoleRead and StdoutRead taken in the wrong parameters based on old versions
    9/13/07 (1.1.1) * Wrote an alternative to the now obsolete \# option with StringRegExp
                    - Fixed bug with [:space:] by replacing it with \s. note: \s includes Vertical Tab: chr(11)
                    - Updated flag 2 of StringRegExp to flag 0 so that it complies with the updated StringRegExp
    [ Patched by ParoXsitiC ]

    [ Patched by Darkthorn ]
    7/20/06 (1.1.0) * Wrote _LineIsFunction() to correctly identify the start/end of functions.
                    - Replaced the previous, buggy StringInStr() matches with the above function.
                        This means that your functions will no longer get incorrectly cut off
                        when they contain the words "Func" or "EndFunc" inside the function
                        definition.
                    * Fixed a bug in _CoChangeWorkingDir(), it didn't work before as the variable
                        wasn't being used anywhere. The following changes were made:
                    - Changed _CoChangeWorkingDir() from $workingDir = $sDir to $sWorkingDir = $sDir.
                    - Changed _RandomFileName() from FileGetShortName(@TempDir) to FileGetShortName($sWorkingDir)
                    * The documentation for _CoInclude() says that it returns 1 on success, but it
                        didn't return anything on success, this has been fixed so that it returns 1.
                    * Added documentation for _LineIsFunction(), and added it to the "Miscellaneous Function List".
                    * Fixed an extremely severe bug where the very important FileClose() command was
                        omitted by the _CoInclude() function which appends functions to the output
                        script. This in turn makes AutoIt go crazy and causes it to truncate/jumble
                        the file contents whenever you try to access the file again. Not only that,
                        but any subsequent attempts to reopen the file for writing (such as another
                        call to _CoInclude()), would cause AutoIt to hang since the file would still be
                        locked from before. This fixed two bugs:
                    - Output files are no longer truncated/jumbled when you use _CoInclude() to append functions.
                    - _CoInclude() no longer hangs AutoIt on subsequent calls, since the file is now free to reopen.
    [ Patched by Darkthorn ]

    4/25/06 (1.0.3) - Fixed bug with using variables that contained the word
                        "return"
                    - Fixed bug with StringLower()-ing every line of code
                        in coroutine script, now case is untouched
                    - Fixed the way _CoInclude() works so that included code
                        is placed at the top of the file instead of the bottom
                    - Fixed a bug with passing a simple string to the script
                        an extra dimension indicator was present

    4/15/06 (1.0.2) - Truncated version number (Builds are irrelevant)
                    - Added support for expressions in the return statement
                        of a coroutine

    3/23/06 (1.0.1.0) - Modified _CoCreate() to handle a function with no
                            parameters
                      - Added "Core Function List" and "Miscellaneous Function
                            List"
                      - Added this Changelog
                      - Changed _CoAddHelper() to _CoInclude()
                      - Fixed a bug in _CoInclude() where the last line of code
                            was not being read into the script file
#ce
;===============================================================================

;===============================================================================
#cs
    Core Function List:

    _CoCreate()
    Create a coroutine script which can be instanced by _CoStart()

    _CoInclude()
    Include helper/wrapper functions to compliment the main body created by
        _CoCreate()

    _CoStart()
    Create an instance of a threaded function as created by _CoCreate()

    _CoYield()
    NOTE: Only to be used in coroutine scripts
        Pauses the coroutine and yields a value to the main script, returns
        any value passed by the corresponding call to _CoResume()

    _CoStatus()
    Returns the status of a coroutine

    _CoSend()
    Sends a variable into a child script

    _CoResume()
    Unpauses a coroutine, optionally sends a value back to the yielded
        coroutine

    _CoGetReturn()
    Retrieves a variable returned by a coroutine

    _CoKill()
    Closes a coroutine

    _CoCleanup()
    Closes all running coroutines, and deletes their respective temporary script
        files

    _CoChangeWorkingDir
    Changes the working directory for storing the temporary script files.
        NOTE: Default is @TempDir

    Miscellaneous Function List:
        NOTE: These functions are meant to be called internally by Coroutine.au3

    _PackVarToStr()
    Packs a variable into a string for transmission between scripts.

    _UnpackStrToVar()
    Unpacks a packed variable-string into its original structure, whether it be
        a string or array.

    _LineIsFunction()
    Classifies the given line as either the start/end of a function, or as a regular line.

    _RandomFileName()
    Generates a random unused filename for use with _CoCreate()
#ce
;===============================================================================

Local $avCoThreads[1][2] ;[n] == iCoThreadID [n][0] == sFilePath [n][1] == iNumParams
$avCoThreads[0][0] = 0 ;Number of Multi-threaded Functions
Local $avPIDs[1][2] ;[n][0] == iPID [n][1] == iCoThreadID
$avPIDs[0][0] = 0 ;Number of iPID's created
Local $sWorkingDir = @TempDir

;===============================================================================
;
; Function Name:    _CoCreate()
; Description:      Create a child script for use with _CoStart()
; Parameter(s):     $sFuncRawText - A delimited string containing the lines of
;                       the threaded function. Default delimiter is "|". Example:
;                       "Func MyFunc($test1)|Return $test1|EndFunc"
;                   $sDelimiter - A string containing the delimiter used in
;                       $sFuncRawText (Default = "|")
; Return Value(s):  On Success - Returns the CoThreadID associated with the
;                                   created thread.
;                   On Failure - 0 and @error set to:
;                                   1 - Invalid delimiter or delimiter not found
;                                   2 - File could not be created, check working
;                                       directory access permissions
;                                   3 - Function parameters not formatted
;                                       correctly. Try eliminating white space:
;                                       Ex: Func MyFunc($test1,$test2,$test3)
;                                   4 - Return line not formatted correctly.
; Author(s):        Ben Brightwell
;
;===============================================================================
Func _CoCreate($sFuncRawText, $sDelimiter = "|")
    Local $asFuncLines = StringSplit($sFuncRawText, $sDelimiter)
    Local $bUnpackStrToVarUsed = 1 ; Determines if source for _UnpackStrToVar() is required in child script
    Local $bPackVarToStrUsed = 0 ; Determines if source for _PackVarToStr() is required in child script
    Local $bCoYieldUsed = 0 ; Determines if source for _CoYield() is required in child script
    Local $hFile = "" ; For writing to the child script file
    Local $asParams = "" ; Array of parameters pulled from the line: "Func MyFunction($test1, $test2, $test3)" in $sFuncRawText
    Local $asReturnLine = "" ; Line parsed from $sFuncRawText to contain the value that is passed as "Return $value"
    If IsArray($asFuncLines) Then
        ReDim $avCoThreads[$avCoThreads[0][0] + 2][2]
        $avCoThreads[0][0] += 1
        $avCoThreads[$avCoThreads[0][0]][0] = _RandomFileName()
        $hFile = FileOpen($avCoThreads[$avCoThreads[0][0]][0], 2)
        If $hFile == -1 Then
            SetError(2) ; File could not be created, check working directory access permissions
            Return 0 ; failure
        EndIf
        $asParams = StringRegExp($asFuncLines[1] & " ", "(\$.*?)(?:[ ,\\)])", 3)
        If IsArray($asParams) Then
            $avCoThreads[$avCoThreads[0][0]][1] = UBound($asParams)
            If IsArray($asParams) Then
                FileWriteLine($hFile, 'Local $sParamsInStr = "" ; Used to pass in parameters')
                FileWriteLine($hFile, 'Local $iNumChrsToRead = 0 ; Used for parameter strings longer than 64Kb')
                FileWriteLine($hFile, 'Local $sReturnStr = ""')
                FileWriteLine($hFile, 'While ConsoleRead(true) == 0')
                FileWriteLine($hFile, '  Sleep(10)')
                FileWriteLine($hFile, 'WEnd')
                FileWriteLine($hFile, '$sParamsInStr = ConsoleRead()')
                FileWriteLine($hFile, '$iNumChrsToRead= StringRegExp($sParamsInStr, "(\d*?)(?:\$\[)", 1)')
                FileWriteLine($hFile, '$iLen = StringInStr($sParamsInStr, $iNumChrsToRead[0]) + StringLen ( $iNumChrsToRead[0] ) - 1')
                FileWriteLine($hFile, '$sParamsInStr = StringTrimLeft($sParamsInStr, $iLen)')
                FileWriteLine($hFile, 'While StringLen($sParamsInStr) < $iNumChrsToRead[0]')
                FileWriteLine($hFile, '  $sParamsInStr &= ConsoleRead()')
                FileWriteLine($hFile, 'WEnd')
                For $i = 0 To UBound($asParams) - 1
                    FileWriteLine($hFile, $asParams[$i] & " = _UnpackStrToVar($sParamsInStr)")
                Next
            EndIf
        ElseIf Not StringInStr($asFuncLines[1], "$") Then
            FileWriteLine($hFile, ';No function parameters')
        Else
            SetError(3) ; Function parameters not formatted correctly
            Return 0 ; failure
        EndIf
        For $i = 1 To $asFuncLines[0] - 1
            If StringInStr($asFuncLines[$i], "return ") And Not StringInStr($asFuncLines[$i], ";") And Not StringInStr($asFuncLines[$i], "=") Then
                $asReturnLine = StringRegExp($asFuncLines[$i], "(?i:return\s*)(.*)", 1)
                If IsArray($asReturnLine) Then
                    FileWriteLine($hFile, '$sExpStr = ' &$asReturnLine[0])
                    FileWriteLine($hFile, '$sExpStr = _PackVarToStr($sExpStr)')
                    FileWriteLine($hFile, 'ConsoleWrite("return" & StringLen($sExpStr) & $sExpStr)')
                    $bPackVarToStrUsed = 1
                Else
                    SetError(4) ; Return line not formatted correctly
                    Return 0 ; failure
                EndIf
            ElseIf Not _LineIsFunction($asFuncLines[$i]) Then
                FileWriteLine($hFile, $asFuncLines[$i])
                If StringInStr($asFuncLines[$i], "_coyield") Then
                    $bCoYieldUsed = 1
                EndIf
            EndIf
        Next
        If $bCoYieldUsed == 1 Then
            FileWriteLine($hFile, 'Func _CoYield($bPeek, $sVarName = "")')
            FileWriteLine($hFile, '  If $bPeek == 1 Then')
            FileWriteLine($hFile, '     If ConsoleRead(true) <> 0 Then')
            FileWriteLine($hFile, '         $vResumeVar = ConsoleRead()')
            FileWriteLine($hFile, '         $vResumeVar = _UnpackStrToVar($vResumeVar)')
            FileWriteLine($hFile, '         SetExtended(1)')
            FileWriteLine($hFile, '         Return $vResumeVar')
            FileWriteLine($hFile, '     Else')
            FileWriteLine($hFile, '         SetExtended(0)')
            FileWriteLine($hFile, '         Return ""')
            FileWriteLine($hFile, '     EndIf')
            FileWriteLine($hFile, '  Else')
            FileWriteLine($hFile, '     Local $vVarNameEval = "" ; If $sVarName equates to a declared variable, a copy of it is stored here')
            FileWriteLine($hFile, '     Local $vResumeVar = "" ; When _CoResume() is called to unpause script, a variable can be passed')
            FileWriteLine($hFile, '     If StringLeft($sVarName, 1) == "$" Then StringReplace($sVarName, "$", "")')
            FileWriteLine($hFile, '     If IsDeclared($sVarName) Then')
            FileWriteLine($hFile, '         $vVarNameEval = Eval($sVarName)')
            FileWriteLine($hFile, '         $vVarNameEval = _PackVarToStr($vVarNameEval)')
            FileWriteLine($hFile, '         ConsoleWrite("yield" & StrLen($vVarNameEval) & $vVarNameEval)')
            FileWriteLine($hFile, '     Else')
            FileWriteLine($hFile, '         $vVarName = _PackVarToStr($vVarName)')
            FileWriteLine($hFile, '         ConsoleWrite("yield" & StrLen($vVarName) & $vVarName')
            FileWriteLine($hFile, '     EndIf')
            FileWriteLine($hFile, '     While ConsoleRead(true) == 0')
            FileWriteLine($hFile, '         Sleep(10)')
            FileWriteLine($hFile, '     WEnd')
            FileWriteLine($hFile, '     $vResumeVar = ConsoleRead()')
            FileWriteLine($hFile, '     $vResumeVar = _UnpackStrToVar($vResumeVar)')
            FileWriteLine($hFile, '     Return $vResumeVar')
            FileWriteLine($hFile, '  EndIf')
            FileWriteLine($hFile, 'EndFunc')
        EndIf
        If $bUnpackStrToVarUsed == 1 Then
            FileWriteLine($hFile, 'Func _UnpackStrToVar(ByRef $sVarStr)')
            FileWriteLine($hFile, ' Local $aiNumDims = StringRegExp($sVarStr, ''(?:\$\[)(\d*)(?:\]\$)'', 1)')
            FileWriteLine($hFile, ' Local $aiDimSizes[1] ; To contain the size of each dimension as passed through $sVarStr')
            FileWriteLine($hFile, ' Local $aiDimSize = "" ; To contain the size of current dimension and string position for stripping')
            FileWriteLine($hFile, ' Local $avRetArr[1] ; To be redimensioned and have $sVarStr parsed and stored into as an array')
            FileWriteLine($hFile, ' Local $avElementStr = "" ; To contain each element as a string as it is parsed from $sVarStr')
            FileWriteLine($hFile, ' If IsArray($aiNumDims) Then')
            FileWriteLine($hFile, '     $sVarStr = StringTrimLeft($sVarStr, StringInStr($sVarStr, $aiNumDims[0] & "]$") + StringLen ( $aiNumDims[0] & "]$" ) - 1)')
            FileWriteLine($hFile, '     If $aiNumDims[0] > 0 Then')
            FileWriteLine($hFile, '         ReDim $aiDimSizes[$aiNumDims[0]]')
            FileWriteLine($hFile, '         For $iCounter1 = 0 To $aiNumDims[0] - 1')
            FileWriteLine($hFile, '             $aiDimSize = StringRegExp($sVarStr, ''(?:\$\[)(\d*)(?:\]\$)'', 1)')
            FileWriteLine($hFile, '             $aiDimSizes[$iCounter1] = $aiDimSize[0]')
            FileWriteLine($hFile, '             $sVarStr = StringTrimLeft($sVarStr, StringInStr($sVarStr, $aiDimSize[0] & "]$") + StringLen ( $aiDimSize[0] & "]$" ) - 1)')
            FileWriteLine($hFile, '         Next')
            FileWriteLine($hFile, '     EndIf')
            FileWriteLine($hFile, '     Select')
            FileWriteLine($hFile, '         Case $aiNumDims[0] == 0')
            FileWriteLine($hFile, '             If StringInStr($sVarStr, "$[") Then')
            FileWriteLine($hFile, '                 $avElementStr = StringRegExp($sVarStr, ''(.*?)(?:\$\[)'', 1)')
            FileWriteLine($hFile, '                 $sVarStr = StringTrimLeft($sVarStr, StringInStr($sVarStr, $avElementStr[0]) + StringLen ( $avElementStr[0]) - 1)')
            FileWriteLine($hFile, '                 If $avElementStr[0] <> "<nil>" Then')
            FileWriteLine($hFile, '                     Return $avElementStr[0]')
            FileWriteLine($hFile, '                 Else')
            FileWriteLine($hFile, '                     Return ""')
            FileWriteLine($hFile, '                 EndIf')
            FileWriteLine($hFile, '             Else')
            FileWriteLine($hFile, '                 If $sVarStr == "<nil>" Then')
            FileWriteLine($hFile, '                     Return ""')
            FileWriteLine($hFile, '                 Else')
            FileWriteLine($hFile, '                     Return $sVarStr')
            FileWriteLine($hFile, '                 EndIf')
            FileWriteLine($hFile, '             EndIf')
            FileWriteLine($hFile, '         Case $aiNumDims[0] == 1')
            FileWriteLine($hFile, '             ReDim $avRetArr[$aiDimSizes[0]]')
            FileWriteLine($hFile, '             For $iCounter1 = 0 To $aiDimSizes[0] - 1')
            FileWriteLine($hFile, '                 $avRetArr[$iCounter1] = _UnpackStrToVar($sVarStr) ; In case element holds another array (Recursive)')
            FileWriteLine($hFile, '             Next')
            FileWriteLine($hFile, '         Case $aiNumDims[0] == 2')
            FileWriteLine($hFile, '             ReDim $avRetArr[$aiDimSizes[0]][$aiDimSizes[1]]')
            FileWriteLine($hFile, '             For $iCounter1 = 0 To $aiDimSizes[0] - 1')
            FileWriteLine($hFile, '                 For $iCounter2 = 0 To $aiDimSizes[1] - 1')
            FileWriteLine($hFile, '                     $avRetArr[$iCounter1][$iCounter2] = _UnpackStrToVar($sVarStr) ; In case element holds another array (Recursive)')
            FileWriteLine($hFile, '                 Next')
            FileWriteLine($hFile, '             Next')
            FileWriteLine($hFile, '         Case $aiNumDims[0] == 3')
            FileWriteLine($hFile, '             ReDim $avRetArr[$aiDimSizes[0]][$aiDimSizes[1]][$aiDimSizes[2]]')
            FileWriteLine($hFile, '             For $iCounter1 = 0 To $aiDimSizes[0] - 1')
            FileWriteLine($hFile, '                 For $iCounter2 = 0 To $aiDimSizes[1] - 1')
            FileWriteLine($hFile, '                     For $iCounter3 = 0 To $aiDimSizes[2] - 1')
            FileWriteLine($hFile, '                         $avRetArr[$iCounter1][$iCounter2][$iCounter3] = _UnpackStrToVar($sVarStr) ; In case element holds another array (Recursive)')
            FileWriteLine($hFile, '                     Next')
            FileWriteLine($hFile, '                 Next')
            FileWriteLine($hFile, '             Next')
            FileWriteLine($hFile, '         Case $aiNumDims[0] == 4')
            FileWriteLine($hFile, '             ReDim $avRetArr[$aiDimSizes[0]][$aiDimSizes[1]][$aiDimSizes[2]][$aiDimSizes[3]]')
            FileWriteLine($hFile, '             For $iCounter1 = 0 To $aiDimSizes[0] - 1')
            FileWriteLine($hFile, '                 For $iCounter2 = 0 To $aiDimSizes[1] - 1')
            FileWriteLine($hFile, '                     For $iCounter3 = 0 To $aiDimSizes[2] - 1')
            FileWriteLine($hFile, '                         For $iCounter4 = 0 To $aiDimSizes[3] - 1')
            FileWriteLine($hFile, '                             $avRetArr[$iCounter1][$iCounter2][$iCounter3][$iCounter4] = _UnpackStrToVar($sVarStr) ; In case element holds another array (Recursive)')
            FileWriteLine($hFile, '                         Next')
            FileWriteLine($hFile, '                     Next')
            FileWriteLine($hFile, '                 Next')
            FileWriteLine($hFile, '             Next')
            FileWriteLine($hFile, '     EndSelect')
            FileWriteLine($hFile, '     Return $avRetArr')
            FileWriteLine($hFile, ' EndIf')
            FileWriteLine($hFile, 'EndFunc   ;==>_UnpackStrToVar')
        EndIf
        If $bPackVarToStrUsed == 1 Then
            FileWriteLine($hFile, 'Func _PackVarToStr(ByRef $vPackVar)')
            FileWriteLine($hFile, ' Local $iNumDims = UBound($vPackVar, 0) ; Number of dimensions of $vPackVar')
            FileWriteLine($hFile, ' Local $sVarStr = "$[" & $iNumDims & "]$" ; Return string of packed variable')
            FileWriteLine($hFile, ' Local $iCounter1 = "" ; Nested Counter')
            FileWriteLine($hFile, ' Local $iCounter2 = "" ; Nested Counter')
            FileWriteLine($hFile, ' Local $iCounter3 = "" ; Nested Counter')
            FileWriteLine($hFile, ' Local $iCounter4 = "" ; Nested Counter')
            FileWriteLine($hFile, ' For $i = 1 To $iNumDims')
            FileWriteLine($hFile, '     $sVarStr &= "$[" & UBound($vPackVar, $i) & "]$"')
            FileWriteLine($hFile, ' Next')
            FileWriteLine($hFile, ' Select')
            FileWriteLine($hFile, '     Case $iNumDims == 0')
            FileWriteLine($hFile, '         If $vPackVar == "" Then')
            FileWriteLine($hFile, '             $sVarStr &= "<nil>"')
            FileWriteLine($hFile, '         Else')
            FileWriteLine($hFile, '             $sVarStr &= $vPackVar')
            FileWriteLine($hFile, '         EndIf')
            FileWriteLine($hFile, '     Case $iNumDims == 1')
            FileWriteLine($hFile, '         For $iCounter1 = 0 To UBound($vPackVar, 1) - 1')
            FileWriteLine($hFile, '             If $vPackVar[$iCounter1] == "" And UBound($vPackVar[$iCounter1], 0) == 0 Then')
            FileWriteLine($hFile, '                 $sVarStr &= "$[0]$<nil>"')
            FileWriteLine($hFile, '             Else')
            FileWriteLine($hFile, '                 $sVarStr &= _PackVarToStr($vPackVar[$iCounter1])')
            FileWriteLine($hFile, '             EndIf')
            FileWriteLine($hFile, '         Next')
            FileWriteLine($hFile, '     Case $iNumDims == 2')
            FileWriteLine($hFile, '         For $iCounter1 = 0 To UBound($vPackVar, 1) - 1')
            FileWriteLine($hFile, '             For $iCounter2 = 0 To UBound($vPackVar, 2) - 1')
            FileWriteLine($hFile, '                 If $vPackVar[$iCounter1][$iCounter2] == "" And UBound($vPackVar[$iCounter1][$iCounter2], 0) == 0 Then')
            FileWriteLine($hFile, '                     $sVarStr &= "$[0]$<nil>"')
            FileWriteLine($hFile, '                 Else')
            FileWriteLine($hFile, '                     $sVarStr &= _PackVarToStr($vPackVar[$iCounter1][$iCounter2])')
            FileWriteLine($hFile, '                 EndIf')
            FileWriteLine($hFile, '             Next')
            FileWriteLine($hFile, '         Next')
            FileWriteLine($hFile, '     Case $iNumDims == 3')
            FileWriteLine($hFile, '         For $iCounter1 = 0 To UBound($vPackVar, 1) - 1')
            FileWriteLine($hFile, '             For $iCounter2 = 0 To UBound($vPackVar, 2) - 1')
            FileWriteLine($hFile, '                 For $iCounter3 = 0 To UBound($vPackVar, 3) - 1')
            FileWriteLine($hFile, '                     If $vPackVar[$iCounter1][$iCounter2][$iCounter3] == "" And UBound($vPackVar[$iCounter1][$iCounter2][$iCounter3], 0) == 0 Then')
            FileWriteLine($hFile, '                         $sVarStr &= "$[0]$<nil>"')
            FileWriteLine($hFile, '                     Else')
            FileWriteLine($hFile, '                         $sVarStr &= _PackVarToStr($vPackVar[$iCounter1][$iCounter2][$iCounter3])')
            FileWriteLine($hFile, '                     EndIf')
            FileWriteLine($hFile, '                 Next')
            FileWriteLine($hFile, '             Next')
            FileWriteLine($hFile, '         Next')
            FileWriteLine($hFile, '     Case $iNumDims == 4')
            FileWriteLine($hFile, '         For $iCounter1 = 0 To UBound($vPackVar, 1) - 1')
            FileWriteLine($hFile, '             For $iCounter2 = 0 To UBound($vPackVar, 2) - 1')
            FileWriteLine($hFile, '                 For $iCounter3 = 0 To UBound($vPackVar, 3) - 1')
            FileWriteLine($hFile, '                     For $iCounter4 = 0 To UBound($vPackVar, 4) - 1')
            FileWriteLine($hFile, '                         If $vPackVar[$iCounter1][$iCounter2][$iCounter3][$iCounter4] == "" And UBound($vPackVar[$iCounter1][$iCounter2][$iCounter3][$iCounter4], 0) == 0 Then')
            FileWriteLine($hFile, '                             $sVarStr &= "$[0]$<nil>"')
            FileWriteLine($hFile, '                         Else')
            FileWriteLine($hFile, '                             $sVarStr &= _PackVarToStr($vPackVar[$iCounter1][$iCounter2][$iCounter3][$iCounter4])')
            FileWriteLine($hFile, '                         EndIf')
            FileWriteLine($hFile, '                     Next')
            FileWriteLine($hFile, '                 Next')
            FileWriteLine($hFile, '             Next')
            FileWriteLine($hFile, '         Next')
            FileWriteLine($hFile, ' EndSelect')
            FileWriteLine($hFile, ' Return $sVarStr')
            FileWriteLine($hFile, 'EndFunc   ;==>_PackVarToStr')
        EndIf
        FileClose($hFile)
        Return $avCoThreads[0][0]
    Else
        SetError(1) ; Invalid delimiter or delimiter not found
        Return 0 ; failure
    EndIf
EndFunc   ;==>_CoCreate

;===============================================================================
;
; Function Name:    _CoYield()
; Description:      Yield a value to the main script and pause the
;                       coroutine, only usable by the coroutine
; Parameter(s):     $sVarName - String containing the name of the variable
;                       to pass to the main script. If string does not contain
;                       the name of a declared variable, it is passed as a
;                       literal string.
; Return Value(s):  The variable passed by the corresponding call to _CoResume()
; Author(s):        Ben Brightwell
;
;===============================================================================
;~ Func _CoYield($bPeek, $sVarName = "")
;~  If $bPeek == 1 Then
;~      If ConsoleRead(0,true) <> 0 Then
;~          $vResumeVar = ConsoleRead()
;~          $vResumeVar = _UnpackStrToVar($vResumeVar)
;~          SetExtended(1)
;~          Return $vResumeVar
;~      Else
;~          SetExtended(0)
;~          Return ""
;~      EndIf
;~  Else
;~      Local $vVarNameEval = "" ; If $sVarName equates to a declared variable, a copy of it is stored here
;~      Local $vResumeVar = "" ; When _CoResume() is called to unpause script, a variable can be passed
;~      If StringLeft($sVarName, 1) == "$" Then StringReplace($sVarName, "$", "")
;~      If IsDeclared($sVarName) Then
;~          $vVarNameEval = Eval($sVarName)
;~          $vVarNameEval = _PackVarToStr($vVarNameEval)
;~          ConsoleWrite("yield" & StrLen($vVarNameEval) & $vVarNameEval)
;~      Else
;~          $vVarName = _PackVarToStr($vVarName)
;~          ConsoleWrite("yield" & StrLen($vVarName) & $vVarName
;~      EndIf
;~      While ConsoleRead(0,true) == 0
;~          Sleep(50)
;~      WEnd
;~      $vResumeVar = ConsoleRead()
;~      $vResumeVar = _UnpackStrToVar($vResumeVar)
;~      Return $vResumeVar
;~  EndIf
;~ EndFunc

;===============================================================================
;
; Function Name:    _CoInclude()
; Description:      Include a UDF(s) in the coroutine script.
;                       NOTE: #include <something.au3> will work in _CoCreate()
;                       but keep in mind that the "something.au3" file must
;                       exist on the computer that is running the script.
; Parameter(s):     $iCoThreadID - The thread to add the include to
;                   $sFuncRawText - The function text, delimited by $sDelimiter
;                   $sDelimiter - Used to delimit $sFuncRawText (Default "|")
; Return Value(s):  Success - 1
;                   Failure - 0 and @error set to:
;                               1 - Invalid iCoThreadID
;                               2 - Invalid delimiter or delimiter not found
;                               3 - Code formatted incorrectly. Namely mis-
;                                   matched number of "Func ..." and "EndFunc"
;                                   lines.
;                               4 - Script file could not be opened.
; Author(s):        Ben Brightwell
;
;===============================================================================
Func _CoInclude($iCoThreadID, $sFuncRawText, $sDelimiter = "|")
    Local $asFuncLines = "" ; Array holding the lines of code stripped from $sFuncRawText
    Local $sFormattedCode = "" ; String to write to the script file
    Local $iNumFuncs = 0 ; Number of lines that contain "Func ...". Used for minimal codechecking
    Local $iNumEndFuncs = 0 ; Number of lines that contain "EndFunc". Used for minimal codechecking
    Local $iFileCheck = 0 ; Used to check if file was written to correctly.
    Local $sPrevContents = "" ; Used to store contents of original coroutine script.
    Local $hFile = "" ; Handle for source file
    If $iCoThreadID <= $avCoThreads[0][0] Then
        $asFuncLines = StringSplit($sFuncRawText, $sDelimiter)
        If IsArray($asFuncLines) Then
            $sFormattedCode &= ';============' & @CRLF
            $sFormattedCode &= ';Include Code' & @CRLF
            $sFormattedCode &= ';============' & @CRLF
            For $i = 1 To $asFuncLines[0]
                If _LineIsFunction($asFuncLines[$i]) Then
                    If @Error = 1 Then
                        $iNumFuncs += 1
                    Else ; error is 2
                        $iNumEndFuncs += 1
                    EndIf
                EndIf
            Next
            If $iNumFuncs == $iNumEndFuncs Then
                For $i = 1 To $asFuncLines[0]
                    $sFormattedCode &= $asFuncLines[$i] & @CRLF
                Next
            Else
                SetError(3) ; Code formatted incorrectly
                Return 0 ; failure
            EndIf
        Else
            SetError(2) ; Delimiter not found in $sFuncRawText
            Return 0 ; failure
        EndIf
        $sPrevContents = FileRead($avCoThreads[$iCoThreadID][0]) ; read old contents
        $hFile = FileOpen($avCoThreads[$iCoThreadID][0], 2) ; mode 2=overwrite
        If FileWrite($hFile, $sFormattedCode) == 0 Then ; write header to file
            SetError(4) ; File could not be written to
            Return 0 ; failure
        Else
            FileClose($hFile) ; close file to reopen in another mode
            $hFile = FileOpen($avCoThreads[$iCoThreadID][0], 1) ; mode 1=append
            FileWrite($hFile, $sPrevContents) ; re-add old content
            FileClose($hFile) ; important: close the file, which you forgot
        EndIf
    Else
        SetError(1) ; Invalid iCoThreadID
        Return 0 ; failure
    EndIf
    Return 1 ; success
EndFunc

;===============================================================================
;
; Function Name:    _CoStart()
; Description:      Create a coroutine from a thread created by _CoCreate()
; Parameter(s):     $iCoThreadID - as returned by _CoCreate()
;                   $sParamStr - the string you would call a normal function
;                       with. Example: MyFunc($test1, $test2)
;                       $sParamStr = "$test1, $test2"
; Return Value(s):  On Success - the PID of the created coroutine
;                   On Failure - -1 and sets @error to:
;                                   1 - Invalid iCoThreadID
;                                   2 - Parameter string formatted incorrectly
; Author(s):        Ben Brightwell
;
;===============================================================================
Func _CoStart($iCoThreadID, $sParamStr = "")
    Local $sPackedParamStr = "" ; If arrays exist in $sParamStr, then they need to be packed into a string
    Local $asParams = "" ; Holds individual parameters parsed from $sParamStr
    Local $sStrippedParam = "" ; Holds a parameter stripped of "$" and ","
    Local $vParamEval = "" ; If $sStrippedParam is a valid variable, a temporary one is evaluated to this container
    Local $sParamLiteral = "" ; If a non-variable is passed, it is interpreted as a literal
    If $iCoThreadID <= $avCoThreads[0][0] And $iCoThreadID <> 0 Then
        ReDim $avPIDs[$avPIDs[0][0] + 2][2]
        $avPIDs[0][0] += 1
        If $sParamStr <> "" Then
            If StringRight($sParamStr, 1) <> "," Then
                $sParamStr &= "," ; Makes $sParamStr easier to parse with StringRegExp
            EndIf
            $asParams = StringRegExp($sParamStr, '(".*?".*?,|[^ ]*?[ ]?,)+', 3)
            If Not IsArray($asParams) Then
                SetError(2) ; Parameter string not formatted correctly
                Return -1 ; failure
            EndIf
            For $i = 0 To UBound($asParams) - 1
                If StringInStr($asParams[$i], "$") Then
                    $sStrippedParam = StringReplace(StringReplace($asParams[$i], "$", ""), ",", "")
                    If IsDeclared($sStrippedParam) Then
                        $vParamEval = Eval($sStrippedParam)
                        $sPackedParamStr &= _PackVarToStr($vParamEval)
                    Else
                        $sParamLiteral = StringTrimRight($asParams[$i], 1)
                        $sPackedParamStr &= _PackVarToStr($sParamLiteral)
                    EndIf
                Else
                    $sParamLiteral = StringTrimRight($asParams[$i], 1)
                    $sPackedParamStr &= _PackVarToStr($sParamLiteral)
                EndIf
            Next
        EndIf
        $avPIDs[$avPIDs[0][0]][0] = Run(@AutoItExe & ' /AutoIt3ExecuteScript "' & $avCoThreads[$iCoThreadID][0] & '"', "", "", 7)
        If StringLen($sPackedParamStr) > 0 Then
            StdinWrite($avPIDs[$avPIDs[0][0]][0], StringLen($sPackedParamStr) & $sPackedParamStr)
        EndIf
        $avPIDs[$avPIDs[0][0]][1] = $iCoThreadID
        Return $avPIDs[$avPIDs[0][0]][0]
    Else
        SetError(1) ;Invalid iCoThreadID
        Return -1 ; failure
    EndIf
EndFunc   ;==>_CoStart

;===============================================================================
;
; Function Name:    _CoStatus()
; Description:      Determines the status of a PID returned by _CoStart()
; Parameter(s):     $iPID - PID as returned by _CoStart()
; Return Value(s):  On Success - Returns the status of the coroutine
;                       Possible values: "yielded" - yielded a value, script paused
;                                        "running" - running
;                                        "returned" - coroutine dead, with a return value
;                                        "dead" - coroutine dead with no return value
;                   On Failure - Returns empty string ("") and sets error to 1
; Author(s):        Ben Brightwell
;
;===============================================================================
Func _CoStatus($iPID)
    Local $bPIDFound = 0 ; Used to search for a PID created by _CoStart()
    For $i = 1 To $avPIDs[0][0]
        If $avPIDs[$i][0] == $iPID Then
            $bPIDFound = 1
        EndIf
    Next
    If $bPIDFound == 1 Then
        Select
            Case ProcessExists($iPID) <> 0
                If StringLeft(StdoutRead($iPID, True), 5) == "yield" Then
                    Return "yielded"
                ElseIf StringLeft(StdoutRead($iPID, True), 6) == "return" Then
                    Return "returned" ; Coroutine returned, but return string is longer than console buffer
                Else
                    Return "running"
                EndIf
            Case ProcessExists($iPID) == 0
                If StringLeft(StdoutRead($iPID, True), 0) <> 0 Then
                    Return "returned"
                Else
                    Return "dead"
                EndIf
        EndSelect
    Else
        SetError(1) ;$PID not created with _CoStart()
        Return ""
    EndIf
EndFunc   ;==>_CoStatus

;===============================================================================
;
; Function Name:    _CoSend()
; Description:      Sends a variable to the child script.
; Parameter(s):     $iPID - PID as returned by _CoStart()
; Return Value(s):  On Success - none
;                   On Failure - Returns empty string ("") and sets error to 1
; Author(s):        Ben Brightwell
;
;===============================================================================
Func _CoSend($iPID, $vInVar)
    If _CoStatus($iPID) <> "dead" And _CoStatus($iPID) <> "returned" And _CoStatus($iPID) <> "yielded" Then
        StdinWrite($iPID, _PackVarToStr($vInVar))
    Else
        SetError(1)
        Return ""
    EndIf
EndFunc

;===============================================================================
;
; Function Name:    _CoResume()
; Description:      Resumes a paused coroutine and returns the yielded variable
; Parameter(s):     $iPID - PID as returned by _CoStart()
; Return Value(s):  On Success - Resumes paused coroutine and returns variable
;                       yielded by _CoYield()
;                   On Failure - Returns empty string ("") and sets error to 1
; Author(s):        Ben Brightwell
;
;===============================================================================
Func _CoResume($iPID, $vInVar = "")
    Local $sReturnStr = "" ; Contains return string
    Local $iNumChrsToRead = 0 ; Number of chars to read
    Local $iTotalChrsRead = 0 ; Total chars currently read
    If _CoStatus($iPID) == "yielded" Then
        If StringLeft(StdoutRead($iPID, True), 0) > 5 Then
            $sReturnStr = StringTrimLeft(StdoutRead($iPID), 5)
            $iNumChrsToRead = StringRegExp($sReturnStr, "(\d*?)(?:\$\[)", 1)
            $sReturnStr = StringTrimLeft($sReturnStr, StringInStr($sReturnStr, $iNumChrsToRead[0]) + StringLen ( $iNumChrsToRead[0] ) - 1)
            While StringLen($sReturnStr) <= $iNumChrsToRead[0]
                $sReturnStr &= StdoutRead($iPID)
            WEnd
            StdinWrite($iPID, _PackVarToStr($vInVar))
            Return _UnpackStrToVar($sReturnStr)
        EndIf
    Else
        SetError(1) ;$PID is not yielded
        Return ""
    EndIf
EndFunc   ;==>_CoResume

;===============================================================================
;
; Function Name:    _CoGetReturn()
; Description:      Returns the variable returned by a coroutine
; Parameter(s):     $iPID - PID as returned by _CoStart()
; Return Value(s):  On Success - Returns variable returned by coroutine
;                   On Failure - Returns empty string ("") and sets error to 1
; Author(s):        Ben Brightwell
;
;===============================================================================
Func _CoGetReturn($iPID)
    Local $sReturnStr = "" ; Contains return string
    Local $iNumChrsToRead = 0 ; Number of chars to read
    Local $iTotalChrsRead = 0 ; Total chars currently read
    If _CoStatus($iPID) == "returned" Then
        $sReturnStr = StdoutRead($iPID)
        $iNumChrsToRead = StringRegExp($sReturnStr, "(\d*?)(?:\$\[)", 1)
        $sReturnStr = StringTrimLeft($sReturnStr, StringInStr($sReturnStr, $iNumChrsToRead[0]) + StringLen ( $iNumChrsToRead[0] ) - 1)
        While StringLen($sReturnStr) < $iNumChrsToRead[0]
            $sReturnStr &= StdoutRead($iPID)
        WEnd
        Return _UnpackStrToVar($sReturnStr)
    Else
        SetError(1) ; $PID not returned
        Return ""
    EndIf
EndFunc   ;==>_CoGetReturn

;===============================================================================
;
; Function Name:    _CoKill()
; Description:      Kills a running coroutine
; Parameter(s):     $iPID - PID as returned by _CoStart()
; Return Value(s):  On Success - Returns 1
;                   On Failure - Returns 0
; Author(s):        Ben Brightwell
;
;===============================================================================
Func _CoKill($iPID)
    If ProcessExists($iPID) <> 0 Then
        ProcessClose($iPID)
        Return 1
    Else
        Return 0
    EndIf
EndFunc   ;==>_CoKill

;===============================================================================
;
; Function Name:    _CoCleanup()
; Description:      Kills all coroutines and deletes their respective temp files
; Parameter(s):     none
; Return Value(s):  none
; Author(s):        Ben Brightwell
;
;===============================================================================
Func _CoCleanup()
    For $i = 1 To $avPIDs[0][0]
        _CoKill($avPIDs[$i][0])
    Next
    For $i = 1 To $avCoThreads[0][0]
        FileDelete($avCoThreads[$i][0])
    Next
EndFunc   ;==>_CoCleanup

;===============================================================================
;
; Function Name:    _CoChangeWorkingDir()
; Description:      Changes the directory to store coroutine temp files in
;                       NOTE: does not currently check for proper directory
;                       structure
; Parameter(s):     $sDir = String representing the new working directory
; Return Value(s):  none
; Author(s):        Ben Brightwell
;
;===============================================================================
Func _CoChangeWorkingDir($sDir)
    $sWorkingDir = $sDir
EndFunc   ;==>_CoChangeWorkingDir

;===============================================================================
;
; Function Name:    _UnpackStrToVar()
; Description:      Turns a packed variable string back into a variable.
; Parameter(s):     $sVarStr = A packed variable string as returned by _PackVarToStr()
; Return Value(s):  Variable of type that was passed to _PackVarToStr()
; Author(s):        Ben Brightwell
;
;===============================================================================
Func _UnpackStrToVar(ByRef $sVarStr)
    Local $aiNumDims = StringRegExp($sVarStr, '(?:\$\[)(\d*)(?:\]\$)', 1)
    Local $aiDimSizes[1] ; To contain the size of each dimension as passed through $sVarStr
    Local $aiDimSize = "" ; To contain the size of current dimension and string position for stripping
    Local $avRetArr[1] ; To be redimensioned and have $sVarStr parsed and stored into as an array
    Local $avElementStr = "" ; To contain each element as a string as it is parsed from $sVarStr
    If IsArray($aiNumDims) Then
        $sVarStr = StringTrimLeft($sVarStr, StringInStr($sVarStr, $aiNumDims[0] & "]$") + StringLen ( $aiNumDims[0] & "]$" ) - 1)
        If $aiNumDims[0] > 0 Then
            ReDim $aiDimSizes[$aiNumDims[0]]
            For $iCounter1 = 0 To $aiNumDims[0] - 1
                $aiDimSize = StringRegExp($sVarStr, '(?:\$\[)(\d*)(?:\]\$)', 1)
                $aiDimSizes[$iCounter1] = $aiDimSize[0]
                $sVarStr = StringTrimLeft($sVarStr, StringInStr($sVarStr, $aiDimSize[0] & "]$") + StringLen ( $aiDimSize[0] & "]$" ) - 1)
            Next
        EndIf
        Select
            Case $aiNumDims[0] == 0
                If StringInStr($sVarStr, "$[") Then
                    $avElementStr = StringRegExp($sVarStr, '(.*?)(?:\$\[)', 1)
                    $sVarStr = StringTrimLeft($sVarStr, StringInStr($sVarStr, $avElementStr[0]) + StringLen ( $avElementStr[0]) - 1)
                    If $avElementStr[0] <> "<nil>" Then
                        Return $avElementStr[0]
                    Else
                        Return ""
                    EndIf
                Else
                    If $sVarStr == "<nil>" Then
                        Return ""
                    Else
                        Return $sVarStr
                    EndIf
                EndIf
            Case $aiNumDims[0] == 1
                ReDim $avRetArr[$aiDimSizes[0]]
                For $iCounter1 = 0 To $aiDimSizes[0] - 1
                    $avRetArr[$iCounter1] = _UnpackStrToVar($sVarStr) ; In case element holds another array (Recursive)
                Next
            Case $aiNumDims[0] == 2
                ReDim $avRetArr[$aiDimSizes[0]][$aiDimSizes[1]]
                For $iCounter1 = 0 To $aiDimSizes[0] - 1
                    For $iCounter2 = 0 To $aiDimSizes[1] - 1
                        $avRetArr[$iCounter1][$iCounter2] = _UnpackStrToVar($sVarStr) ; In case element holds another array (Recursive)
                    Next
                Next
            Case $aiNumDims[0] == 3
                ReDim $avRetArr[$aiDimSizes[0]][$aiDimSizes[1]][$aiDimSizes[2]]
                For $iCounter1 = 0 To $aiDimSizes[0] - 1
                    For $iCounter2 = 0 To $aiDimSizes[1] - 1
                        For $iCounter3 = 0 To $aiDimSizes[2] - 1
                            $avRetArr[$iCounter1][$iCounter2][$iCounter3] = _UnpackStrToVar($sVarStr) ; In case element holds another array (Recursive)
                        Next
                    Next
                Next
            Case $aiNumDims[0] == 4
                ReDim $avRetArr[$aiDimSizes[0]][$aiDimSizes[1]][$aiDimSizes[2]][$aiDimSizes[3]]
                For $iCounter1 = 0 To $aiDimSizes[0] - 1
                    For $iCounter2 = 0 To $aiDimSizes[1] - 1
                        For $iCounter3 = 0 To $aiDimSizes[2] - 1
                            For $iCounter4 = 0 To $aiDimSizes[3] - 1
                                $avRetArr[$iCounter1][$iCounter2][$iCounter3][$iCounter4] = _UnpackStrToVar($sVarStr) ; In case element holds another array (Recursive)
                            Next
                        Next
                    Next
                Next
        EndSelect
        Return $avRetArr
    EndIf
EndFunc   ;==>_UnpackStrToVar

;===============================================================================
;
; Function Name:    _PackVarToStr()
; Description:      Packs a variable into a string form for sending to another
;                       script. Maintains array integrity up to 4 dimensions.
;                       Also supports nested arrays inside array elements.
; Parameter(s):     $vPackVar = Variable to be packed into a string.
;                       NOTE: Variables of type Object or DllStructs are not yet
;                       supported.
; Return Value(s):  Returns packed string to be unpacked by _UnpackStrToVar()
; Author(s):        Ben Brightwell
;
;===============================================================================
Func _PackVarToStr(ByRef $vPackVar)
    Local $iNumDims = UBound($vPackVar, 0) ; Number of dimensions of $vPackVar
    Local $sVarStr = "$[" & $iNumDims & "]$" ; Return string of packed variable
    Local $iCounter1 = "" ; Nested Counter
    Local $iCounter2 = "" ; Nested Counter
    Local $iCounter3 = "" ; Nested Counter
    Local $iCounter4 = "" ; Nested Counter
    For $i = 1 To $iNumDims
        $sVarStr &= "$[" & UBound($vPackVar, $i) & "]$"
    Next
    Select
        Case $iNumDims == 0
            If $vPackVar == "" Then
                $sVarStr &= "<nil>"
            Else
                $sVarStr &= $vPackVar
            EndIf
        Case $iNumDims == 1
            For $iCounter1 = 0 To UBound($vPackVar, 1) - 1
                If $vPackVar[$iCounter1] == "" And UBound($vPackVar[$iCounter1], 0) == 0 Then
                    $sVarStr &= "$[0]$<nil>"
                Else
                    $sVarStr &= _PackVarToStr($vPackVar[$iCounter1])
                EndIf
            Next
        Case $iNumDims == 2
            For $iCounter1 = 0 To UBound($vPackVar, 1) - 1
                For $iCounter2 = 0 To UBound($vPackVar, 2) - 1
                    If $vPackVar[$iCounter1][$iCounter2] == "" And UBound($vPackVar[$iCounter1][$iCounter2], 0) == 0 Then
                        $sVarStr &= "$[0]$<nil>"
                    Else
                        $sVarStr &= _PackVarToStr($vPackVar[$iCounter1][$iCounter2])
                    EndIf
                Next
            Next
        Case $iNumDims == 3
            For $iCounter1 = 0 To UBound($vPackVar, 1) - 1
                For $iCounter2 = 0 To UBound($vPackVar, 2) - 1
                    For $iCounter3 = 0 To UBound($vPackVar, 3) - 1
                        If $vPackVar[$iCounter1][$iCounter2][$iCounter3] == "" And UBound($vPackVar[$iCounter1][$iCounter2][$iCounter3], 0) == 0 Then
                            $sVarStr &= "$[0]$<nil>"
                        Else
                            $sVarStr &= _PackVarToStr($vPackVar[$iCounter1][$iCounter2][$iCounter3])
                        EndIf
                    Next
                Next
            Next
        Case $iNumDims == 4
            For $iCounter1 = 0 To UBound($vPackVar, 1) - 1
                For $iCounter2 = 0 To UBound($vPackVar, 2) - 1
                    For $iCounter3 = 0 To UBound($vPackVar, 3) - 1
                        For $iCounter4 = 0 To UBound($vPackVar, 4) - 1
                            If $vPackVar[$iCounter1][$iCounter2][$iCounter3][$iCounter4] == "" And UBound($vPackVar[$iCounter1][$iCounter2][$iCounter3][$iCounter4], 0) == 0 Then
                                $sVarStr &= "$[0]$<nil>"
                            Else
                                $sVarStr &= _PackVarToStr($vPackVar[$iCounter1][$iCounter2][$iCounter3][$iCounter4])
                            EndIf
                        Next
                    Next
                Next
            Next
    EndSelect
    Return $sVarStr
EndFunc   ;==>_PackVarToStr

;===============================================================================
;
; Function Name:    _LineIsFunction()
; Description:      Classifies the given line as either the start/end of a function, or as a regular line.
;       Uses regular expressions to match every possible scenario.
; Parameter(s):     $vLine = The string to be classified.
; Return Value(s):  On Success - 1, this line is the start/end of a function. Sets @Error to:
;                           1 - Line is the start of a function
;                           2 - Line is the end of a function
;                       On Failure - 0, this line isn't a function.
; Author(s):        Chris H. (darkthorn)
;
;===============================================================================
Func _LineIsFunction($vLine)
    ; Classify the type of line. First check if this line is an EndFunc.
    If StringRegExp($vLine, "^\s*(?i:EndFunc)", 0) Then
        ; Success. Pattern Matched. This is the end of a function.
        SetError(2) ; line is endfunc
        Return 1 ; success
    Else
        ; Failure. Pattern not matched. This is not the end of a function. So let's check if it's the start of a function.
        If StringRegExp($vLine, "^\s*(?i:Func)\s+\S+\s*\(.*\)", 0) Then
            ; Success. Pattern Matched. This is the start of a function.
            SetError(1) ; line is func
            Return 1 ; success
        Else
            ; Failure. Pattern not matched. Since this wasn't the start of a function either, treat it as a normal line.
            SetError(0) ; line isn't function
            Return 0 ; failure
        EndIf
    EndIf
EndFunc   ;==>_LineIsFunction

;===============================================================================
;
; Function Name:    _RandomFileName()
; Description:      Generates a random file name in the working directory so that
;                       the a filename is not repeated for up to 10000 coroutine
;                       scripts per main script.
; Parameter(s):     none
; Return Value(s):  FileGetShortName-converted string representing the full path
;                       to the temporary file, including the .au3 extension
; Author(s):        Ben Brightwell
;
;===============================================================================
Func _RandomFileName()
    $name = FileGetShortName($sWorkingDir) & "\" & StringTrimRight(@ScriptName, 4) & Random(0, 10000, 1) & ".au3"
    $found = 0
    For $i = 1 To $avCoThreads[0][0]
        If $name == $avCoThreads[$i][0] Then
            $found = 1
        EndIf
    Next
    While $found == 1
        $found = 0
        $name = FileGetShortName($sWorkingDir) & "\" & StringTrimRight(@ScriptName, 4) & Random(0, 10000, 1) & ".au3"
        For $i = 1 To $avCoThreads[0][0]
            If $name == $avCoThreads[$i][0] Then
                $found = 1
            EndIf
        Next
    WEnd
    Return $name
EndFunc   ;==>_RandomFileName
Edited by ParoXsitiC
Link to comment
Share on other sites

Ok, so I lied. I tested it with the included "Arrays.au3" Located here:

#include "Coroutine.au3"

HotKeySet("{END}", "_exit")

;Just setting up the test array
Dim $testarray[5]
Dim $sub1[8]
$sub1[0] = "test1"
$sub1[1] = "test2"
$sub1[2] = "test3"
$sub1[3] = "test4"
$sub1[4] = "test5"
$sub1[5] = "test6"
$sub1[6] = "test7"
$sub1[7] = "test8"
Dim $sub2[8]
$sub2[0] = "Elements can hold strings"
$sub2[1] = "with spaces           "
$sub2[2] = "and punctuation.!@#$%^&*()"
$sub2[3] = "Elements can also be null"
$sub2[4] = ""
$sub2[5] = ""
$sub2[6] = "End of"
$sub2[7] = "sub2 array"
$testarray[0] = "Beginning of Main Array"
$testarray[1] = "Contains 2 nested arrays"
$testarray[2] = $sub1
;$testarray[3] = $sub2 ... $sub2 will be placed here in the thread and passed back
$testarray[4] = "End of Main Array"

;Creates the thread using the default delimiter "|" and stores the thread ID in $thread
$thread = _CoCreate('Func function($arr1, $arr2)|$arr1[3] = $arr2|Return $arr1|EndFunc')

;Starts an instance of the thread
$pid = _CoStart($thread, '$testarray, $sub2')

;This loop waits until the coroutine returns whatever it's supposed to return, then displays the results using
;   another UDF I created: _ArrayToDisplayString() which handles up to 4-dimensional arrays and nested arrays
While 1
    If _CoStatus($pid) == "returned" Then
        MsgBox(0,"Main Thread", _ArrayToDisplayString(_CoGetReturn($pid)))
        ExitLoop
    EndIf
    Sleep(10)
WEnd

;This must be called to clean up the temp files that were created with _CoCreate()
_CoCleanup()

Func _exit()
    Exit
EndFunc





;===============================================================================
;
; Function Name:    _ArrayToDisplayString()
; Description:      Converts an Array of up to 4 dimensions into a string
;                       displaying the contents of the array.
;                       NOTE: now supports nested arrays
; Parameter(s):     $vDispVar - Array to convert
;                   $iStyle[optional] - 0 = Cascading style (Default)
;                   $iLevel - used for recursion, DO NOT CHANGE
; Return Value(s):  If array is passed - String representing array
;                   If non-array is passed - String representing variable
; Author(s):        Ben Brightwell
;
;===============================================================================
Func _ArrayToDisplayString($vDispVar, $iStyle = 0, $iLevel = 0)
    If $iStyle <> 0 And $iStyle <> 1 Then
        $iStyle = 0
    EndIf
    $iNumDims = UBound($vDispVar, 0) ; Number of dimensions of $vDispVar
    Local $sDisplayStr = ""
    Local $iDimCounter = "" ; Main Dimension Counter
    Local $iCounter1 = "" ; Nested Counter
    Local $iCounter2 = "" ; Nested Counter
    Local $iCounter3 = "" ; Nested Counter
    Local $iCounter4 = "" ; Nested Counter
    Select
    Case $iNumDims == 1
        For $iCounter1 = 0 To UBound($vDispVar, 1) - 1
            If $iLevel > 0 Then
                For $i = 1 To $iLevel
                    $sDisplayStr &= "     "
                Next
            EndIf
            If IsArray($vDispVar[$iCounter1]) Then
                $sDisplayStr &= "[" & $iCounter1 & "]= " & @CRLF
            Else
                $sDisplayStr &= "[" & $iCounter1 & "]= "
            EndIf
            $sDisplayStr &= _ArrayToDisplayString($vDispVar[$iCounter1], $iStyle, $iLevel+1)
        Next
    Case $iNumDims == 2
        For $iCounter1 = 0 To UBound($vDispVar, 1) - 1
            If $iStyle == 0 Then
                For $i = 1 To $iLevel
                    $sDisplayStr &= "     "
                Next
                $sDisplayStr &= "[" & $iCounter1 & "]= " & @CRLF
            EndIf
            For $iCounter2 = 0 To UBound($vDispVar, 2) - 1
                If $iStyle == 0 Then
                    $sDisplayStr &= "     "
                EndIf
                If $iLevel > 0 Then
                    For $i = 1 To $iLevel
                        $sDisplayStr &= "     "
                    Next
                EndIf
                If $iStyle = 0 Then
                    If IsArray($vDispVar[$iCounter1][$iCounter2]) Then
                        $sDisplayStr &= "[" & $iCounter2 & "]= " & @CRLF
                    Else
                        $sDisplayStr &= "[" & $iCounter2 & "]= "
                    EndIf
                ElseIf $iStyle == 1 Then
                    If IsArray($vDispVar[$iCounter1][$iCounter2]) Then
                        $sDisplayStr &= "[" & $iCounter1 & "][" & $iCounter2 & "]= " & @CRLF
                    Else
                        $sDisplayStr &= "[" & $iCounter1 & "][" & $iCounter2 & "]= "
                    EndIf
                EndIf
                $sDisplayStr &= _ArrayToDisplayString($vDispVar[$iCounter1][$iCounter2], $iStyle, $iLevel+1)
            Next
        Next
    Case $iNumDims == 3
        For $iCounter1 = 0 To UBound($vDispVar, 1) - 1
            If $iStyle == 0 Then
                For $i = 1 To $iLevel
                    $sDisplayStr &= "     "
                Next
                $sDisplayStr &= "[" & $iCounter1 & "]= " & @CRLF
            EndIf
            For $iCounter2 = 0 To UBound($vDispVar, 2) - 1
                If $iStyle == 0 Then
                    For $i = 1 To $iLevel
                        $sDisplayStr &= "     "
                    Next
                    $sDisplayStr &= "     [" & $iCounter2 & "]= " & @CRLF
                EndIf
                For $iCounter3 = 0 To UBound($vDispVar, 3) - 1
                    If $iStyle == 0 Then
                        $sDisplayStr &= "          "
                    EndIf
                    If $iLevel > 0 Then
                        For $i = 1 To $iLevel
                            $sDisplayStr &= "     "
                        Next
                    EndIf
                    If $iStyle == 0 Then
                        If IsArray($vDispVar[$iCounter1][$iCounter2][$iCounter3]) Then
                            $sDisplayStr &= "[" & $iCounter3 & "]= " & @CRLF
                        Else
                            $sDisplayStr &= "[" & $iCounter3 & "]= "
                        EndIf
                    ElseIf $iStyle == 1 Then
                        If IsArray($vDispVar[$iCounter1][$iCounter2][$iCounter3]) Then
                            $sDisplayStr &= "[" & $iCounter1 & "][" & $iCounter2 & "][" & $iCounter3 & "]= " & @CRLF
                        Else
                            $sDisplayStr &= "[" & $iCounter1 & "][" & $iCounter2 & "][" & $iCounter3 & "]= "
                        EndIf
                    EndIf
                    $sDisplayStr &= _ArrayToDisplayString($vDispVar[$iCounter1][$iCounter2][$iCounter3], $iStyle, $iLevel+1)
                Next
            Next
        Next
    Case $iNumDims == 4
        For $iCounter1 = 0 To UBound($vDispVar, 1) - 1
            If $iStyle == 0 Then
                For $i = 1 To $iLevel
                    $sDisplayStr &= "     "
                Next
                $sDisplayStr &= "[" & $iCounter1 & "]= " & @CRLF
            EndIf
            For $iCounter2 = 0 To UBound($vDispVar, 2) - 1
                If $iStyle == 0 Then
                    For $i = 1 To $iLevel
                        $sDisplayStr &= "     "
                    Next
                    $sDisplayStr &= "     [" & $iCounter2 & "]= " & @CRLF
                EndIf
                For $iCounter3 = 0 To UBound($vDispVar, 3) - 1
                    If $iStyle == 0 Then
                        For $i = 1 To $iLevel
                            $sDisplayStr &= "     "
                        Next
                        $sDisplayStr &= "          [" & $iCounter3 & "]= " & @CRLF
                    EndIf
                    For $iCounter4 = 0 To UBound($vDispVar, 4) - 1
                        If $iStyle == 0 Then
                            $sDisplayStr &= "               "
                        EndIf
                        If $iLevel > 0 Then
                            For $i = 1 To $iLevel
                                $sDisplayStr &= "     "
                            Next
                        EndIf
                        If $iStyle == 0 Then
                            If IsArray($vDispVar[$iCounter1][$iCounter2][$iCounter3][$iCounter4]) Then
                                $sDisplayStr &= "[" & $iCounter4 & "]= " & @CRLF
                            Else
                                $sDisplayStr &= "[" & $iCounter4 & "]= "
                            EndIf
                        ElseIf $iStyle == 1 Then
                            If IsArray($vDispVar[$iCounter1][$iCounter2][$iCounter3][$iCounter4]) Then
                                $sDisplayStr &= "[" & $iCounter1 & "][" & $iCounter2 & "][" & $iCounter3 & "][" & $iCounter4 & "]= " & @CRLF
                            Else
                                $sDisplayStr &= "[" & $iCounter1 & "][" & $iCounter2 & "][" & $iCounter3 & "][" & $iCounter4 & "]= "
                            EndIf
                        EndIf
                        $sDisplayStr &= _ArrayToDisplayString($vDispVar[$iCounter1][$iCounter2][$iCounter3][$iCounter4], $iStyle, $iLevel+1)
                    Next
                Next
            Next
        Next
    EndSelect
    If $iNumDims == 0 Then
        $sDisplayStr &= $vDispVar & @CRLF
    EndIf
    Return $sDisplayStr
EndFunc

Which seems to output fine. However, when I try to use example MultiDimensionalArrays.au3 Located here:

#include "Coroutine.au3"

HotKeySet("{END}", "_exit")

;Testing 1-Dimensional Array
Dim $test1dim[2]
$test1dim[0] = "test1"
$test1dim[1] = "test2"

;Testing 2-Dimensional Array
Dim $test2dim[2][2]
$test2dim[0][0] = "test1"
$test2dim[0][1] = "test2"
$test2dim[1][0] = "test3"
$test2dim[1][1] = "test4"

;Testing 3-Dimensional Array
Dim $test3dim[2][2][2]
$test3dim[0][0][0] = "test1"
$test3dim[0][0][1] = "test2"
$test3dim[0][1][0] = "test3"
$test3dim[0][1][1] = "test4"
$test3dim[1][0][0] = "test5"
$test3dim[1][0][1] = "test6"
$test3dim[1][1][0] = "test7"
$test3dim[1][1][1] = "test8"

;Testing 4-Dimensional Array
Dim $test4dim[3][2][2][2]
$test4dim[0][0][0][0] = "test1"
$test4dim[0][0][0][1] = "test2"
$test4dim[0][0][1][0] = "test3"
$test4dim[0][0][1][1] = "test4"
$test4dim[0][1][0][0] = "test5"
$test4dim[0][1][0][1] = "test6"
$test4dim[0][1][1][0] = "test7"
$test4dim[0][1][1][1] = "test8"
$test4dim[1][0][0][0] = "test9"
$test4dim[1][0][0][1] = "test0"
$test4dim[1][0][1][0] = "test11"
$test4dim[1][0][1][1] = "test12"
$test4dim[1][1][0][0] = "test13"
$test4dim[1][1][0][1] = "test14"
$test4dim[1][1][1][0] = "test15"
$test4dim[1][1][1][1] = "test16"
$test4dim[2][0][0][0] = "test17"
$test4dim[2][0][0][1] = "test18"
$test4dim[2][0][1][0] = "test19"
$test4dim[2][0][1][1] = "test20"
$test4dim[2][1][0][0] = "test21"
$test4dim[2][1][0][1] = "test22"
$test4dim[2][1][1][0] = "test23"
$test4dim[2][1][1][1] = "test24"


;Creates the thread using the default delimiter "|" and stores the thread ID in $thread
$thread = _CoCreate('Func function($arr1)|Return $arr1|EndFunc')

;Starts an instance of the thread
$pid1 = _CoStart($thread, '$test1dim')
$pid2 = _CoStart($thread, '$test2dim')
$pid3 = _CoStart($thread, '$test3dim')
$pid4 = _CoStart($thread, '$test4dim')

;This loop waits until the coroutine returns whatever it's supposed to return, then displays the results using
;   another UDF I created: _ArrayToDisplayString() which handles up to 4-dimensional arrays and nested arrays
$numFinished = 0
While $numFinished < 4
    If _CoStatus($pid1) == "returned" Then
        MsgBox(0,"1-Dimensional", _ArrayToDisplayString(_CoGetReturn($pid1)))
        $numFinished += 1
    EndIf
    If _CoStatus($pid2) == "returned" Then
        MsgBox(0,"2-Dimensional", _ArrayToDisplayString(_CoGetReturn($pid2)))
        $numFinished += 1
    EndIf
    If _CoStatus($pid3) == "returned" Then
        MsgBox(0,"3-Dimensional", _ArrayToDisplayString(_CoGetReturn($pid3)))
        $numFinished += 1
    EndIf
    If _CoStatus($pid4) == "returned" Then
        MsgBox(0,"4-Dimensional", _ArrayToDisplayString(_CoGetReturn($pid4)))
        $numFinished += 1
    EndIf
    Sleep(10)
WEnd

;This must be called to clean up the temp files that were created with _CoCreate()
_CoCleanup()

Func _exit()
    Exit
EndFunc








;===============================================================================
;
; Function Name:    _ArrayToDisplayString()
; Description:      Converts an Array of up to 4 dimensions into a string
;                       displaying the contents of the array.
;                       NOTE: now supports nested arrays
; Parameter(s):     $vDispVar - Array to convert
;                   $iStyle[optional] - 0 = Cascading style (Default)
;                   $iLevel - used for recursion, DO NOT CHANGE
; Return Value(s):  If array is passed - String representing array
;                   If non-array is passed - String representing variable
; Author(s):        Ben Brightwell
;
;===============================================================================
Func _ArrayToDisplayString($vDispVar, $iStyle = 0, $iLevel = 0)
    If $iStyle <> 0 And $iStyle <> 1 Then
        $iStyle = 0
    EndIf
    $iNumDims = UBound($vDispVar, 0) ; Number of dimensions of $vDispVar
    Local $sDisplayStr = ""
    Local $iDimCounter = "" ; Main Dimension Counter
    Local $iCounter1 = "" ; Nested Counter
    Local $iCounter2 = "" ; Nested Counter
    Local $iCounter3 = "" ; Nested Counter
    Local $iCounter4 = "" ; Nested Counter
    Select
    Case $iNumDims == 1
        For $iCounter1 = 0 To UBound($vDispVar, 1) - 1
            If $iLevel > 0 Then
                For $i = 1 To $iLevel
                    $sDisplayStr &= "     "
                Next
            EndIf
            If IsArray($vDispVar[$iCounter1]) Then
                $sDisplayStr &= "[" & $iCounter1 & "]= " & @CRLF
            Else
                $sDisplayStr &= "[" & $iCounter1 & "]= "
            EndIf
            $sDisplayStr &= _ArrayToDisplayString($vDispVar[$iCounter1], $iStyle, $iLevel+1)
        Next
    Case $iNumDims == 2
        For $iCounter1 = 0 To UBound($vDispVar, 1) - 1
            If $iStyle == 0 Then
                For $i = 1 To $iLevel
                    $sDisplayStr &= "     "
                Next
                $sDisplayStr &= "[" & $iCounter1 & "]= " & @CRLF
            EndIf
            For $iCounter2 = 0 To UBound($vDispVar, 2) - 1
                If $iStyle == 0 Then
                    $sDisplayStr &= "     "
                EndIf
                If $iLevel > 0 Then
                    For $i = 1 To $iLevel
                        $sDisplayStr &= "     "
                    Next
                EndIf
                If $iStyle = 0 Then
                    If IsArray($vDispVar[$iCounter1][$iCounter2]) Then
                        $sDisplayStr &= "[" & $iCounter2 & "]= " & @CRLF
                    Else
                        $sDisplayStr &= "[" & $iCounter2 & "]= "
                    EndIf
                ElseIf $iStyle == 1 Then
                    If IsArray($vDispVar[$iCounter1][$iCounter2]) Then
                        $sDisplayStr &= "[" & $iCounter1 & "][" & $iCounter2 & "]= " & @CRLF
                    Else
                        $sDisplayStr &= "[" & $iCounter1 & "][" & $iCounter2 & "]= "
                    EndIf
                EndIf
                $sDisplayStr &= _ArrayToDisplayString($vDispVar[$iCounter1][$iCounter2], $iStyle, $iLevel+1)
            Next
        Next
    Case $iNumDims == 3
        For $iCounter1 = 0 To UBound($vDispVar, 1) - 1
            If $iStyle == 0 Then
                For $i = 1 To $iLevel
                    $sDisplayStr &= "     "
                Next
                $sDisplayStr &= "[" & $iCounter1 & "]= " & @CRLF
            EndIf
            For $iCounter2 = 0 To UBound($vDispVar, 2) - 1
                If $iStyle == 0 Then
                    For $i = 1 To $iLevel
                        $sDisplayStr &= "     "
                    Next
                    $sDisplayStr &= "     [" & $iCounter2 & "]= " & @CRLF
                EndIf
                For $iCounter3 = 0 To UBound($vDispVar, 3) - 1
                    If $iStyle == 0 Then
                        $sDisplayStr &= "          "
                    EndIf
                    If $iLevel > 0 Then
                        For $i = 1 To $iLevel
                            $sDisplayStr &= "     "
                        Next
                    EndIf
                    If $iStyle == 0 Then
                        If IsArray($vDispVar[$iCounter1][$iCounter2][$iCounter3]) Then
                            $sDisplayStr &= "[" & $iCounter3 & "]= " & @CRLF
                        Else
                            $sDisplayStr &= "[" & $iCounter3 & "]= "
                        EndIf
                    ElseIf $iStyle == 1 Then
                        If IsArray($vDispVar[$iCounter1][$iCounter2][$iCounter3]) Then
                            $sDisplayStr &= "[" & $iCounter1 & "][" & $iCounter2 & "][" & $iCounter3 & "]= " & @CRLF
                        Else
                            $sDisplayStr &= "[" & $iCounter1 & "][" & $iCounter2 & "][" & $iCounter3 & "]= "
                        EndIf
                    EndIf
                    $sDisplayStr &= _ArrayToDisplayString($vDispVar[$iCounter1][$iCounter2][$iCounter3], $iStyle, $iLevel+1)
                Next
            Next
        Next
    Case $iNumDims == 4
        For $iCounter1 = 0 To UBound($vDispVar, 1) - 1
            If $iStyle == 0 Then
                For $i = 1 To $iLevel
                    $sDisplayStr &= "     "
                Next
                $sDisplayStr &= "[" & $iCounter1 & "]= " & @CRLF
            EndIf
            For $iCounter2 = 0 To UBound($vDispVar, 2) - 1
                If $iStyle == 0 Then
                    For $i = 1 To $iLevel
                        $sDisplayStr &= "     "
                    Next
                    $sDisplayStr &= "     [" & $iCounter2 & "]= " & @CRLF
                EndIf
                For $iCounter3 = 0 To UBound($vDispVar, 3) - 1
                    If $iStyle == 0 Then
                        For $i = 1 To $iLevel
                            $sDisplayStr &= "     "
                        Next
                        $sDisplayStr &= "          [" & $iCounter3 & "]= " & @CRLF
                    EndIf
                    For $iCounter4 = 0 To UBound($vDispVar, 4) - 1
                        If $iStyle == 0 Then
                            $sDisplayStr &= "               "
                        EndIf
                        If $iLevel > 0 Then
                            For $i = 1 To $iLevel
                                $sDisplayStr &= "     "
                            Next
                        EndIf
                        If $iStyle == 0 Then
                            If IsArray($vDispVar[$iCounter1][$iCounter2][$iCounter3][$iCounter4]) Then
                                $sDisplayStr &= "[" & $iCounter4 & "]= " & @CRLF
                            Else
                                $sDisplayStr &= "[" & $iCounter4 & "]= "
                            EndIf
                        ElseIf $iStyle == 1 Then
                            If IsArray($vDispVar[$iCounter1][$iCounter2][$iCounter3][$iCounter4]) Then
                                $sDisplayStr &= "[" & $iCounter1 & "][" & $iCounter2 & "][" & $iCounter3 & "][" & $iCounter4 & "]= " & @CRLF
                            Else
                                $sDisplayStr &= "[" & $iCounter1 & "][" & $iCounter2 & "][" & $iCounter3 & "][" & $iCounter4 & "]= "
                            EndIf
                        EndIf
                        $sDisplayStr &= _ArrayToDisplayString($vDispVar[$iCounter1][$iCounter2][$iCounter3][$iCounter4], $iStyle, $iLevel+1)
                    Next
                Next
            Next
        Next
    EndSelect
    If $iNumDims == 0 Then
        $sDisplayStr &= $vDispVar & @CRLF
    EndIf
    Return $sDisplayStr
EndFunc

It gives me weird and intermittent results. I am no programmer, my fixes just included little "hacks" to get it to work. Obviously im not doing something right. Here is an idea of the hacks I did, so that someone can understand;

I changed all ConsoleRead(0,true) to ConsoleRead(true)

When using $iNumChrsToRead = StringRegExp($sParamsInStr, "(\d*?)(\#)(?:\$\[)", 1) I understood the supposed 2nd match was a length of the first match, there I changed it to $iNumChrsToRead= StringRegExp($sParamsInStr, "(\d*?)(?:\$\[)", 1) and instead of using the old $iNumChrsToRead[1] I would use StringInStr($sParamsInStr, $iNumChrsToRead[0]) + StringLen ( $iNumChrsToRead[0] ) - 1

Regexs like (?i:return[:space:]*) gave me an error so i replaced them with (?i:return\s*)

$asParams = StringRegExp($sParamStr, '(".*?".*?,|[^ ]*?[ ]?,)*', 3) changed to $asParams = StringRegExp($sParamStr, '(".*?".*?,|[^ ]*?[ ]?,)+', 3) ..... I forgot why.

Occurrence of things like StdoutRead($iPID, 5, True) where it seemed the old function means to take the first 5 chars from the left, was changed to StringLeft(StdoutRead($iPID, True), 5) ...... I think there is where my problem is, because he has IF statements like: If StdoutRead($iPID, 0, True) <> 0 Then ... and StringLeft(StdoutRead($iPID, True), 0) wouldnt make sense there....

Anyways... I am noob, will someone who knows what they are doing please take my notes and turn

Coroutine 1.1.0 Found here: download into a working model on today's autoit

Link to comment
Share on other sites

As I guessed it was StdoutRead...

I suppose the old StdoutRead if you had StdoutRead($iPID, 0, True) then it was checking if it was done reading... So I changed it from If StdoutRead($iPID, 0, True) <> 0 Then to StdoutRead($iPID)

$StdoutReadIsDone = @error

If $StdoutReadIsDone Then

I am not sure how to do If StringLeft(StdoutRead($iPID, True), 0) > 5 Then for yielding.

Does autoit have old documentation anywhere?

Edited by ParoXsitiC
Link to comment
Share on other sites

  • 3 years later...


Hi, Neogia, Ben Brightwell, ChrisL. & ParoXsitiC 
I expect I don’t miss to name the authors of this solution. (just let me know)
Its wonderful!!  Thanks

Anyways I think there is needed to explain how to use this multitreading solution. Today ill explain the basics.(I spent more than 4 hours to figure out how to use it). Im sure that there are some things that I miss, but . this works for me.
 I used the code for Coroutine.au3 1.1.1 .  published in the forum by ParoXsitiC at http://www.autoitscript.com/forum/topic/23545-coroutine-multithreading-udf-library/?p=815988
; File: Coroutine.au3
; Description: UDF Library used for coroutine multithreading
; Version: 1.1.1
; Date: 8/13/07

 1. Write your code that you want to run as a tread, as a function.

a. I didn’t test yet,  having in the function calls for other functions ( this is for the next post)

2.Use the code FormatCoCreate.au3 to prepeare the tread function

this is the code i used ( i dont like the extra white lines that the original script creates)

a. run the FormatCoCreate.au3 .

b. select the function for the tread

c: use the keys CRTL + SHIFT + C

d. the code will be formated for the use with corutine ( this is every line is ended by a delimiter ( in this case , the pipe symbol)

 

 

HotKeySet("^+c", "_FormatCoCreate")
HotKeySet("{END}", "_exit")

While 1
 Sleep(50)
WEnd

Func _FormatCoCreate()
 Send("^c")
 Sleep(500)
 $rawText = ClipGet()
 If StringInStr($rawText, "$[") Then
  MsgBox(0,"Error","Function cannot contain the special delimiter: $[" & @CRLF & "Please replace text and try again.")
 EndIf
 If StringInStr($rawText, "|") Then
  If StringInStr($rawText, "~{[]}~") Then
   $delim = InputBox('Input custom delimiter, as both "|" and "~{[]}~" exist in your function', "Delimiter")
  Else
   $delim = "~{[]}~"
  EndIf
 Else
  $delim = "|"
 EndIf
;~  $replacedText = StringReplace($rawText, @CRLF, $delim)
 $replacedText = ""
 $splitText = StringSplit($rawText, @CRLF)
 For $lineCount = 1 To $splitText[0]
  $textParts = StringRegExp($splitText[$lineCount], '(\s*)(.*)', 1)
  If IsArray($textParts) And UBound($textParts) == 2 Then
   if $textParts[1]<>"" then    ;to eliminar las lineas blancas
    $replacedText &= $textParts[0] & "'" & $textParts[1] & $delim & "'"
    If $lineCount <> $splitText[0] Then
     $replacedText &= " & _" & @CRLF
    EndIf
   endif   ;to eliminar las lineas blancas
  ElseIf IsArray($textParts) And UBound($textParts) == 1 Then
   $replacedText &= "'" & $textParts[0] & $delim & "'"
   If $lineCount <> $splitText[0] Then
    $replacedText &= " & _" & @CRLF
   EndIf
  EndIf
 Next
 If $delim <> "|" Then
  $setText = '$ThreadName = _CoCreate(' & $replacedText & ', ''' & $delim & ''')'
 Else
  $setText = '$YourThreadNameHere = _CoCreate(' & $replacedText & ')'
 EndIf
 ClipPut($setText)
 Send("^v")
EndFunc

Func _exit()
 Exit
EndFunc

 

 

3. copy the prepared code to a new file (in this example Tread.au3)

a. ( this file will be included in your code)

b. Ex.   $ThreadName = _CoCreate (your code)

4. In this new file Tread.au3 , add the _CoInclude command to add all the includes you nned

a. Every UDF must be included

b. EX.     _CoInclude($ThreadName,"#include <WinAPI.au3>")

5. In the new file Tread.au3 ,add the _CoStart  function to start the tread

a. EX.      $Threadpid = _CoStart($LynckThreadName, '')

6. Back to your Script

a. Add the    #include <Coroutine.au3>

b. Add the    #include <Tread.au3>  to call the tread an start it.

7. You can monitor the tread activity status from your main code.

ConsoleWrite("!!!!" & _CoStatus($Threadpid) & @crlf)

If _CoStatus($LynckThreadpid) == "returned" Then

ConsoleWrite(">>>>" & _CoStatus(Threadpid) & @crlf)

_CoCleanup() 

endif

 

 I keep working on the use of this Coroutine UDF , and i post my findings.

i ll expect this helps you

 

Thanks again for the great solution.







			
				


	Edited  by Marcelos
	
	

			
		
Link to comment
Share on other sites

@Marcelos - Welcome to the forum.

I guess your deliberations, code, etc might help someone, but you may not have noticed, that the last update to this UDF was early 2006. The OP hasn't been active here in about 3 years and some of the others (maybe all) haven't been around for some time.

So it is possible, that with newer versions of AutoIt since then, that there may be more current/better similar UDF's out there?

Cheers!

Make sure brain is in gear before opening mouth!
Remember, what is not said, can be just as important as what is said.

Spoiler

What is the Secret Key? Life is like a Donut

If I put effort into communication, I expect you to read properly & fully, or just not comment.
Ignoring those who try to divert conversation with irrelevancies.
If I'm intent on insulting you or being rude, I will be obvious, not ambiguous about it.
I'm only big and bad, to those who have an over-active imagination.

I may have the Artistic Liesense ;) to disagree with you. TheSaint's Toolbox (be advised many downloads are not working due to ISP screwup with my storage)

userbar.png

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...