40 out of 55 rated this helpful - Rate this topic

Listing the Files in a Directory

The following example calls FindFirstFile, FindNextFile, and FindClose to list files in a specified directory.


#include <windows.h>
#include <tchar.h> 
#include <stdio.h>
#include <strsafe.h>
#pragma comment(lib, "User32.lib")

void DisplayErrorBox(LPTSTR lpszFunction);

int _tmain(int argc, TCHAR *argv[])
{
   WIN32_FIND_DATA ffd;
   LARGE_INTEGER filesize;
   TCHAR szDir[MAX_PATH];
   size_t length_of_arg;
   HANDLE hFind = INVALID_HANDLE_VALUE;
   DWORD dwError=0;
   
   // If the directory is not specified as a command-line argument,
   // print usage.

   if(argc != 2)
   {
      _tprintf(TEXT("\nUsage: %s <directory name>\n"), argv[0]);
      return (-1);
   }

   // Check that the input path plus 3 is not longer than MAX_PATH.
   // Three characters are for the "\*" plus NULL appended below.

   StringCchLength(argv[1], MAX_PATH, &length_of_arg);

   if (length_of_arg > (MAX_PATH - 3))
   {
      _tprintf(TEXT("\nDirectory path is too long.\n"));
      return (-1);
   }

   _tprintf(TEXT("\nTarget directory is %s\n\n"), argv[1]);

   // Prepare string for use with FindFile functions.  First, copy the
   // string to a buffer, then append '\*' to the directory name.

   StringCchCopy(szDir, MAX_PATH, argv[1]);
   StringCchCat(szDir, MAX_PATH, TEXT("\\*"));

   // Find the first file in the directory.

   hFind = FindFirstFile(szDir, &ffd);

   if (INVALID_HANDLE_VALUE == hFind) 
   {
      DisplayErrorBox(TEXT("FindFirstFile"));
      return dwError;
   } 
   
   // List all the files in the directory with some info about them.

   do
   {
      if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
      {
         _tprintf(TEXT("  %s   <DIR>\n"), ffd.cFileName);
      }
      else
      {
         filesize.LowPart = ffd.nFileSizeLow;
         filesize.HighPart = ffd.nFileSizeHigh;
         _tprintf(TEXT("  %s   %ld bytes\n"), ffd.cFileName, filesize.QuadPart);
      }
   }
   while (FindNextFile(hFind, &ffd) != 0);
 
   dwError = GetLastError();
   if (dwError != ERROR_NO_MORE_FILES) 
   {
      DisplayErrorBox(TEXT("FindFirstFile"));
   }

   FindClose(hFind);
   return dwError;
}


void DisplayErrorBox(LPTSTR lpszFunction) 
{ 
    // Retrieve the system error message for the last-error code

    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError(); 

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );

    // Display the error message and clean up

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
        (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR)); 
    StringCchPrintf((LPTSTR)lpDisplayBuf, 
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"), 
        lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); 

    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
}



 

 

Send comments about this topic to Microsoft

Build date: 4/17/2012

Did you find this helpful?
(1500 characters remaining)
Community Content Add
Annotations FAQ
Console apps and codepage

If a directory contains characters outside of the current codepage, be it ANSI or OEM then you will get this behaviour, so it is expected. If a directory contains a character which isn't in the current codepage then it will be converted to either a fallback character or the substitute character. Example of fallback characters is fullwidth Latin characters from East Asian codepages get converted to the standard Latin characters in western codepages. If there is no fallback character in the current codepage then it will convert the character to the substitute character, which is a ?. These are basics that you should know, especially after dealing with MultiByteToWideChar and WideCharToMultiByte.

Whats more, directories going in will be converted based on the input codepage. So again, these will be converted to ?s or the fallback character. Which is why it will fail with either error 3 (file not found) or error 123 (incorrect syntax).

This is why using Unicode builds isn't a work around, it is the recommended way. You also don't have the conversion overhead that using the non Unicode version has and it also does no conversion on the input parameters so you get what you type in. Whats more, the output is dependent on the font installed on your system, so if you have a font which supports the correct character ranges then the output will be properly.

WIndows 7 problem
This program doesn't work on Windows 7 machine, when trying to get information from a UNIX machine. Works well on Windows XP though.
Console apps need to use the OEM codepage

Note: The above example when compiled for 8-bit strings (LPCSTR) will display incorrectly if a file's name contains characters outside of the 7-bit ASCII character set. And FindFirstFile will fail if argv[1] contains such characters. Console applications use the OEM codepage, not the ANSI codepage. The workaround is to build the example with UNICODE (LPCWSTR). Or call SetFileApisToOem() and use CharToOem(argv[i]) for each argv argument

Ditto the use of the global array __argv. See also setlocale(LC_ALL, ".OCP")

Update for East-Asian Users: After I posted the above information my Chinese friends have informed me that the OEM code page might not be used by the console window in certain East-Asian language encodings provided in the MUI edition of Windows XP. If (for example) the user switches to MUI=Chinese Simplified, the console switches to the Chinese ANSI code page 936 (simplified Chinese GBK encoding). In this case the console is using the ANSI code page, not the OEM code page 437, so the above code will work as designed (ironically enough).