News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

FindFirstFile/FindNextFile

Started by Ian_B, January 10, 2006, 12:42:35 AM

Previous topic - Next topic

Ian_B

I was going to ask a few rather simple questions about the FindFirstFile and FindNextFile APIs, then out of curiosity did a Google on them to see if they were already answered, and opened up a huge can of potential worms...  :'(

So, I will first offer this link, and wonder if anyone has noticed this problem, or has attempted a similar code-around in Assembler for this apparent bug when wildcards are used, and for problems with the last error number not being consistent or being misgiven when in fact a match was found:

http://www.codeguru.com/Cpp/W-P/files/article.php/c4441/

My first slightly dumb question is this. If I create a search handle with FindFirstFile, perhaps using "*.*" as a wildcard, assuming I don't suffer the bug above, then according to the SDK I keep calling FindNextFile until it returns ERROR_NO_MORE_FILES, then I should know I have iterated through all the files matching the search string in that folder.

But what the SDK doesn't say is what happens to the handle then! If I call FindNextFile again, is the handle "all used up" and will continue to give an error, so must be closed, or will it reset in some way? In particular, if the count of files in the folder is changing, and there are new ones being added and older ones being deleted as I continue to call the function, will the handle remain valid and show me every new file that gets added to that folder, whether or not it ignores the old ones? Or must I close it and renew the handle with a repeat FindFirstFile once it's given the first NO_MORE_FILES error?

The second question is this. Is there a simpler way to enumerate the files in a folder OTHER than calling FindFirstFile/FindNextFile with a "*.*" wildcard and adding one to a counter until it errors NO_MORE_FILES? I can't find a possible API that would do something so basic...  :eek

IanB

MichaelW

#1
FindNextFile will continue to work even after it fails.

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc

    createfile MACRO quotedfname
      invoke CreateFile,reparg(quotedfname),GENERIC_READ,
                FILE_SHARE_READ,NULL,CREATE_ALWAYS,NULL,NULL
      invoke CloseHandle, eax
    ENDM
    findfirst MACRO quotedfname
      mov hFind, rv( FindFirstFile,reparg(quotedfname),ADDR fd )
      print uhex$(hFind),13,10
      .IF hFind == INVALID_HANDLE_VALUE
        print LastError$(),13,10
      .ELSE
        print ADDR fd.cFileName,13,10
      .ENDIF
    ENDM
    findnext MACRO
      invoke FindNextFile, hFind, ADDR fd
      .IF eax == 0
        print LastError$(),13,10
      .ELSE
        print ADDR fd.cFileName,13,10
      .ENDIF
    ENDM
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
      hFind dd 0
      fd WIN32_FIND_DATA <>
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    createfile "0.0"
    createfile "0.1"
    createfile "0.2"
    createfile "1.0"
    createfile "1.1"
    createfile "1.2"
    createfile "2.0"
    createfile "2.1"
    createfile "2.2"

    findfirst "0.*"
    findnext
    findnext
    findnext
    findnext
    createfile "0.3"
    findnext
    findnext
    createfile "0.4"
    findnext
    findnext
    invoke FindClose, hFind

    findfirst "1.*"
    findnext
    findnext
    findnext
    invoke FindClose, hFind

    findfirst "2.*"
    findnext
    findnext
    findnext

    findfirst "*.*"
    findnext
    findnext
    findnext
    findnext
    findnext
    findnext
    findnext
    findnext
    findnext
    findnext
    findnext
    findnext
    findnext
    findnext
    findnext
    findnext
    findnext
    createfile "justonemore.txt"
    findnext
    findnext

    invoke FindClose, hFind
   
    inkey "Press any key to exit..."
    exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
typedef struct _WIN32_FIND_DATA {
  DWORD dwFileAttributes;
  FILETIME ftCreationTime;
  FILETIME ftLastAccessTime;
  FILETIME ftLastWriteTime;
  DWORD nFileSizeHigh;
  DWORD nFileSizeLow;
  DWORD dwReserved0;
  DWORD dwReserved1;
  TCHAR cFileName[MAX_PATH];
  TCHAR cAlternateFileName[14];
} WIN32_FIND_DATA, *PWIN32_FIND_DATA;


00133038
0.0
0.1
0.2
There are no more files.

There are no more files.

0.3
There are no more files.

0.4
There are no more files.

00133038
1.0
1.1
1.2
There are no more files.

00133038
2.0
2.1
2.2
There are no more files.

00134078
.
..
test.asm
test0.asm
test.obj
test.exe
0.0
0.1
0.2
1.0
1.1
1.2
2.0
2.1
2.2
0.3
0.4
There are no more files.

justonemore.txt
There are no more files.


I know of no API or CRT function that can be used to directly enumerate the files in a folder, but it should not be difficult to create one. The MASM32 LoadList procedure performs a similar task.
eschew obfuscation

Ian_B

Quote from: MichaelW on January 10, 2006, 08:51:31 AM
You can continue cycling between FindFirstFile and FindNextFile until you call FindClose.
To be pedantic, that's really a new search, and you created two new search handles without closing the old ones, AFAICT, which would be a serious resource leak if it was continually repeated. And my main question was about using full wildcards because I don't know what might be in the folder, or dumped into it while the program is running, so I can't guess at the names or part of them.

What I'd have liked is to keep open a single search handle for the lifetime of the app that recognised new files as they appeared, as I will be processing the old ones and deleting them. Clearly that means there will be times when the folder is empty and FindNextFile will error, but I don't know whether that will invalidate the search handle or it will continue to work if another file should turn up later. It just seems really inefficient to have to invoke a brand new search using exactly the same parameters just to see whether there are any files in the folder, then close it when I've got a file, if there is one, then redo the search, etc. etc....

Re LoadList, yes that uses FindFirstFile/FindNextFile in exactly the way I suggested, but sending the results as strings to the listbox rather than just counting them. There's a listbox function or perhaps a message you can send it to do that as well, but I suspect it's only (slower) encapsulation for the same code.

IanB

MichaelW

QuoteTo be pedantic, that's really a new search, and you created two new search handles without closing the old ones, AFAICT, which would be a serious resource leak if it was continually repeated.

Yes, I have now verified that FindFirstFile is returning a different handle each time it is called, and my code failed to close two of them. And I added another test that indicates that an error return from FindNextFile does not invalidate the handle or prevent FindNextFile from succeeding on a subsequent call. I have updated my post.

Can files be removed from the folder? If so then I think you will need to start a new search anyway.


eschew obfuscation

Tedd

It's modelled after the DOS functions which worked in exactly the same way. The expectation is that you find the files you want within a fairly short time period and then finish with it.
The reason it will continue to work when you add a new file after it's already finished is because it just iterates over the file system, and only keeps track of the previous file reported. So when you ask for the next one, it will simply hop onto the next file entry, or report none if there isn't one. Adding a new file adds a new entry, and so the next hop will land onto that entry. Removing files shouldn't mess it up, except for the fact that you may have previously reported a file which no longer exists.
No snowflake in an avalanche feels responsible.

Ian_B

Thanks guys, it really depends on what it's storing internally to enable it to continue the search, as Tedd says.

Michael, that's fascinating code, because it throws up two other points that you won't see in the SDK at all! The first is that the full wildcard finds (incorrectly) the DOS directory entries "." and "..", so that will have to be checked for in any code using that wildcard. The second is that the LastError changes after the NO_MORE_FILES, presumably to NO_ERROR, so there's no text output. FindNextFile has produced a zero initial error result though, otherwise the code would output the last filename again, and it doesn't.

Looks like we need to start an unofficial SDK with all the bits MS left out...  :lol

On the plus side, though, this does mean one less problem for me - I will always be able to create an initial search handle using this wildcard, even if the folder is already apparently "empty", because the old DOS directory entries will be visible to it, so this can be part of the app setup without needing further calls to FindFirstFile later.

IanB

MichaelW

Quote
The second is that the LastError changes after the NO_MORE_FILES, presumably to NO_ERROR, so there's no text output.

I don't understand. FindNextFile does not alter the value of LastError when it succeeds, only when it fails (a design flaw IMO). And if the value of LastError were zero, then LastError$ (which calls FormatMessage) would return "The operation completed successfully."

MSDN: System Error Codes (0-499)

eschew obfuscation

Ian_B

My mistake, I miscounted the lines and it looked from my reading of the output that the extra line spacing was produced by a LastError$ result that output a null error string plus the linefeed you specified, but clearly there's an extra linefeed output by the LastError$ macro on top of the one you specified. A few extra FindNexts in there would have shown up the problem...  :wink

sinsi

From what I've seen, XP returns the file/dir names in alphabetical order, so it seems
to be filtered before you get it anyway...
Light travels faster than sound, that's why some people seem bright until you hear them.