      PROCEDURE MyThreadFunc(p2)    && p2 is the 2nd param to MyDoCmd
            TRY   && use exception handling
                  DECLARE integer GetCurrentThreadId in WIN32API
                  DECLARE integer PostMessage IN WIN32API integer hWnd, integer nMsg, integer wParam, integer lParam
                  cPath=SUBSTR(p2,AT(",",p2)+1)
                  hWnd=INT(VAL(p2))
                  CREATEOBJECT("SearchDisk",cPath)
                  PostMessage(hWnd, 0x400, 0, GetCurrentThreadId())   && Tell main thread we're just about done!
            CATCH TO oex
                  INSERT INTO C:\EDDY\GHOST\THREADLOG.DBF VALUES (GetCurrentThreadId(), DATETIME(),p2+" Error: "+oex.message+" "+oex.details+" "+TRANSFORM(oex.lineno))
            ENDTRY
DEFINE CLASS SearchDisk as Session
      hAbortEvent=0
      PROCEDURE init(cPath)
            DECLARE integer CreateEvent IN WIN32API integer lpEventAttributes, integer bManualReset, integer bInitialState, string lpName
            DECLARE integer WaitForSingleObject IN WIN32API integer hHandle, integer dwMilliseconds
            DECLARE integer GetLastError IN WIN32API
            this.hAbortEvent = CreateEvent(0,0,0,"VFPAbortThreadEvent") && Get the existing event
            IF this.hAbortEvent = 0
                  THROW "Creating event error:"+TRANSFORM(GetLastError())
            ENDIF
            DECLARE integer Sleep in WIN32API integer
            DECLARE integer CloseHandle IN WIN32API integer
            nStart=SECONDS()
            fUseCritSects=.t. && try with .f.
            IF fUseCritSects
                  SYS(2336,1) && Enter a critical section. First thread in wins
            ENDIF
            IF !FILE("files.dbf")
                  IF !fUseCritSects      
                        Sleep(1000) && give a chance for other threads to come in here!
                  ENDIF
                  CREATE TABLE files (path c(100), size n(10))
            ENDIF
            USE files SHARED && reopen shared
            IF fUseCritSects
                  SYS(2336,2) && Exit the critical section
            ENDIF
            cResult = TRANSFORM(this.RecurPath(cPath))      && search disk to gather files into table. Returns file count
            nDuration = SECONDS()-nStart
            nDesiredDuration=5      && # secs
            IF nDuration < nDesiredDuration     && let's make the thread proc last longer: OS caches disk results
*                 Sleep((nDesiredDuration - nDuration)*1000)
            ENDIF
            IF this.IsThreadAborted()     && if main thread said to abort
                  cResult=cResult+ " Aborted"
            ENDIF
            INSERT INTO C:\EDDY\GHOST\THREADLOG.DBF VALUES (GetCurrentThreadId(), DATETIME(),TRANSFORM(cPath)+":"+cResult)
      PROCEDURE IsThreadAborted as Boolean
            IF WaitForSingleObject(this.hAbortEvent,0) = 258
                  RETURN .f.
            ENDIF
            RETURN .t.
      PROCEDURE RecurPath(cPath as String) as Integer
            LOCAL n,i,aa[1],nRetval
            nRetval=0
            n = ADIR(aa,cPath+"*.*","D")
            FOR i = 1 TO n
                  IF "D"$aa[i,5]    && if it's a dir
                        IF aa[i,1] != '.'
*                             nRetval=nRetval + this.RecurPath(cPath+aa[i,1]+"\")   && be careful!
                        ENDIF
                  ELSE
                        INSERT INTO files VALUES (cPath+aa[i,1], aa[i,2])
                        nRetval=nRetval+1
                        IF this.IsThreadAborted()     && Did main thread request abort
                              EXIT
                        ENDIF
                  ENDIF
            ENDFOR
            RETURN nRetval
      PROCEDURE Destroy
            CloseHandle(this.hAbortEvent)
ENDDEFINE