Thread: FindNextFile

  1. #1
    Registered User
    Join Date
    Dec 2007
    Posts
    932

    FindNextFile

    Im checking if FindNextFile() return value is zero or GetLastError() != ERROR_NO_MORE_FILES but it still goes into the while() loop.

    What am i doing wrong?

    Actually its working ok as long as there are more than one file but when there is
    only one it gets printed two times.

    Code:
    #include <windows.h>
    #include <iostream>
    using namespace std;
    
    int main()
    {
        char  Data[256] = "C:\\*.txt";
        WIN32_FIND_DATA data;
        HANDLE hFile = FindFirstFileA(Data, &data);
    
        if(hFile != INVALID_HANDLE_VALUE)
        {
           strcpy(Data, data.cFileName);
           cout << "1: " << Data << endl;
        }
    
        while(FindNextFile(hFile, &data)!= 0 || GetLastError() != ERROR_NO_MORE_FILES);
        {
              strcpy(Data, data.cFileName);
              cout << "2: " << Data << endl;
        }
    }
    Using Windows 10 with Code Blocks and MingW.

  2. #2
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    Faulty logic. There's no reason to enter the loop if FindNextFile returns 0.

    gg

  3. #3
    Registered User
    Join Date
    Dec 2007
    Posts
    932
    Thanks Codeplug but i dont get it.

    It shouldnt enter when its zero:

    while(FindNextFile(hFile, &data)!= 0

    means: as long as its not zero, enter... when its zero do not enter...
    Using Windows 10 with Code Blocks and MingW.

  4. #4
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by Ducky View Post
    Thanks Codeplug but i dont get it.

    It shouldnt enter when its zero:

    while(FindNextFile(hFile, &data)!= 0

    means: as long as its not zero, enter... when its zero do not enter...
    except, of course, for the or statement after that.... If the other side is true, you still enter the loop.

  5. #5
    Malum in se abachler's Avatar
    Join Date
    Apr 2007
    Posts
    3,195
    Code:
    while(FindNextFile(hFile, &data)!= 0 && GetLastError() != ERROR_NO_MORE_FILES)
    should fix it

  6. #6
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    Quote Originally Posted by abachler View Post
    Code:
    while(FindNextFile(hFile, &data)!= 0 && GetLastError() != ERROR_NO_MORE_FILES)
    should fix it
    That's wrong too. If FindNextFile() succeeds, then the result of GetLastError() is undefined (or more specifically, it returns the last error set by the last function that failed).
    bit∙hub [bit-huhb] n. A source and destination for information.

  7. #7
    Registered User
    Join Date
    Dec 2007
    Posts
    932
    Thanks for the help!

    Yes with the '&&' doesnt work either.

    By the way shouldnt it supposed to work only by checking the return 0?

    I cant seem to understand what could be the problem.
    Using Windows 10 with Code Blocks and MingW.

  8. #8
    Malum in se abachler's Avatar
    Join Date
    Apr 2007
    Posts
    3,195
    Quote Originally Posted by bithub View Post
    That's wrong too. If FindNextFile() succeeds, then the result of GetLastError() is undefined (or more specifically, it returns the last error set by the last function that failed).
    Wrong, it returns

    ERROR_SUCCESS
    0 (0x0)
    The operation completed successfully.

    http://msdn.microsoft.com/en-us/libr...82(VS.85).aspx

  9. #9
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Quote Originally Posted by abachler View Post
    Wrong, it returns

    ERROR_SUCCESS
    0 (0x0)
    The operation completed successfully.

    http://msdn.microsoft.com/en-us/library/ms681382(VS.85).aspx
    At any rate, it isn't necessary to call GetLastError if the function succeeds...

  10. #10
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    Quote Originally Posted by abachler View Post
    Wrong, it returns

    ERROR_SUCCESS
    0 (0x0)
    The operation completed successfully.

    System Error Codes (0-499) (Windows)
    From the GetLastError() documentation:
    If the function is not documented to set the last-error code, the value returned by this function is simply the most recent last-error code to have been set; some functions set the last-error code to 0 on success and others do not.
    Since the documentation for FindNextFile() doesn't say anything one way or the other, relying on it to return ERROR_SUCCESS is bad program design. There is no requirement at all that the function set ERROR_SUCCESS on a successful call.
    bit∙hub [bit-huhb] n. A source and destination for information.

  11. #11
    Registered User
    Join Date
    Dec 2007
    Posts
    932
    Ok, so if im only checking if return !=0, it enters the loop only once,
    the second time it returns 0 like it didnt find anything else.

    Maybe i should update hFile to the new file that has been just found?

    Code:
    while(FindNextFile(hFile, &data)!= 0);
        {
              cout << "2: " << data.cFileName << endl;
        }
        cout << "2: " << FindNextFile(hFile, &data) << endl;
    Last edited by Ducky; 08-27-2009 at 02:15 AM.
    Using Windows 10 with Code Blocks and MingW.

  12. #12
    Registered User
    Join Date
    Dec 2007
    Posts
    932
    Ok i found the solution!

    You need to use
    Code:
    do{}while
    statement.
    Using Windows 10 with Code Blocks and MingW.

  13. #13
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    You need to do this:
    Find first file.
    If above failed, quit.
    Start loop.
    Find next file.
    If above call failed,
    --> Check if there are no more files.
    --> If there are no more files, quit loop.
    --> Do some error recovery and/or error print/whatever.
    Do what you want with the file.
    Loop.
    Close file handle.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  14. #14
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Quote Originally Posted by Ducky View Post
    Ok i found the solution!

    You need to use
    Code:
    do{}while
    statement.
    Right, because FindFirstFile grabs the first file, if available. Just be sure to check that it succeeds first, obviously.

    Here's a slightly more portable method (not standard, but most systems support the functions used). The general approach here is to encapsulate the traversal logic in a reusable interface that allows you to simply pass callback functions/objects to it that get invoked at key points in the traversing routine, namely before entering a directory, when processing a file, and after exiting the directory. Another feature is that it restores the current working directory to it's original state. It even works with individual file names, so you don't have to incorporate any special logic to detect what type of file-system object you're dealing with.

    Code:
    #include <dirent.h>
    #include <sys/stat.h>
    #include <string>
    
    template < typename PreProcessFolder, typename ProcessFile, typename PostProcessFolder >
    bool file_system_process
    ( 
        std::string const& name,
        PreProcessFolder pre_process_folder,
        ProcessFile process_file,
        PostProcessFolder post_process_folder
    );
    
    template < typename ProcessFile, typename PostProcessFolder >
    inline bool file_system_process
    ( 
        std::string const& name,
        ProcessFile process_file,
        PostProcessFolder post_process_folder
    );
    
    template < typename ProcessFile >
    inline bool file_system_process
    ( 
        std::string const& name,
        ProcessFile process_file
    );
    
    namespace impl_ {
    
    struct DIR_cleanup_helper
    {
    	DIR_cleanup_helper( DIR* directory )
    	: directory( directory )
    	{	}
    	
    	~DIR_cleanup_helper( void )
    	{
    		closedir( directory );
    	}
    	
    	DIR*
    		directory;
    };
    
    template < typename PreProcessFolder, typename ProcessFile, typename PostProcessFolder >
    bool file_system_process_
    ( 
    	std::string const& folder_name, 
    	PreProcessFolder pre_process_folder, 
    	ProcessFile process_file, 
    	PostProcessFolder post_process_folder
    )
    {
    	struct stat
    		attributes;
    	DIR*
    		directory = opendir( folder_name.data( ) );
    	if( directory != 0 )
    	{
    		DIR_cleanup_helper
    			dch( directory );
    		pre_process_folder( folder_name.data( ) );
    		chdir( folder_name.data( ) );
    		dirent*
    			entry;
    		while( ( entry = readdir( directory ) ) != 0 )
    		{
    			std::string
    				name = entry->d_name;
    			if( name == "." || name == ".." )
    				continue;
    			if( stat( name.data( ), &attributes ) == 0 )
    			{
    				if( ( attributes.st_mode & _S_IFDIR ) == _S_IFDIR )
    				{
    					if
    					( 
    						!file_system_process_
    						( 
    							name, 
    							pre_process_folder, 
    							process_file, 
    							post_process_folder 
    						)
    					)
    						return false;
    				}
    				else
    					process_file( name.data( ) );
    			}
    			else
    				return false;
    		}		
    		chdir( ".." );
    		post_process_folder( folder_name.data( ) );
    	}
    	else
    		return false;
    	return true;
    }
    
    void file_system_process_unused_placeholder_( char const* )
    {    }
    
    } // ::impl_
    
    template < typename PreProcessFolder, typename ProcessFile, typename PostProcessFolder >
    bool file_system_process
    ( 
        std::string const& name,
        PreProcessFolder pre_process_folder,
        ProcessFile process_file,
        PostProcessFolder post_process_folder
    )
    {
        std::string
            buffer( 32, 0 );
        while( !getcwd( &buffer[ 0 ], buffer.size( ) ) )
        {
            if( errno == ENODEV )
                return false;
            buffer.resize( buffer.size( ) * 2 );
        }    
        if(
            !impl_::file_system_process_
            (
                name,
                pre_process_folder,
                process_file,
                post_process_folder
            )
        )
        {
            struct stat
                attributes;
            if(
                stat( name.data( ), &attributes ) == 0
                &&
                !( ( attributes.st_mode & _S_IFDIR ) == _S_IFDIR )
            )
            {
                process_file( name.data( ) );
                return true;
            }    
            return false;
        }    
        chdir( buffer.data( ) );
        return true;
    }
    
    template < typename ProcessFile, typename PostProcessFolder >
    inline bool file_system_process
    ( 
        std::string const& name,
        ProcessFile process_file,
        PostProcessFolder post_process_folder
    )
    {
        return file_system_process
        (
            name,
            impl_::file_system_process_unused_placeholder_,
            process_file,
            post_process_folder    
        );
    }
    
    template < typename ProcessFile >
    inline bool file_system_process
    ( 
        std::string const& name,
        ProcessFile process_file
    )
    {
        return file_system_process
        (
            name,
            impl_::file_system_process_unused_placeholder_,
            process_file,
            impl_::file_system_process_unused_placeholder_
        );
    }
    If the functionality you isn't covered by any of those three variants, just pass "place-holder" functions where appropriate. Here's an example usage, a file/folder-erasing function:

    Code:
    #include <string>
    #include <vector>
    #include <fstream>
    
    /*
        TODO: make thread-safe
    */
    bool file_system_erase( std::string const& name );
    
    namespace impl_ {
    
    static bool 
        file_system_erase_error_;
    
    void file_system_erase_file_( char const* name )
    {
        using namespace
            std;
        static size_t
            buffer_size = 1024;
        static vector< char >
            buffer( buffer_size );
        ofstream
            out( name );
        if( out )
        {
            struct stat
                attributes;
            if( stat( name, &attributes ) == 0 )
            {    
                size_t
                    count = 0;        
                while
                (
                    out.write( ( char* )&buffer[ 0 ], buffer_size )
                    &&
                    count < attributes.st_size
                )
                {
                    count += buffer_size;
                }
                if( count >= attributes.st_size )
                {
                    out.close( );
                    if( remove( name ) == 0 )
                        return;
                }
            }    
        }
        file_system_erase_error_ = true;
    }
    
    void file_system_erase_folder_( char const* name )
    {
        if( rmdir( name ) != 0 )
            file_system_erase_error_ = true;
    } 
    
    } // ::impl_
    
    bool file_system_erase( std::string const& name )
    {
        impl_::file_system_erase_error_ = false;
        return
            file_system_process
            (
                name,
                impl_::file_system_erase_file_,
                impl_::file_system_erase_folder_
            )
            &&
            !impl_::file_system_erase_error_;
    }
    And a sample program:

    Code:
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    int main( int argc, char** argv )
    {
        if( argc < 2 )
        {
            cerr << "Simple File System Eraser Utility" << endl;
            cerr << "Usage: " << *argv << " <files_and_folders>" << endl;
            cerr << "Warning: deleted files will NOT be recoverable" << endl;
        }
        else while( *( ++argv ) )
            if( !file_system_erase( *argv ) )
                cerr << "Error processing '" << *argv << "'" << endl;
        return 0;
    }
    Anyway, if nothing else, it may be a good starting point for you, in the event that you want a more customized solution.
    Last edited by Sebastiani; 08-27-2009 at 07:00 AM.

  15. #15
    Registered User
    Join Date
    Dec 2007
    Posts
    932
    Great, thanks both of you!
    Using Windows 10 with Code Blocks and MingW.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. FindFirstFile and FindNextFile
    By scwizzo in forum Windows Programming
    Replies: 15
    Last Post: 03-12-2008, 04:50 PM
  2. Msdn example code (FindFirstFile, FindNextFile
    By Probose in forum Windows Programming
    Replies: 2
    Last Post: 09-22-2006, 04:16 PM
  3. FindNextFile()
    By siavoshkc in forum C++ Programming
    Replies: 9
    Last Post: 03-03-2006, 09:47 AM
  4. m_dir = system("dir c:")
    By Ray Schmidt in forum Windows Programming
    Replies: 4
    Last Post: 03-09-2003, 09:39 AM
  5. directories, lccwin32 and findnextfile
    By ggs in forum Windows Programming
    Replies: 6
    Last Post: 07-23-2002, 10:00 PM