I have been looking for a routine that will find all the occurrences of a file in a directory tree, but there are no QuickWin functions that are useful. At first I successfully used the DOS command

dir filename /b/l/s/o:n /a:-d >c:dirlist.txt'

called by SYSTEMQQ. This works well and with wildcards, but flashes up a DOS box whilst searching.

I then wrote a successful recursive routine FindFileinTree using the WIN APIs FindFirstFile and FindNextFile ? but I am still working on the wildcards side of it.

Then I chanced upon SearchTreeForFile in imagehlp.dll. This is not one for which CVF has an interface ? indeed I cannot even find it in either of JR Simon?s API Superbibles. I applied my considerable degree of ignorance of WIN APIs to hacking a working interface for it, and found that it will only find the first occurrence of a file, and does not work with wildcards.

Does anyone know if it is possible to use SearchTreeForFile or any other similar routine to find all the occurrences of a file in a tree?

Bear of little brain

7 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

AFAIK no -- solution with recursive FindFirstFile/FindNextFile is "classic". I'd suggest having two "interwoven" searches instead of one (i.e. two FindFirstFiles with two separate handles) -- one recursively searching for directories (*.* with WFD%dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY), the second searching for files within one directory which satisfy given file-spec.



Many thanks for your helpful reply. Do you recommend a two-thread process because it is faster or for another reason? Two threads is about one more than I can hack.

I find it strange that a single WIN API does not exist for this purpose, which is exactly what Windows Explorer does in searching for a file. SearchTreeForFile is OK if you are certain there is only one occurrence of a file, but even that is buried away. Anyway, I am a firm believer in Paranoia Programming, and with Windows, copies of files can proliferate.

So I think that on this one I will retire hurt and use my home-brewed version which currently can't deal with partial paths.
Bear of little Brain

No, no, I didn't meant threads, but rather something along these lines (off the top of my head, so excuse me for errors)

RECURSIVE SUBROUTINE SearchTree(szDirName, szFileSpec) 
CHARACTER(*):: szDirName, & 
         szFileSpec  !file name including wildcards 
INTEGER:: hFindDir, &  !For traversing subdirectories 
          hFindFile      !For traversing this directory 
hFindDir=FindFirstFile(C_TRIM(szDirName)//"*.*"C, WFDdir) 
bFoundDir =.TRUE. 
    !Now search for files in this directory 
    hFindFile = FindFirstFile(C_TRIM(szDirName)//""//szFileSpec, WFDfile) 
    DO WHILE (hFindFile/=INVALID_HANDLE_VALUE .AND. bFoundFile) 
        !Do something with WFDfile%cFileName here 
        bFoundFile = FindNextFile(hFindFile, WFDfile) 
    END DO 
   !Find next directory 
    hFindDir=FindNextFile(hFindDir, WFDdir) 
       CALL  SearchTree(WFDdir%cFileName, szFileSpec) 
    END IF 

Hope you get the idea. (I used C_TRIM for something that returns string up to the first CHAR(0)). My suggestion was actually related with your "home-brew" idea -- I just meant that using two FindFirstFiles should solve the problem entirely, including wildcard stuff.

Perhaps the code above could be speeded up if one is sure that subdirectories are always found first (because the outer loop as is now will check for all files and subdirectories), but docs do not guarantee that.




Just spotted an obvious logical error -- there shouldn't be two embedded loops, but two consecutive loops, the one being inner now should be first... see what I mean...



Many thanks for the code. With your later correction, it is basically what I have done, but the use of another FindFirst File call with a different selection criterion fixes my wildcard problem. Your code is also rather more elegant than mine, because I am not yet comfortable with these NUL-terminated strings.

Bear of little brain


I append a working version of FindFileInTree, which can work from the root directory down. I made some changes to avoid trouble with the '.' & '..' at the start of every directory, also to the logic on the last 'do while' to cater for empty directories. Being fairly fresh from the mill, it is not yet very well tested, so please to you and others, let me know what the bugs are.

Thanks again for your help.

recursive subroutine FindFileInTree(CurDir, FileName, Path, nPath)
! Searches directory CurDir for file FileName and puts its path into character
! array Path, incrementing nPath by one. It then searches any subdirectories
! using a recursive call. Set nPath to zero before calling.
use kernel32, only : FILE_ATTRIBUTE_DIRECTORY, FindFirstFile, FindNextFile, &
use winfns, only : C_TRIM ! Returns string with Nul trimmed off.
implicit none
character(*), intent(in) :: CurDir
! Nul-terminated string containing start directory path.
character(*), intent(in) :: FileName
! Nul-terminated string containing filename including wildcards.
character(*), intent(inout) :: Path(*)
! Array of paths for files found (Nul-terminated strings).
integer(4), intent(inout) :: nPath
! Number of files found.
integer(4) hFindDir, hFindFile
logical(4) bFoundDir, BFoundFile
hFindFile = FindFirstFile(C_TRIM(CurDir)//""//FileName,WFDfile)
bFoundFile = .TRUE.
do while (hFindFile /= INVALID_HANDLE_VALUE .and. bFoundFile)
nPath = nPath+1
Path(nPath) = C_TRIM(CurDir)//""//WFDfile.cFileName
bFoundFile = FindNextFile(hFindFile,WFDfile)
end do
! The next two lines read in the '.' & '..' at the top of every directory.
hFindDir = FindFirstFile(C_TRIM(CurDir)//"*.*"C, WFDdir)
bFoundDir = FindNextFile(hFindDir,WFDDir)
bFoundDir = .TRUE.
do while (hFindDir/=INVALID_HANDLE_VALUE .AND. bFoundDir)
bFoundDir = FindNextFile(hFindDir,WFDDir)
if (WFDdir.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY &
.AND. bFoundDir) then
call FindFileInTree(C_TRIM(CurDir)//""//WFDdir.cFileName,FileName, &
end if
end do
end subroutine FindFileInTree

PS. It works OK on the wildcards that I tested.

Leave a Comment

Please sign in to add a comment. Not a member? Join today