Announcement

Collapse
No announcement yet.

Find File (as fast as possible)

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Find File (as fast as possible)

    for reasons that i know the name of the file or program being searched for and my end user doesn't
    i can not just have them browse for where the file is, so i was looking at borje hagsten's code for
    a recursive file find at findfilev2

    i made some modifications to speed things up (stuff like the overhead of graphically showing status of the search etc...)
    along with the ability to stop looking once the file is found (rather than enumerating alllll the files on all drives and then
    search an over-kill array size.)

    things are going well with my machine at work, and about 198,000 files searched takes about 1 minute if the file is not found first.
    but then i tried again at home and roughly 249,000 takes roughly 7.5 minutes???!!??!?!?! what a jump

    (now remember this is if i go through every file on the system and do not escape if the file is found)

    i tried with the original code and got roughly the same results (lil bit longer but i can chalk that up to overhead of displaying status and results as i search)

    what i would like to do is speed things up (at the moment i don't think i can, unless there is another way to search for a file?)
    or at least show a current status so that thing's do not look broke while still searching.

    so i was thinking, how would i callback the parent function that called my long running function? (besides some hidden textbox or something to post replies to?)
    or somehow thread things (but if i do that, i still have to wait for my function to either find or not find the file before the rest of my program can continue)

    the other thing i was trying to figure out is how looking through about 198,000 items only takes 1 minute, but 249,000 takes 7x as long??!?!?!?!

    i will have to test in the morning but at the moment my only clue is that the machine at work is smaller and slower and partitioned into different drives (which runs faster)
    vs my much faster machine with 1 solid drive but somehow runs much slower in this case?

    any ideas?


    ------------------
    Engineer's Motto: If it aint broke take it apart and fix it

    "If at 1st you don't succeed... call it version 1.0"

    "Half of Programming is coding"....."The other 90% is DEBUGGING"

    "Document my code????" .... "WHYYY??? do you think they call it CODE? "

  • #2
    Cliff -

    is it possible to exclude some locations from searching?
    i.e. Windows and Program Files?

    any "warranty" the missing file isn't in such?

    any knowledge of "the default directory" when the app that created the file was running?

    if you can identify the "probable" locations and search those first, you may hit real fast - "My Documents" etc....


    ps: I don't know all the technical reasons, but I can confirm with my experience that "one big drive" runs proportionally much slower than multiple smaller drives - especially when addressing "the whole thing"
    pps: indexing turned on/off?
    ------------------




    [This message has been edited by Tom Tallardy (edited July 05, 2007).]

    Comment


    • #3
      There can be lots of reasons why on one pc it runs faster as the other:

      - Maybe different file system? (FAT/NTFS, compression on/off)
      - Also directories with large number of files can slow down (3000 or more files)
      - Is the solid drive connected through USB? (also slower)


      ------------------
      Regards,
      Peter
      Regards,
      Peter

      Comment


      • #4
        > I know the name of the file or program being searched for ..

        You know the NAME of the file, just not WHERE it is?

        Then why are you are searching 198,000 or 249,000 FILES?

        All you need to do is make one check in each DIRECTORY.

        Your "I made some modifications to speed things up" is suspect.

        Basically your code should look like..

        Code:
        FUNCTION FindFileInFolder (sFolderName, sfileName)
        
             ' is file in this folder?
               IF DIR$(sFolderName & "\"  & sFileName) > "" THEN
                   iFound = %TRUE
               ELSE
                 ' process each DIRECTORY in this folder
                   f = DIR$ ("*.*")
                   IF f is a directory (you can use GETATTR)  then
                      Call FindFileinFolder (f, sFileName) TO iFound
               ...
        
               END IF
        
               FUNCTION = ISTRUE (iFound) 
               if IFound THEN
                  exit recursion
               endif
        You'll have to add some code to handle the early exit from the recursion when the file is found. I know I have posted something here to exit recursion but I think you need to take a second look at your modifications.

        I'm not sure DIR$ is 100% stack-based and therefore it might require you to go to the WinApi FindFirstFile (or maybe a DIR$ CLOSE?) , but I think at this point we should see what you have got so far. The tradeoff is you automatically get the File attribute in the WIN32_FIND_DATA structure so you won't need a second call to decide if the found file is a directory or not.

        MCM




        Comment


        • #5
          But you know.... I'll bet there is a way to use either the WMI stuff or maybe the Shell to get a list of folders on the system quite quickly... then you could just check them sequentially for the 'file of interest.'

          Maybe there's some WMI or Shell whiz here?

          ???


          Comment


          • #6
            This is what I am currently using

            Code:
            FUNCTION FindFile(BYVAL Path AS STRING, CheckSubDirs AS LONG, FileToFind AS STRING)AS LONG
                 DIM hSearch     AS DWORD               'search handle
                 DIM tmpSize     AS QUAD                'must use QUAD, in case of huge files..
                 DIM WFD         AS WIN32_FIND_DATA     'FindFirstFile structure
                 DIM curpath     AS ASCIIZ * %MAX_PATH  'what to search for
                 curpath = path & fName                            'this is what we want to find
                 hSearch = FindFirstFile(curpath, WFD)             'get search handle, if success -
                 IF hSearch <> %INVALID_HANDLE_VALUE THEN          'loop through directory for files
                      DO
                           IF (WFD.dwFileAttributes AND %FILE_ATTRIBUTE_DIRECTORY)<> %FILE_ATTRIBUTE_DIRECTORY THEN    ' if not directory bit (16) is set(files only here..)
                                IF Abort = 1 THEN EXIT DO
                                IF FileFound = %True  THEN EXIT DO
                                REPLACE fName WITH "" IN CurPath
                                Files(UBOUND(Files)) = curpath + WFD.cFileName
                                SELECT CASE INSTR(UCASE$(Files(UBOUND(Files))), UCASE$(FileToFind))
                                     CASE 0
                                          REDIM PRESERVE Files(UBOUND(Files) + 1)
                                     CASE ELSE
                                          FileFound = %True
                                          EXIT DO
                                END SELECT
            
                           END IF
                      LOOP WHILE FindNextFile(hSearch, WFD)
                      CALL FindClose(hSearch)
                 END IF
            
                 IF SubDirs THEN                                     'if to search in subdirectories.
                      curpath = path & "*"
                      hSearch = FindFirstFile(curpath, WFD)
                      IF hSearch <> %INVALID_HANDLE_VALUE THEN
                           DO
                                IF (WFD.dwFileAttributes AND %FILE_ATTRIBUTE_DIRECTORY) THEN 'if directory
                                     IF (ASC(WFD.cFileName) = 46) = 0 THEN    'Not . or ..
                                          IF Abort = 1 THEN EXIT DO
                                          IF FileFound = %True  THEN EXIT DO
                                               CALL FindFile(path & RTRIM$(WFD.cFileName, CHR$(0)) & "\", CheckSubDirs, FileToFind)      'Recursive call to find files in sub-directory
                                     END IF
                                END IF
                           LOOP WHILE FindNextFile(hSearch, WFD)
                           CALL FindClose(hSearch)
                      END IF
                 END IF
            END FUNCTION
            Which I was trying to figure out how to add some Variable for callback to the parent function to report the current status of the search.

            I will try one using Dir$ and see if it is any faster.

            Normally I would not have thought this a problem, but then realized if the file does not exist, then the code will continue until all paths are checked to see if the file exists.
            and a few other tests with other drives I have found that 1 big drive is definitely slower than 1 big drive that has been partitioned

            ------------------
            Engineer's Motto: If it aint broke take it apart and fix it

            "If at 1st you don't succeed... call it version 1.0"

            "Half of Programming is coding"....."The other 90% is DEBUGGING"

            "Document my code????" .... "WHYYY??? do you think they call it CODE? "

            Comment


            • #7
              This logic here...
              Code:
               hSearch = FindFirstFile(curpath, WFD)  'get search handle, if success -
                   IF hSearch <> %INVALID_HANDLE_VALUE THEN 
                       'loop through directory for files
                        DO
                             IF (WFD.dwFileAttributes AND %FILE_ATTRIBUTE_DIRECTORY)<> %FILE_ATTRIBUTE_DIRECTORY THEN 
                                 ' if not directory bit (16) is set(files only here..)
                                  IF Abort = 1 THEN EXIT DO
                                  IF FileFound = %True  THEN EXIT DO
                                  REPLACE fName WITH "" IN CurPath
              .. doesn't make any sense. If the file you seek is not in this folder, why are you doing anything with files other than directories in that folder?

              If you are just looking for a file with a specific name, there's no need to build an array of all files.

              But let's say you do have some undisclosed reason for making an array.....adding one to the array size each time:
              Code:
               REDIM PRESERVE Files(UBOUND(Files) + 1)
              .. is for sure a CPU-sucker. Better you should increase that array size in multiples of some number (say, 500? 100?) and test the current subscript vs. the current UBOUND before resizing.

              MCM


              [This message has been edited by Michael Mattias (edited July 06, 2007).]

              Comment


              • #8
                Dummy me....In knowing how many files were checked to see if the file exists
                I was adding a ton of overhead, when really I just want to know if it exists and where it exists and
                do not care about the other files.

                If I change my code to
                Code:
                FUNCTION FindFile(BYVAL Path AS STRING, CheckSubDirs AS LONG, FileToFind AS STRING)AS LONG
                     DIM hSearch     AS DWORD               'search handle
                     DIM tmpSize     AS QUAD                'must use QUAD, in case of huge files..
                     DIM WFD         AS WIN32_FIND_DATA     'FindFirstFile structure
                     DIM curpath     AS ASCIIZ * %MAX_PATH  'what to search for
                     curpath = path & fName                            'this is what we want to find
                     hSearch = FindFirstFile(curpath, WFD)             'get search handle, if success -
                     IF hSearch <> %INVALID_HANDLE_VALUE THEN          'loop through directory for files
                          DO
                               IF (WFD.dwFileAttributes AND %FILE_ATTRIBUTE_DIRECTORY)<> %FILE_ATTRIBUTE_DIRECTORY THEN    ' if not directory bit (16) is set(files only here..)
                                    IF Abort = 1 THEN EXIT DO
                                    IF FileFound = %True  THEN EXIT DO
                                    SELECT CASE UCASE$(WFD.cFileName)
                                         CASE UCASE$(FileToFind)
                                              REPLACE fName WITH "" IN CurPath
                                              Files(UBOUND(Files)) = curpath + WFD.cFileName
                                              FileFound = %True
                                              EXIT DO
                                    END SELECT
                               END IF
                          LOOP WHILE FindNextFile(hSearch, WFD)
                          CALL FindClose(hSearch)
                     END IF
                
                     IF SubDirs THEN                                     'if to search in subdirectories.
                          curpath = path & "*"
                          hSearch = FindFirstFile(curpath, WFD)
                          IF hSearch <> %INVALID_HANDLE_VALUE THEN
                               DO
                                    IF (WFD.dwFileAttributes AND %FILE_ATTRIBUTE_DIRECTORY) THEN 'if directory bit (16) is set
                                         IF (ASC(WFD.cFileName) = 46) = 0 THEN    'Not . or ..
                                              IF Abort = 1 THEN EXIT DO
                                              IF FileFound = %True  THEN EXIT DO
                                                   CALL FindFile(path & RTRIM$(WFD.cFileName, CHR$(0)) & "\", CheckSubDirs, FileToFind)
                                         END IF
                                    END IF
                               LOOP WHILE FindNextFile(hSearch, WFD)
                               CALL FindClose(hSearch)
                          END IF
                     END IF
                END FUNCTION
                Then locating the same file takes approximately 1 second, but if I look for a file that does not exist
                now takes approximately 91 seconds. So now I am down to reporting back to the parent function the current status
                in the case that the file does not exist. (I am thinking in the subdirs to report back so to minimize adding overhead)

                ------------------
                Engineer's Motto: If it aint broke take it apart and fix it

                "If at 1st you don't succeed... call it version 1.0"

                "Half of Programming is coding"....."The other 90% is DEBUGGING"

                "Document my code????" .... "WHYYY??? do you think they call it CODE? "

                Comment


                • #9
                  Only the (heavy) com interface to collect filenames is the fastest.

                  FindFile and such are much slower.

                  No source though.


                  ------------------
                  http://www.hellobasic.com
                  Click here for PwrDev.
                  hellobasic

                  Comment


                  • #10
                    At the very start of your function, you can check to see if the file if interest is in that folder with a simple
                    Code:
                    hSearch  = FindFirstFile (path & "\" & FileToFind, WFD)
                    If that comes up INVALID_HANDLE_VALUE, the file ain't in that folder, and you can proceed immediately to look for subdirectories. There is no sense looping thru all the files in the current 'path' variable foider which are NOT directories, because your file IS NOT THERE.

                    If it comes up other than INVALID_HANDLE_VALUE, congratulations, you just found your file.

                    In eithercase all yiou have to worry about at this poing is unrolling the recursion, but you are already doing that now.

                    This would also be an ideal time to

                    CALL NotifyCallerThatIJustStartedWorkingOnAntotherFolder.

                    Or, if alling from a window, just post a message...
                    Code:
                    %PWM_STARTED_FOLDER   = %WM_USER + 1  ' wparam=unused, lparam = ptr to folder name
                    ...
                    FUNCTION FINDFILE (path, CheckSubDirs, FileToFind) ...
                     LOCAL psz As ASCIIZ PTR 
                    
                       psz   = malloc (LEN(Path) + 1) 
                       @psz  = Path 
                       PostMessage hWndWhatever, PWM_STARTED_FOLDER, %NULL, BYVAL psz
                      ...
                    
                    
                    CALLBACK FUNCTION CallingDialogProc
                     LOCAL psz AS ASCIIZ PTR 
                    
                       IF CBMSG = %PWM_STARTED_FOLDER THEN 
                           psz  =  CBLPARAM
                           CONTROL SET TEXT  CBHNDL, %ID_WHERE_MY_FOLDERS_SHOW_LABEL, @psz
                           Free     psz  ' allocated fresh in other routine
                    Malloc and free functions in Source code forum, search subject 'malloc'


                    MCM

                    Comment


                    • #11
                      Thanx Michael,
                      I did a search and found out that Malloc was NOT a keyword as I first thought
                      nor a direct API (I think), but rather a routine that you so nicely wrapped. (I will not
                      give the direct link in this case since I also saw that once I understood the basics (well still a lil fuzzy but I think I get it)
                      that someone else had posted a direct link to the answer. (Which in this case I believe the original intent of the conversation was meant as a learning experience (aka: "Here are the tools, think about it, and learn from the answer"
                      rather than "Forget looking here is how you do it")

                      Thankfully this time I lucked out and did not pick a example too big to grasp all at once and make my head explode *LOL*
                      and lets be quite frank...when searching to learn from code, you often find TOOOO MANY examples that are either badly written, or too complex, or too many to sort that
                      after a while you give up and look for a easier answer.

                      (I got the same curse, but luckily for me, my curiosity of "How things work" brings me back and figure out how it works if it is a good example but too hard to grasp for a while)

                      Tom, and Peter made some good points too (if I TOTALLY want to optimize, which I might in the future, but for now until I really understand what I think I do I will keep things at this level)

                      The good news is I hope to tear things down the next day or 2 so I can show what I have learned, and under the circumstances of my slowest time so far (which I know I can optimize even more)
                      I have now gone from 7.5 Minutes to roughly 10-15 seconds for a file that I know does not exist, and roughly 0.01 seconds if it does exist.

                      Quite a MASSIVE increase in speed if you ask me. (That and I actually think I am beginning to understand how it works too
                      :-)

                      Thanx again guys, I hope to have a sample soon.
                      Cliff

                      ------------------
                      Engineer's Motto: If it aint broke take it apart and fix it

                      "If at 1st you don't succeed... call it version 1.0"

                      "Half of Programming is coding"....."The other 90% is DEBUGGING"

                      "Document my code????" .... "WHYYY??? do you think they call it CODE? "

                      Comment


                      • #12
                        If you change the Algo from Recursive to iterative, you'll get another Speed increase.
                        Therefore you'd have to use a intelligent managed global String-Array to store the results in,
                        and one to store temp-dir pathes in.

                        At the End you would not use Recursion but do only work on that List LILO (Last-In-Last-Out) until the element Number in the Temp-Dir-List is zero.
                        In my test you can get another significant speed improvement.

                        Link :-) http://www.refactoring.com/catalog/r...Iteration.html

                        ------------------
                        --Theo Gottwald
                        Theos Site * IT-Berater.org



                        [This message has been edited by Theo Gottwald (edited July 10, 2007).]
                        --Theo Gottwald
                        ------------------------------------------------
                        76706 Dettenheim * Germany * [email protected]
                        ------------------------------------------------
                        Joses Forum * Theo's Link Site * IT-Berater.org

                        Comment


                        • #13
                          I have now gone from 7.5 Minutes to roughly 10-15 seconds for a file that I know does not exist, and roughly 0.01 seconds if it does exist.
                          Sounds similar to Windows' Explorer's 'search' to me.

                          (Not including the two days it takes to fill in the seventy-seven screens required just to set up the search.)

                          Comment


                          • #14
                            Explorer also uses multiple threads. I've seen it usually use 8-11
                            when searching. Try spawning off threads to search the sub folders.

                            ------------------
                            If you aim at nothing...you will hit it.
                            sigpic
                            Mobile Solutions
                            Sys Analyst and Development

                            Comment


                            • #15
                              Cliff,
                              I keep looking in the Source Code forum for the final version of this super fast search program but it's not there.


                              Roger,
                              I can't see how threads will help very much in a file search on disk. Almost all the time will be spent waiting for data to arrive from the disk and next to no time in processing that data (after all, it's either there or it's not, there's no other processing to do for a FileFind routine). Many threads will all stall at the same time waiting for data. You might gain by having 1 thread run a search on each physical disk and one more thread to do the overall control and processing but I don't think Windows searches physical disks simultaneously.

                              Paul.


                              ------------------


                              [This message has been edited by Paul Dixon (edited July 10, 2007).]

                              Comment


                              • #16
                                Actually Paul, that is what I did.

                                a thread for each drive.

                                I am working on an example now (sorry haven't had time till tonight to work on it).


                                ------------------
                                Engineer's Motto: If it aint broke take it apart and fix it

                                "If at 1st you don't succeed... call it version 1.0"

                                "Half of Programming is coding"....."The other 90% is DEBUGGING"

                                "Document my code????" .... "WHYYY??? do you think they call it CODE? "

                                Comment


                                • #17
                                  > a thread for each drive

                                  So that's why you asked about how to 'stop' a thread, huh?

                                  If you find the file on drive K: no sense continuing the searches on A, B, C....

                                  Well, the example I provided of using Windows' events will work for that... and wouldn't you know it, now you DO have a recursing thread function!


                                  Comment


                                  • #18
                                    As promised a working example of a fast file find. Based from Borje Hagsten's FindFileV2 code
                                    and Michael Mattias code for Heap Allocation. And from the many suggestions from others in the forums.

                                    Its still a work in progress, but maybe someone will find parts of it useful.

                                    ********************** HEADER AND INCLUDES FILE ********************
                                    Code:
                                    '*** Windows Components
                                    #INCLUDE "WIN32API.INC"  'Define Windows OS variables and functions
                                    
                                    '*** Headers
                                    #INCLUDE "FILEFOLDER.h"  'Declarations for FileFolder.inc
                                    #INCLUDE "HARDDRIVE.h"  'Declarations for HardDrive.inc
                                    
                                    '*** Includes
                                    #INCLUDE "MEMORYALLOCATE.INC"
                                    #INCLUDE "FILEFOLDER.INC"  'Declarations for FileFolder.inc
                                    #INCLUDE "HARDDRIVE.INC"  'Declarations for HardDrive.inc
                                    ********************** HARD DRIVE HEADER ********************
                                    Code:
                                    DECLARE SUB GetDriveNames()
                                    GLOBAL HardDrives() AS STRING
                                    ********************** FILE FOLDER HEADER ********************
                                    Code:
                                    DECLARE FUNCTION FindFile(BYVAL Path AS STRING, CheckSubDirs AS LONG, FileToFind AS STRING, OPTIONAL ReportCurPath AS LONG)AS LONG
                                    
                                    %FindFileCurPath = %WM_USER + 1
                                    
                                    GLOBAL FileCount AS LONG, SubDirs AS LONG, Abort AS BYTE
                                    GLOBAL tSize AS QUAD ' For calculating the files total size
                                    GLOBAL fName AS STRING, StartPath AS STRING
                                    GLOBAL Files() AS STRING
                                    GLOBAL FileFound AS LONG
                                    ********************** MEMORY ALLOCATION FROM MICHAEL MATTIAS ********************
                                    Code:
                                    ' MALLOC.INC
                                    ' Generic Memory Alloction/ReAllocation/Free Routines for use with PB Programs.
                                    ' CREATED: 11/09/02
                                    ' 11/24/02 Added #INCLUDE file control %MALLOC_INC
                                    ' Placed in Public Domain by author, Michael C. Mattias Racine WI USA
                                    ' 3.17.04 Changed Free flags to drop NO_SERIALIZE, which should not be used on process heap
                                    
                                    #IF NOT %DEF(%MALLOC_INC)
                                    %MALLOC_INC = 1
                                    
                                    #IF NOT %DEF(%WINAPI)
                                    DECLARE FUNCTION GetProcessHeap LIB "KERNEL32.DLL" ALIAS "GetProcessHeap" () AS LONG
                                    DECLARE FUNCTION HeapAlloc LIB "KERNEL32.DLL" ALIAS "HeapAlloc" (BYVAL hHeap AS DWORD, BYVAL dwFlags AS DWORD, BYVAL dwBytes AS DWORD) AS DWORD
                                    DECLARE FUNCTION HeapFree LIB "KERNEL32.DLL" ALIAS "HeapFree" (BYVAL hHeap AS DWORD, BYVAL dwFlags AS DWORD, BYVAL lpMem AS DWORD) AS LONG
                                    DECLARE FUNCTION HeapReAlloc LIB "KERNEL32.DLL" ALIAS "HeapReAlloc" (BYVAL hHeap AS DWORD, BYVAL dwFlags AS DWORD, BYVAL lpMem AS DWORD, BYVAL dwBytes AS DWORD) AS DWORD
                                    %HEAP_NO_SERIALIZE = &H00000001
                                    %HEAP_GENERATE_EXCEPTIONS = &H00000004
                                    %HEAP_ZERO_MEMORY = &H00000008
                                    #ENDIF
                                    
                                    #IF NOT %DEF(%HEAP_ALLOC_FLAGS)
                                    ' See MSDN for options for flags.
                                    %HEAP_ALLOC_FLAGS = %HEAP_ZERO_MEMORY OR %HEAP_GENERATE_EXCEPTIONS
                                    %HEAP_FREE_FLAGS = 0&
                                    #ENDIF
                                    
                                    FUNCTION malloc (BYVAL NumberOfBytes AS LONG) AS DWORD
                                    ' returns: address of block of size NumberOfBytes
                                    FUNCTION = HeapAlloc (GetProcessHeap(), %HEAP_ALLOC_FLAGS, NumberOfBytes)
                                    END FUNCTION
                                    
                                    FUNCTION realloc (BYVAL currentaddress AS DWORD, BYVAL NewSize AS LONG) AS DWORD
                                    ' returns: new address for block at current address after resized to newsize
                                    ' do I have to save what was there? I do not think so...nope, the original contents
                                    ' of the memory block are unaffected by HeapRealloc, pass or fail.
                                    FUNCTION = HeapRealloc(GetProcessHeap(), %HEAP_ALLOC_FLAGS, Currentaddress, newSize)
                                    END FUNCTION
                                    
                                    FUNCTION free (BYVAL BlockAddress AS DWORD) AS DWORD
                                    'returns: null on failure (probably was passed an invalid pointer)
                                    FUNCTION = HeapFree (getProcessHeap(), %HEAP_FREE_FLAGS, blockAddress)
                                    END FUNCTION
                                    
                                    #ENDIF
                                    'if %DEF %MALLOC_INC
                                    ' ** END OF FILE ***

                                    ********************** HARD DRIVE ROUTINES ********************
                                    Code:
                                    ' Return an array of drive names.
                                    SUB GetDriveNames()
                                         DIM sDrives AS STRING
                                         DIM i AS LONG
                                         REDIM HardDrives(0)
                                         sDrives = STRING$(GetLogicalDriveStrings(0, BYVAL %NULL), $NUL)
                                         GetLogicalDriveStrings LEN(sDrives), BYVAL STRPTR(sDrives)
                                         REDIM PRESERVE HardDrives(PARSECOUNT(sDrives, $NUL)- 3)
                                         FOR i = LBOUND(HardDrives) TO UBOUND(HardDrives)
                                              SELECT CASE RTRIM$(PARSE$(sDrives, $NUL, i), ANY "\/")
                                                   CASE ""
                                                   CASE ELSE
                                                        HardDrives(i) = RTRIM$(PARSE$(sDrives, $NUL, i), ANY "\/") + "\"
                                              END SELECT
                                        NEXT i
                                    END SUB
                                    ********************** FILE FOLDER (modified from Borje Hagsten's code) ********************
                                    Code:
                                    FUNCTION FindFile(BYVAL Path AS STRING, CheckSubDirs AS LONG, FileToFind AS STRING, OPTIONAL ReportCurPath AS LONG)AS LONG
                                         DIM hSearch     AS DWORD               'search handle
                                         DIM tmpSize     AS QUAD                'must use QUAD, in case of huge files..
                                         DIM WFD         AS WIN32_FIND_DATA     'FindFirstFile structure
                                         DIM curpath     AS ASCIIZ * %MAX_PATH  'what to search for
                                         curpath = path & FileToFind       '& fName                            'this is what we want to find
                                         SELECT CASE FindFirstFile(curpath, WFD)
                                              CASE %INVALID_HANDLE_VALUE
                                                   IF SubDirs THEN                                     'if to search in subdirectories.
                                                        curpath = path & "*"
                                                        hSearch = FindFirstFile(curpath, WFD)
                                                        IF hSearch <> %INVALID_HANDLE_VALUE THEN
                                                             DO
                                                                  IF (WFD.dwFileAttributes AND %FILE_ATTRIBUTE_DIRECTORY) THEN 'if directory bit (16) is set
                                                                       IF (ASC(WFD.cFileName) = 46) = 0 THEN    'Not . or ..
                                                                            IF Abort = 1 THEN EXIT DO
                                                                            IF FileFound = %True  THEN EXIT DO
                                                                            'DIALOG SET TEXT HwndSchedule ,path & RTRIM$(WFD.cFileName, CHR$(0)) & "\" + FileToFind
                                                                            DIM psz AS STRING PTR
                                                                            psz   = malloc (LEN(Path) + 1)
                                                                            @psz = path & RTRIM$(WFD.cFileName, CHR$(0)) '& "\" + FileToFind      'Post the current folder
                                                                            PostMessage ReportCurPath, %FindFileCurPath, BYVAL %NULL, BYVAL psz
                                                                            CALL FindFile(path & RTRIM$(WFD.cFileName, CHR$(0)) & "\", CheckSubDirs, FileToFind, ReportCurPath)
                                                                       END IF
                                                                  END IF
                                                             LOOP WHILE FindNextFile(hSearch, WFD)
                                                             CALL FindClose(hSearch)
                                                        END IF
                                                   END IF
                                              CASE ELSE
                                    '               REPLACE fName WITH "" IN CurPath
                                                   Files(UBOUND(Files)) = curpath '+ WFD.cFileName
                                                   FileFound = %True
                                                   CALL FindClose(hSearch)
                                                   EXIT FUNCTION
                                         END SELECT
                                    END FUNCTION
                                    ********************** Test Executable ********************
                                    Code:
                                    #COMPILE EXE
                                    #DIM ALL
                                    
                                    '------------------------------------------------------------------------------
                                    '   ** Includes **
                                    '------------------------------------------------------------------------------
                                    #INCLUDE "HEADERINCLUDES.h"
                                    '------------------------------------------------------------------------------
                                    
                                    '------------------------------------------------------------------------------
                                    '   ** Constants **
                                    '------------------------------------------------------------------------------
                                    %IDD_DIALOG1 =  101
                                    %IDC_BUTTON1 = 1001
                                    '------------------------------------------------------------------------------
                                    
                                    '------------------------------------------------------------------------------
                                    '   ** Declarations **
                                    '------------------------------------------------------------------------------
                                    DECLARE CALLBACK FUNCTION ShowDIALOG1Proc()
                                    DECLARE FUNCTION ShowDIALOG1(BYVAL hParent AS DWORD) AS LONG
                                    DECLARE FUNCTION ThreadDrives(BYVAL X AS LONG)AS LONG
                                    '------------------------------------------------------------------------------
                                    
                                    '------------------------------------------------------------------------------
                                    '   ** Globals **
                                    '------------------------------------------------------------------------------
                                    GLOBAL hDlg  AS DWORD
                                    GLOBAL StopThreads AS LONG
                                    '------------------------------------------------------------------------------
                                    
                                    '------------------------------------------------------------------------------
                                    '   ** Main Application Entry Point **
                                    '------------------------------------------------------------------------------
                                    FUNCTION PBMAIN()
                                        ShowDIALOG1 %HWND_DESKTOP
                                    END FUNCTION
                                    '------------------------------------------------------------------------------
                                    
                                    '------------------------------------------------------------------------------
                                    '   ** CallBacks **
                                    '------------------------------------------------------------------------------
                                    CALLBACK FUNCTION ShowDIALOG1Proc()
                                         DIM i AS LONG
                                         DIM TmpReply AS LONG
                                         DIM psz AS STRING PTR
                                        SELECT CASE AS LONG CBMSG
                                              CASE %FindFileCurPath
                                                   psz = CBLPARAM
                                                   DIALOG SET TEXT hDlg, @psz
                                                   Free psz
                                            CASE %WM_INITDIALOG
                                                ' Initialization handler
                                    
                                            CASE %WM_NCACTIVATE
                                                STATIC hWndSaveFocus AS DWORD
                                                IF ISFALSE CBWPARAM THEN
                                                    ' Save control focus
                                                    hWndSaveFocus = GetFocus()
                                                ELSEIF hWndSaveFocus THEN
                                                    ' Restore control focus
                                                    SetFocus(hWndSaveFocus)
                                                    hWndSaveFocus = 0
                                                END IF
                                    
                                            CASE %WM_COMMAND
                                                ' Process control notifications
                                                SELECT CASE AS LONG CBCTL
                                                    CASE %IDC_BUTTON1
                                                        IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                                                             GetDriveNames
                                                             FOR i = LBOUND(HardDrives) TO UBOUND(HardDrives)
                                                                  THREAD CREATE ThreadDrives(i) TO TmpReply
                                                             NEXT i
                                                        END IF
                                                END SELECT
                                        END SELECT
                                    END FUNCTION
                                    '------------------------------------------------------------------------------
                                    
                                    '------------------------------------------------------------------------------
                                    '   ** Dialogs **
                                    '------------------------------------------------------------------------------
                                    FUNCTION ShowDIALOG1(BYVAL hParent AS DWORD) AS LONG
                                        LOCAL lRslt AS LONG
                                        DIALOG NEW hParent, "Super Fast File Find", 70, 70, 152, 27, %WS_POPUP OR _
                                            %WS_BORDER OR %WS_DLGFRAME OR %WS_THICKFRAME OR %WS_CAPTION OR _
                                            %WS_SYSMENU OR %WS_MINIMIZEBOX OR %WS_MAXIMIZEBOX OR _
                                            %WS_CLIPSIBLINGS OR %WS_VISIBLE OR %DS_MODALFRAME OR %DS_3DLOOK OR _
                                            %DS_NOFAILCREATE OR %DS_SETFONT, %WS_EX_CONTROLPARENT OR %WS_EX_LEFT _
                                            OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR, TO hDlg
                                        CONTROL ADD BUTTON, hDlg, %IDC_BUTTON1, "Find File", 45, 5, 60, 19
                                    
                                        DIALOG SHOW MODAL hDlg, CALL ShowDIALOG1Proc TO lRslt
                                        FUNCTION = lRslt
                                    END FUNCTION
                                    '------------------------------------------------------------------------------
                                    FUNCTION ThreadDrives(BYVAL X AS LONG)AS LONG
                                         LOCAL t AS SINGLE
                                         DIM i AS LONG
                                         LOCAL text$
                                         t = TIMER
                                         REDIM Files(0) 'initiate these
                                         fName = "*.*"
                                         SubDirs = 1
                                         filefound = %false
                                         DIM FileToFind AS STRING
                                         DIM ReportBackWindow AS LONG
                                         ReportBackWindow = hDlg
                                    '     FileToFind = "avgcc.exe"           'Avg Antivirus
                                         FileToFind = "MyTest.txt"           'Test File
                                    '     FileToFind = "My Super Duper Underdog Program.exe"    'Bogus program to demonstrate finding a non-existant file
                                         SELECT CASE UCASE$(HardDrives(X))
                                              CASE "", "A:\"
                                              CASE ELSE
                                                   FindFile(HardDrives(x), SubDirs, FileToFind, ReportBackWindow)
                                         END SELECT
                                         SELECT CASE FileFound
                                              CASE %True           'File Found
                                                   SELECT CASE StopThreads       'If StopThreads then one thread mustve found already
                                                        CASE %True
                                                        CASE %False
                                                             StopThreads = %True
                                                             t = TIMER - t                                        'the time it took
                                                             text$ = Files(UBOUND(Files)) & " " &  " in " & FORMAT$(t, "0.000") & " sec."
                                                             MSGBOX files(UBOUND(Files)) + $CR + text$
                                                   END SELECT
                                    
                                                   DIALOG SET TEXT hDlg, Files(UBOUND(Files))     'In the case a thread changed before it was stopped
                                              CASE %False      'File not found
                                    '               msgbox "File Not Found " + str$(ThreadId)
                                                   EXIT FUNCTION
                                         END SELECT
                                    END FUNCTION
                                    ------------------
                                    Engineer's Motto: If it aint broke take it apart and fix it

                                    "If at 1st you don't succeed... call it version 1.0"

                                    "Half of Programming is coding"....."The other 90% is DEBUGGING"

                                    "Document my code????" .... "WHYYY??? do you think they call it CODE? "

                                    Comment


                                    • #19
                                      Thanks Cliff for your work. I ran into a problem on the hard drive enumeration function that turned out to be a simple fix. The call:
                                      Code:
                                      sDrives = STRING$(GetLogicalDriveStrings(0, BYVAL %NULL), $NUL)
                                      GetLogicalDriveStrings LEN(sDrives), BYVAL STRPTR(sDrives)
                                      This line should be modified to allow room for the terminating NULL, or you will miss the last drive returned.
                                      Code:
                                      sDrives = STRING$(GetLogicalDriveStrings(0, BYVAL %NULL)+1, $NUL)
                                      GetLogicalDriveStrings LEN(sDrives), BYVAL STRPTR(sDrives)
                                      My modified hard drive enumeration function allows you to select a subset of available drives. Sample program follows:
                                      regards, Ian.
                                      Code:
                                      #COMPILE EXE
                                      #DIM ALL
                                      #INCLUDE "WIN32API.INC"  'Define Windows OS variables and functions
                                      
                                      DECLARE FUNCTION GetDriveNames(BYVAL firstDrive AS STRING, BYVAL lastDrive AS STRING) AS LONG
                                      '=================
                                      FUNCTION PBMAIN () AS LONG
                                        GLOBAL HardDrives() AS STRING
                                        LOCAL noDrives AS LONG, _
                                              i        AS LONG, _
                                              s        AS STRING
                                      ' change it to reflect your own situation.
                                      ' You can mix uppercase/lowercase and order is not important
                                        noDrives = GetDriveNames("g", "")
                                        FOR i = LBOUND(hardDrives) TO LBOUND(hardDrives) + noDrives-1
                                          s = s + HardDrives(i) + " "
                                        NEXT i
                                        MSGBOX "No Drives: " + FORMAT$(noDrives) + CHR$(13) + s
                                      
                                      END FUNCTION
                                      '=================
                                      FUNCTION GetDriveNames(BYVAL firstDrive AS STRING, BYVAL lastDrive AS STRING) AS LONG
                                      ' firstDrive would usually be "C"
                                      ' lastDrive would be used if you were on a large network
                                      '           and didn't want to search beyond your own computer.
                                        LOCAL sDrives  AS STRING, _
                                              xFirst   AS STRING, _
                                              xLast    AS STRING, _
                                              noDrives AS LONG, _
                                              i        AS LONG, _
                                              j        AS LONG, _
                                              xLower   AS LONG, _
                                              xUpper   AS LONG
                                      
                                        REDIM HardDrives(0) ' HardDrives is a global string array
                                      ' sDrives has to have one more character than is reported by the function!
                                      ' From the help file: This size does not include the terminating null character.
                                        sDrives = STRING$(GetLogicalDriveStrings(0, BYVAL %NULL)+1, $NUL)
                                        GetLogicalDriveStrings LEN(sDrives), BYVAL STRPTR(sDrives)
                                        noDrives = PARSECOUNT(sDrives, $NUL)- 3
                                        DIM xDrives(1:noDrives) AS STRING
                                        xLower = 1 : xUpper = noDrives
                                        FOR i = 1 TO noDrives
                                          SELECT CASE RTRIM$(PARSE$(sDrives, $NUL, i), ANY "\/")
                                            CASE ""
                                            CASE ELSE
                                              xDrives(i) = RTRIM$(PARSE$(sDrives, $NUL, i), ANY "\/") + "\"
                                          END SELECT
                                        NEXT i
                                      ' If necessary, reduce the number of drives to be searched.
                                        xFirst = UCASE$(LEFT$(LTRIM$(firstDrive), 1))
                                        xLast = UCASE$(LEFT$(LTRIM$(lastDrive), 1))
                                      ' check on validity of entries
                                        IF LEN(xFirst) THEN
                                          IF xFirst <"A" OR xFirst >"Z" THEN xFirst = ""
                                        END IF
                                        IF LEN(xLast) THEN
                                          IF xLast <"A" OR xLast >"Z" THEN xLast = ""
                                        END IF
                                      ' Make sure the order is correct
                                        IF xLast < xFirst THEN SWAP xFirst, xLast
                                      ' do the extractions
                                        IF xFirst <> "" THEN
                                          FOR i = 1 TO noDrives
                                            IF xFirst <= LEFT$(xDrives(i),1) THEN
                                              xLower = i : EXIT FOR
                                            END IF
                                          NEXT i
                                        END IF
                                      
                                        IF xLast <> "" THEN
                                          FOR i = noDrives TO 1 STEP -1
                                            IF xLast >= LEFT$(xDrives(i),1) THEN
                                              xUpper = i : EXIT FOR
                                            END IF
                                          NEXT i
                                        END IF
                                        noDrives = xUpper - xLower +1
                                        REDIM PRESERVE HardDrives(noDrives)
                                        j = LBOUND(HardDrives)
                                        FOR i = xLower TO xUpper
                                          HardDrives(j) = xDrives(i)
                                          INCR j
                                        NEXT i
                                        FUNCTION = noDrives
                                      END FUNCTION
                                      '=================
                                      ------------------
                                      IRC
                                      :) IRC :)

                                      Comment


                                      • #20
                                        Update for PB10?

                                        I was thinking of updating a file find subroutine that I use in a couple of programs, and certainly this version is several times faster than what I have used before.

                                        However, it will compile in PB9 but not PB10. In PB10, it throws up an error:

                                        Code:
                                        Error 539 in E:\CURRENT\test.bas(146:045):  Invalid Thread Function
                                          Line 146:                               THREAD CREATE ThreadDrives(i) TO TmpReply
                                        Can't see what's wrong with that line (or the ThreadDrives() function). The parameter i is a LONG integer, which is OK in the Help file (could also be DWORD).
                                        Last edited by Peter Simmonds; 10 Jun 2011, 09:38 AM.

                                        Comment

                                        Working...
                                        X