SearchTreeForFile

SearchTreeForFile

Community Admin的头像

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 帖子 / 0 new
最新文章
如需更全面地了解编译器优化,请参阅优化注意事项
Jugoslav Dujic的头像

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.

Jugoslav www.xeffort.com
Community Admin的头像

Jugoslav,

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

Jugoslav Dujic的头像

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 
TYPE (T_WIN32_FIND_DATA):: WFDdir, WFDfile 
 
hFindDir=FindFirstFile(C_TRIM(szDirName)//"*.*"C, WFDdir) 
bFoundDir =.TRUE. 
DO WHILE (hFindDir/=INVALID_HANDLE_VALUE .AND. bFoundDir) 
    !Now search for files in this directory 
    hFindFile = FindFirstFile(C_TRIM(szDirName)//""//szFileSpec, WFDfile) 
    bFoundFile=.TRUE. 
    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) 
    IF (IAND(WFDdir%dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY).NE.0) THEN 
       CALL  SearchTree(WFDdir%cFileName, szFileSpec) 
    END IF 
END DO 


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.

HTH

Jugoslav

Jugoslav www.xeffort.com
Jugoslav Dujic的头像

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...

Jugoslav www.xeffort.com
Community Admin的头像

Jugooslav,

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

Community Admin的头像

Jugoslav,

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, &
INVALID_HANDLE_VALUE, T_WIN32_FIND_DATA
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
TYPE (T_WIN32_FIND_DATA) WFDdir, WFDfile
!
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, &
Path,nPath)
end if
end do
return
end subroutine FindFileInTree




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

登陆并发表评论。