0

I need to get a list of folders in the directory, but only the folders. No files are needed. Only folders. I use filters to determine if this is a folder, but they do not work and all files and folders are output.

string root = "D:\\*";
cout << "Scan " << root << endl;
std::wstring widestr = std::wstring(root.begin(), root.end());
const wchar_t* widecstr = widestr.c_str();
WIN32_FIND_DATAW wfd;
HANDLE const hFind = FindFirstFileW(widecstr, &wfd);

In this way, I check that it is a folder.

if (INVALID_HANDLE_VALUE != hFind)
    if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))

How to solve a problem?

xom9ikk
  • 2,159
  • 5
  • 20
  • 27
  • https://stackoverflow.com/questions/5043403/listing-only-folders-in-directory – Hakan SONMEZ Oct 05 '17 at 15:03
  • 1
    Swears on #include and on DIR – xom9ikk Oct 05 '17 at 15:04
  • 1
    windows current not support this. you can you [`FindFirstFileEx`](https://msdn.microsoft.com/en-us/library/windows/desktop/aa364419(v=vs.85).aspx) with *fSearchOp* set to [`FindExSearchLimitToDirectories`](https://msdn.microsoft.com/en-us/library/windows/desktop/aa364416(v=vs.85).aspx) - but this flag have no effect now – RbMm Oct 05 '17 at 15:10
  • RbMm, help me please. How to implement what you described in my example? – xom9ikk Oct 05 '17 at 15:15
  • @Xom9ik - how i say windos **not support** directory filtering. you can write `FindFirstFileExW(L"D:\\*", FindExInfoBasic, &fd, FindExSearchLimitToDirectories, 0, FIND_FIRST_EX_LARGE_FETCH )` but `FindExSearchLimitToDirectories` have no effect – RbMm Oct 05 '17 at 15:19
  • @RbMm - This task can not be solved in any way? :( Can eat any other ways? – xom9ikk Oct 05 '17 at 15:22
  • 1
    @Xom9ik - check for `FILE_ATTRIBUTE_DIRECTORY`. you already do this. and `FindFirstFileExW` (with `FIND_FIRST_EX_LARGE_FETCH` and `FindExInfoBasic` much more faster. and can be make in several times faster (on ssd disk) if use asynchronous handles with `ZwQueryDirectoryFile` and not wait result but have multiple queries at once – RbMm Oct 05 '17 at 15:25

2 Answers2

3

There are two ways to do this: The hard way, and the easy way.

The hard way is based on FindFirstFile and FindNextFile, filtering out directories as needed. You will find a bazillion samples that outline this approach, both on Stack Overflow as well as the rest of the internet.

The easy way: Use the standard directory_iterator class (or recursive_directory_iterator, if you need to recurse into subdirectories). The solution is as simple as1:

for ( const auto& entry : directory_iterator( path( L"abc" ) ) ) {
    if ( is_directory( entry.path() ) ) {
        // Do something with the entry
        visit( entry.path() );
    }
}

You will have to include the <filesystem> header file, introduced in C++17.

Note: Using the latest version of Visual Studio 2017 (15.3.5), this is not yet in namespace std. You will have to reference namespace std::experimental::filesystem instead.


1 Note in particular, that there is no need to filter out the . and .. pseudo-directories; those aren't returned by the directory iterators.
IInspectable
  • 46,945
  • 8
  • 85
  • 181
2

This function collects folders into given vector. If you set recursive to true, it will be scanning folders inside folders inside folders etc.

// TODO: proper error handling.

void GetFolders( std::vector<std::wstring>& result, const wchar_t* path, bool recursive )
{
    HANDLE hFind;
    WIN32_FIND_DATA data;
    std::wstring folder( path );
    folder += L"\\";
    std::wstring mask( folder );
    mask += L"*.*";

    hFind=FindFirstFile(mask.c_str(),&data);
    if(hFind!=INVALID_HANDLE_VALUE)
    {
        do
        {
            std::wstring    name( folder );
            name += data.cFileName;
            if ( ( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
                 // I see you don't want FILE_ATTRIBUTE_REPARSE_POINT
                 && !( data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) )
            {
                // Skip . and .. pseudo folders.
                if ( wcscmp( data.cFileName, L"." ) != 0 && wcscmp( data.cFileName, L".." ) != 0 )
                {
                    result.push_back( name );
                    if ( recursive )
                        // TODO: It would be wise to check for cycles!
                        GetFolders( result, name.c_str(), recursive );
                }
            }
        } while(FindNextFile(hFind,&data));
    }
    FindClose(hFind);
}

Modified from https://stackoverflow.com/a/46511952/8666197

Daniel Sęk
  • 2,504
  • 1
  • 8
  • 17