Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28629 Discussions

FindFirstFile picks up a "wrong" file name

michael_green
Beginner
1,892 Views
Hi All,
I've been using FindFirstFile (followed by FindNextFile, etc) for listing all files in a folder conforming to a certain name pattern. But when the pattern is *81*, for example, I sometimes find I get file lists like the following:
mdg-81
mjp-81
oldgrowth-within-ecosystem$02
wnn-81
The problem is obviously with the third item in the list. It seems, on investigation , that FindFirstFile is latching on to the cAlternateFileName field of the WIN32_FIND_DATA structure, which in the above case contains the value OL4A81~1.FMD. This is very unhelpful. I can get around it, I suppose, by checking each file name that's returned, but that seems to defeat the purpose of these otherwise very useful functions.
Does anyone know how I can suppress this feature?
With many thanks in advance
Mike
0 Kudos
16 Replies
Jugoslav_Dujic
Valued Contributor II
1,893 Views
I googled a little for "FindFirstFile short name" and it seems like a bug embedded in Windows depth; see for yourself.

This article describes a workaround of some kind (plus, the workaround for GetLastError() misbehavior). Mhm... maybe I should have embed it in XFTFile::XEnumFiles as well.
0 Kudos
bearoflittlebrain_ol
1,893 Views
A couple of years back I wrote a recursive routine FindFileinTree (with some help from Jugoslav, naturally). It is based on FindFirstFile and FindNextFile. It does not pick up "oldgrowth-within-ecosystem$02" when the File Spec is '*81*'C, but does alas pick up "abcd.resx" when the File Spec is '*.res'C. I am looking at fixing the latter, but it will probably be an inelegant bodge.
Regards
Alan
0 Kudos
bearoflittlebrain_ol
1,893 Views
Mike,
I bodged a fix for the extension problem (it works for me anyway), but noticed during developing it that the Short Filename that my program derivedfor "oldgrowth-within-ecosystem$02" was "OLDGRO~1". Naturally, it didn't get selected as a match. So if your program produces "OL4A81.FMD", there is something interestingly different there, but I don't know what causes it and I hope it isn't contagious.
Regards
Alan
0 Kudos
Les_Neilson
Valued Contributor II
1,893 Views

Would GETFILEINFOQQ help ?

for example:

Code:

      use ifport


      integer*4 :: handle, isize, length
      character(255) :: name
      character(255) :: cpath

      record /FILE$INFO/ :: info

! Assume cpath is the path to be searched and has been initialised
! init

      handle = file$first


! read the directory

      do while (.true.)
        length = GETFILEINFOQQ(cpath,info,handle)
        if ((handle<0)) exit

        name = info.name       ! file name
        isize = info.length    ! file length

! Do stuff here
! eg index for specific string in file name

      end do


I just did aquic test andit appears to return full length file names in the file$info structure.

Does this help?
Les

0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,893 Views
I doubt it would – it's based on FindFirstFile. I didn't carefully examine myself, but I think that FindFirstFile will always return the long name in WFD.cFileName also, however it would match the short name as well.
0 Kudos
michael_green
Beginner
1,893 Views

Just to put Alan's mind at rest ...

The filename "oldgrowth-within-ecosystems$02" is displayed without its extension, which is ".fmd". I presume this would account for the different short names that we get.

Thanks to all for the input.

Mike

0 Kudos
bearoflittlebrain_ol
1,893 Views

Mike,

When I use the wildcard search criterion '*.*'C on oldgrowth-within-ecosystems$02.fmd, I get:

infofile.cFileName= oldgrowth-within-ecosystems$02.fmd

infofile.cAlternateFileName= OLDGRO~1.FMD

The short name that I get is what I would expect from normal file-shortening. If you are getting anything different, it is exacerbating the Deprecated DOS Wildcard bug, and you should try to find out why your short names don't obey the usual rules. Does anyone else know what's happening?

Btw, my attempted fix for it in my search routine cured one part of the problem, but introduced other problems - eg it does not now select .jpeg files. So I will revert to my original routine and wait to see if Jugoslav or some other good soul can get the search functions to effectively ignore the short name.

Regards

Alan

0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,893 Views
Alan, I'm not sure why Mike got "OL4a81~1.FMD" -- maybe he has a ton of OL*.fmd files in the directory; so I'm not sure that you can repeat his exact structure. Regardless of how he got it, the FindFirstFile shouldn't match the short names -- yet it does.

Being on a longish trip, I'm currently not in a position to experiment with that -- did you check out what the code on the link I gave above actually does?
0 Kudos
michael_green
Beginner
1,893 Views

Alan & Jugoslav,

The folder I am dealing with currently contains 895 files, of which 17 long names begin with "oldgrowth". Just for the record, some other files (and short names) which are correctly selected are as follows:

dieback-calm81.fmd(DI57B9~1.FMD)

dieback-swanyint81.fmd (DI52A8~1.FMD)

krp-nnp-81.fmd(KRP-NN~1.FMD)

Thanks again for all the input

Mike

0 Kudos
bearoflittlebrain_ol
1,891 Views

Jugoslav, Mike,

Thanks for solving that mystery for me. Searching using Windows Explorer and the OpenFile Common Dialogue both seem to work correctly, so presumably Microsoft don't use FindFirstFile / FindNextFile for those routines - or else they have developed a fix that they have not shared with us.

Yes, I had looked at Shonichev's article - very interesting on the history, but his fix only covers the extension part of the problem, leaving the main name unfixed. If you create files abcdefgh01, abcdefgh02 ... abcdefgh07, and use his CAdvancedFindFile.exe using *88* as a file spec,both the original and his fix pick up the last 3 files.

I think I can see how to do it (but I have said that before). Almost all of my searches and I guess about 95% of other people's searches use only *. So take the cases without ? initially and match the selected files against the spec. Then if there are any ? enter a more time-consuming detailed matching exercise.

For each file selected:
1. Accept if no Short name or spec is *, ** or *.*
2. Set StarStart and StarEnd flags as appropriate and strip the spec of all *
3. If any ? in the spec skip to 6.
4. There are only 4 combinations of flag settings, so look at the appropriate one and goto 10 if there is a match.
5. Skip to 9
6. Set an array of Query flags as appropriate for the presence of ? in the long file name string.
7. Set up a nested matching system. The outer loop variable is the index in the (long) file name. The inner loop variable is the index in the spec string.
8. Run the loops and goto 10 if there is a match.
9. Failure - go look for the next file.
10. Accept the file and do whatever.

I will post my fix here if I get something that works reasonably well. At the moment it works very nicely on specs with * only, but I have not dug into the ? problem. Perhaps after Easter.

Regards

Alan

0 Kudos
bearoflittlebrain_ol
1,891 Views

Sorry - the files in my last post that I suggested you create for a test should be abcdefgh01.fmd to abcdefgh07.fmd.

Bear of Little Brain

0 Kudos
bearoflittlebrain_ol
1,891 Views

Here is something that works except in one respect in which possibly someone might help me. In the source code scraps below, the line:

Code:

bMatch = Query(j) .OR. lstrcmpi(szName(i+j-1:i+j-1),               &
                                WFileSpec(j:j)) == 0

lstrcmpi does not always return the expected value, in a lot of cases returning the value 1 when comparing 'a' with 'a' rather than the expected 0, so the routine as a whole rejects a number of valid file names.

Code:

!
! Extra declarations in FindFileinTree procedure.
! 
integer*4   :: lSpec, lName        ! Lengths of Spec w/o * & szName
character*MAX_PATH :: szName       ! Filename (Long).
integer*4   :: i, j                ! Working variables.
integer*4   :: iEnd, iStart        ! Limits on index i.
logical*1   :: bMatch              ! Flag - File matches.
logical*1   :: Query(LENCSTR(szFileSpec)) !  in Spec flag array.
!
! Set up conditions for matching Spec to file name (long).
!
StarStart = .FALSE.                       ! Default setting.
WFileSpec = WszFileSpec                   ! Working string.
if (WFileSpec(1:1) == '*') then           ! * at start of Spec.
  WFileSpec = WFileSpec(2:)               ! Delete *.
  StarStart = .TRUE.                      ! Set flag.
end if
StarEnd = .FALSE.                         ! Default setting.
lSpec = LenCstr(WFileSpec)                ! Length of Spec.
if (WFileSpec(lSpec:lSpec) == '*') then   ! * at end of Spec.
  WFileSpec(lSpec:) = WFileSpec(lSpec+1:) ! Delete *.
  lSpec = lSpec - 1                       ! Decrement lSpec.
  StarEnd = .TRUE.                        ! Set flag.
end if
do i = 1, lSpec                           ! Start of search  do loop. 
  Query(i) = WFileSpec == ''             ! Set flag.
end do                                    ! End of search  do loop.
szName = infofile.cFileName               ! File name (Long).
lName = LenCstr(szName)                   ! Length of file name.
      !
      ! Matching Spec with Long name.
      !
      if ((LenCstr(infofile.cAlternateFileName) == 0 .OR. & ! No Short filename
        WszFileSpec == '*'C  .OR.      &                    ! All  
        WszFileSpec == '**'C .OR.      &                    ! files
        WszFileSpec == '*.*'C)) then                        ! selected.
        bMatch = .TRUE.
      else
        !
        ! Set search index limits.
        !
        iStart = 1; iEnd = 1
        if (StarStart .AND. StarEnd) then          ! * at start & end.
          iEnd = lName - lSpec + 1
        else if (StarStart) then                   ! * at start only.
          iStart = lName - lSpec + 1
          iEnd = iStart
        end if
        !
        ! Search for match.
        !
        do i = iStart, iEnd
          bMatch = .TRUE.
          do j = 1, lSpec
            bMatch = Query(j) .OR. lstrcmpi(szName(i+j-1:i+j-1),               &
                                            WFileSpec(j:j)) == 0
            if(.NOT. bMatch) exit
          end do
          if (bMatch) exit
        end do
      end if
      if (bMatch) then
        nPath = nPath + 1                          ! Increment nPath.
        ! And whatever else needs doing on success.



Alan

0 Kudos
bearoflittlebrain_ol
1,891 Views

Users of FindFirstFile / FindNextFile might like to note that even when no short name is produced, a Spec of *.res? will select file asda.res, whereas Windows Explorer does not. If anyone is interested in helping me to validate my fix, contact me at alan.cruttenden@lineone.net and I will send you the complete source code.

Regards

Alan

0 Kudos
bearoflittlebrain_ol
1,891 Views
Ihope thatI haveattached a littleexecutable that fixes a lot of the FindFirstFile/FindNextFile bugs (it's the first attachment that I have tried). If anyone can find any failures, please let me know.
BearofLittleBrain
0 Kudos
michael_green
Beginner
1,891 Views

Hi Alan,

Thanks for the software. I have tested it in my environment and it passed the *81* test that started all this very well. I also tested it with oldgrowth* as the search string and got an odd result. I have attached a Word document containing snapshots of your dialog that demonstrate this. The first shot shows the output when the search string is applied to the real folder, containing 918 files. There are 18 oldgrowth derived files here, but you can see that the last 8 of them appear to be repetitions of the tenth one - clearly impossible.

I then made up a new folder containing junk files having just those 18 file names. The result of this is shown in the second snapshot, and this is quite correct.

There appears to be something odd about the short names of the 4th items in the two lists - they're different, and the short name that appears in the correct list appears as the repeated short name in the failed list. I'm thinking Microsoft bug here.

The only other difference between the two setups (apart from the number of files in each folder - 918 vs 18) is the size of the files. The junk files all contain just "adfasdf", while the the real files are direct access files mostly around 15Mbyte in size.

Thanks again for all your efforts.

Mike

0 Kudos
bearoflittlebrain_ol
1,891 Views

Mike,

Thanks for your observations. As an immediate response, it looks suspiciously as if it is the organisation of presenting the results that is causing the problem (I hope!). Does the result vary depending on what you did before it?

Theinconsistencies withShort Names mightwell be explained by the different number of somewhat-similar filenames in your working file compared to your test file, but something smells there. Incidentally, my Fix does not look at the Short Names at all.

The contents of the file won't make any difference - my Temp directory contains empty files only. If you would like me to work on your precise file names, list them and send them toat theEmail address at the top of the dialogue boxcreated bymy executable, plussome of your typical search criteria.

I am starting on the QA of the fix, so you will not get a new version until that is well advanced, unless the problem was my organisation of the results.

Regards

Alan

0 Kudos
Reply