Search code examples
c++cwindowsdirectorylistings

Listing directories in c++ under windows


I want to list all files of the current directory, so I have this code :

int WLoader::listdir(void)
{
    WIN32_FIND_DATA data;
    std::wstring path(L"*");
    std::wstring *name;
    HANDLE hFile = FindFirstFile(path.c_str(), &data);

    if  (hFile == INVALID_HANDLE_VALUE)
       return (-1);

    while(FindNextFile(hFile, &data) != 0 || GetLastError() != ERROR_NO_MORE_FILES)
    {
        std::cout << data.cFileName << std::endl;
    }
    return (0);
}

For unknown reasons, my program is displaying this result :

0029F29C
0029F29C
0029F29C
0029F29C
0029F29C
0029F29C

Can someone help me please?


Solution

  • The WIN32_FIND_DATA structure's member cFileName is a TCHAR[N], and TCHAR is a Windows type alias that maps either to char or wchar_t. Problem is, you don't know which one it will be when you write your code.

    Depending on your build settings, you either have a char*, or a wchar_t*; one should be used with std::cout and the other must be used with std::wcout. But which one do you use?!

    Fortunately, there's a macro to find out which is in use when you compile:

    while(FindNextFile(hFile, &data) != 0 || GetLastError() != ERROR_NO_MORE_FILES) {
    #ifdef UNICODE
       std::wcout << data.cFileName << std::endl;
    #else
       std::cout << data.cFileName << std::endl;
    #endif
    }
    

    You're going to find the same problem if you try to assign the filename to a std::string/std::wstring. That's what you get for using the Windows API. :)


    One way around this is to define macros for the output stream and for strings.

    So, somewhere at the top of your program:

    #ifdef UNICODE
    #define STDCOUT std::wcout
    #define STDSTR  std::wstring
    #else
    #define STDCOUT std::cout
    #define STDSTR  std::string
    #endif
    

    Then in your function, all you need is:

    while(FindNextFile(hFile, &data) != 0 || GetLastError() != ERROR_NO_MORE_FILES) {
       STDCOUT << data.cFileName << std::endl;
    }
    

    and you can use STDSTR elsewhere.

    Something to consider.