How to Quickly Enumerate Directories on Win32

How to list files in a directory using the Windows API?

HANDLE hFind = FindFirstFile("C:\\semester2", &data);       // DIRECTORY

You got the directory because that's what you asked for. If you want the files, ask for them:

HANDLE hFind = FindFirstFile("C:\\semester2\\*", &data);  // FILES

(You can instead use *.* if you prefer, but apparently this only works because of a backwards compatibility hack so should probably be avoided. See comments and RbMm's answer.)

Displaying all the files in a folder using win32 C++

Your while statement has an erroneous ; on it:

while (FindNextFile(hFind, &FindFileData) != 0); // <-- here

Thus, you loop without processing FindFileData until FindNextFile() fails, AND THEN you process the last FindFileData data that was reported. And that display ends up wiping out the error code that the failed FindNextFile() reported, which is why the error code is no longer ERROR_NO_MORE_FILES when you actually retrieve it.

You need to remove that erroneous ;.

Also, you should use a do..while loop instead. Using a while loop will skip the 1st file that FindFirstFile() reports.

Try this instead:

case IDC_BUTTON_RUN:

hFind = FindFirstFile(L"C:\\Users\\sallen\\Desktop\\Folder1\\*", &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
fileError = GetLastError();
if (fileError == ERROR_FILE_NOT_FOUND)
{
fileError = 0;
MessageBox(NULL, L"No files were found.", L"File Search Error", MB_OK | MB_ICONWARNING);
}
else
{
wsprintfW(file_buff, L"Error finding files. %d", fileError);
MessageBox(hWnd, file_buff, L"File Search Error", MB_OK | MB_ICONERROR);
}
return fileError;
}

do
{
if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
{
MessageBox(hWnd, FindFileData.cFileName, L"File Name", MB_OK | MB_ICONINFORMATION);
}
}
while (FindNextFile(hFind, &FindFileData));

fileError = GetLastError();
FindClose(hFind);

if (fileError != ERROR_NO_MORE_FILES)
{
wsprintfW(file_buff, L"Error finding files: %d", fileError);
MessageBox(hWnd, file_buff, L"File Search Error", MB_OK | MB_ICONERROR);
}
else
fileError = 0;

return fileError;

UPDATE: to handle sub-folders, you will have to move your code into a recursive function, eg:

DWORD SearchFolder(LPCTSTR folder)
{
WIN32_FIND_DATA FindFileData;
TCHAR mask[MAX_PATH+3], filePath[MAX_PATH];
DWORD dwError, dwResult;

PathCombine(mask, folder, L"*");

HANDLE hFind = FindFirstFile(mask, &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
return GetLastError();

dwResult = ERROR_NO_FILES_FOUND;

do
{
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (lstrcmp(FindFileData.cFileName, ".") == 0 || lstrcmp(FindFileData.cFileName, "..") == 0)
continue;

PathCombine(filePath, folder, FindFileData.cFileName);
dwError = SearchFolder(filePath);

if (dwError != NO_ERROR && dwError != ERROR_NO_FILES_FOUND) {
SetLastError(dwError);
break;
}
}
else
{
// do something with FindFileData.cFileName ...

PathCombine(filePath, folder, FindFileData);
...

dwResult = NO_ERROR;
}
}
while (FindNextFile(hFind, &FindFileData));

dwError = GetLastError();
FindClose(hFind);

if (dwError != ERROR_NO_MORE_FILES)
return dwError;

return dwResult;
}

...

case IDC_BUTTON_RUN:
{
DWORD dwResult = SearchFolder(L"C:\\Users\\sallen\\Desktop\\Folder1\\");
if (dwResult != NO_ERROR)
{
if (dwResult == ERROR_NO_FILES_FOUND) {
MessageBox(NULL, L"No files were found.", L"File Search Error", MB_OK | MB_ICONWARNING);
}
else {
swprintf(file_buff, L"Error finding files. %u", dwError);
MessageBox(hWnd, file_buff, L"File Search Error", MB_OK | MB_ICONERROR);
}
}
return dwError;
}

How to get list of directories and files with full path using win32 in c# application

You can put your code into a rekursive function, and call it for each folder:

static void Main()
{

WIN32_FIND_DATA findData;

FINDEX_INFO_LEVELS findInfoLevel = FINDEX_INFO_LEVELS.FindExInfoBasic;

int additionalFlags = 0;

if (Environment.OSVersion.Version.Major >= 6)
{
findInfoLevel = FINDEX_INFO_LEVELS.FindExInfoBasic;
//additionalFlags = FIND_FIRST_EX_LARGE_FETCH;
}

string startFolder = @"D:\";

var watch = new Stopwatch();
watch.Start();

IterateFolder(findInfoLevel, additionalFlags, startFolder);

watch.Stop();

Console.WriteLine();
Console.WriteLine($"{watch.Elapsed.TotalSeconds}");
Console.ReadLine();

}

public const uint FILE_ATTRIBUTE_DIRECTORY = 0x10;

private static void IterateFolder(FINDEX_INFO_LEVELS findInfoLevel, int additionalFlags, string folder)
{
WIN32_FIND_DATA findData;
IntPtr hFile = FindFirstFileEx(
$"{folder}\\*.*",
findInfoLevel,
out findData,
FINDEX_SEARCH_OPS.FindExSearchNameMatch,
IntPtr.Zero,
additionalFlags);

int error = Marshal.GetLastWin32Error();

if (hFile.ToInt32() != -1)
{
do
{
if (findData.cFileName == "." || findData.cFileName == "..") continue;//ignore folder navigation
var foundFolder = $"{folder}\\{findData.cFileName}";
Console.WriteLine("Found file {0}, & Attribute is {1}", foundFolder, findData.dwFileAttributes);
if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) > 0)
{
//Recursive for directories
IterateFolder(findInfoLevel, additionalFlags, foundFolder);
}
}
while (FindNextFile(hFile, out findData));

FindClose(hFile);

}

}

How can you iterate though directories without using any recursive functions in c?

There are two specific methods: breadth-first traversal implemented with queues and depth-first traversal implemented with stacks.

I explain in detail how to use queues (similar to using stacks):

  1. First add the path of this folder to a queue.
  2. Determine whether the number of elements in the queue is greater than 0. If the number of elements is greater than 0, traverse the
    folder corresponding to the first element. If there is a folder in
    this folder, add the path of this folder to the queue. After scanning
    a folder, the first element is popped out of the queue, and the second
    step is continued. If there is no element in the queue, the third step
    is executed.
  3. Exit the loop.

Here is the sample I implemented in C++:

#include <Windows.h>
#include <iostream>
#include <queue>
using namespace std;

void QueryFileCounts(string Path)
{
queue<std::string> qFolders;
qFolders.push(Path);

WIN32_FIND_DATA findResult;
HANDLE handle = NULL;

while (qFolders.size() > 0)
{
std::string tempFolder = qFolders.front();
tempFolder.append("\\*.*");
handle = FindFirstFile(tempFolder.c_str(), &findResult);
do
{
if (findResult.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (lstrcmp(".", findResult.cFileName) == 0 || lstrcmp("..", findResult.cFileName) == 0)
{
continue;
}
tempFolder = qFolders.front();
tempFolder.append("\\").append(findResult.cFileName);
qFolders.push(tempFolder);
}
else {
cout << findResult.cFileName << endl;
}
} while (FindNextFile(handle, &findResult));
qFolders.pop();
}
if (handle)
{
FindClose(handle);
handle = NULL;
}
}

int main()
{
QueryFileCounts("D:\\test");
return 0;
}

Edit:

There are many ways to implement queues and stacks. I recommend that you use chained queues (or chained stacks). You can find many ways to implement them on the Internet. I will provide a way of my implementation for your reference.

Here is the sample implemented in pure C:

#include <stdio.h>
#include <Windows.h>
#include <string.h>
struct Link
{
char data[1024];
struct Link* next;
};

struct Queue
{
struct Link* front;
struct Link* rear;
int size;
};

void QueueInit(struct Queue* queue)
{
queue->front = NULL;
queue->rear = NULL;
queue->size = 0;
}

int QueueEmpty(struct Queue* queue)
{
return (queue->size == 0);
}

void QueuePush(struct Queue* queue, const char * data)
{
struct Link* node;
node = (struct Link*)malloc(sizeof(struct Link));
if (node)
{
strcpy(node->data, data);
node->next = NULL;
if (QueueEmpty(queue))
{
queue->front = node;
queue->rear = node;
}
else
{
queue->rear->next = node;
queue->rear = node;
}
++queue->size;
}

}

int QueuePop(struct Queue* queue)
{
if (QueueEmpty(queue))
{
return 0;
}
struct Link* tmp = queue->front;
queue->front = queue->front->next;
free(tmp);
--queue->size;
return 1;
}

void QueueDestroy(struct Queue* queue)
{
struct Link* tmp;
while (queue->front)
{
tmp = queue->front;
queue->front = queue->front->next;
free(tmp);
}
}

void QueryFileCounts(const char * Path)
{
struct Queue qFolders;
QueueInit(&qFolders);
QueuePush(&qFolders, Path);
WIN32_FIND_DATA findResult;
HANDLE handle = NULL;

while (qFolders.size > 0)
{
char tempFolder[1024];
strcpy(tempFolder, qFolders.front->data);
sprintf(tempFolder, "%s\\*.*", tempFolder);
handle = FindFirstFile(tempFolder, &findResult);
do
{
if (findResult.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (lstrcmp(".", findResult.cFileName) == 0 || lstrcmp("..", findResult.cFileName) == 0)
{
continue;
}
strcpy(tempFolder, qFolders.front->data);
sprintf(tempFolder, "%s\\%s", tempFolder, findResult.cFileName);
QueuePush(&qFolders, tempFolder);
}
else {
printf("%s\n", findResult.cFileName);
}
} while (FindNextFile(handle, &findResult));
QueuePop(&qFolders);
}
if (handle)
{
FindClose(handle);
handle = NULL;
}
QueueDestroy(&qFolders);
}

int main()
{
QueryFileCounts("D:\\test");
return 0;
}

What is the MS Win32 API to know a folder is shortcuts?

I have known the answer, see on this blog.
http://blogs.msdn.com/oldnewthing/archive/2004/12/27/332704.aspx

Listing directory contents using C and Windows

Just like everyone else said (with FindFirstFile, FindNextFile and FindClose)... but with recursion!

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

bool ListDirectoryContents(const char *sDir)
{
WIN32_FIND_DATA fdFile;
HANDLE hFind = NULL;

char sPath[2048];

//Specify a file mask. *.* = We want everything!
sprintf(sPath, "%s\\*.*", sDir);

if((hFind = FindFirstFile(sPath, &fdFile)) == INVALID_HANDLE_VALUE)
{
printf("Path not found: [%s]\n", sDir);
return false;
}

do
{
//Find first file will always return "."
// and ".." as the first two directories.
if(strcmp(fdFile.cFileName, ".") != 0
&& strcmp(fdFile.cFileName, "..") != 0)
{
//Build up our file path using the passed in
// [sDir] and the file/foldername we just found:
sprintf(sPath, "%s\\%s", sDir, fdFile.cFileName);

//Is the entity a File or Folder?
if(fdFile.dwFileAttributes &FILE_ATTRIBUTE_DIRECTORY)
{
printf("Directory: %s\n", sPath);
ListDirectoryContents(sPath); //Recursion, I love it!
}
else{
printf("File: %s\n", sPath);
}
}
}
while(FindNextFile(hFind, &fdFile)); //Find the next file.

FindClose(hFind); //Always, Always, clean things up!

return true;
}

ListDirectoryContents("C:\\Windows\\");

And now its UNICODE counterpart:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

bool ListDirectoryContents(const wchar_t *sDir)
{
WIN32_FIND_DATA fdFile;
HANDLE hFind = NULL;

wchar_t sPath[2048];

//Specify a file mask. *.* = We want everything!
wsprintf(sPath, L"%s\\*.*", sDir);

if((hFind = FindFirstFile(sPath, &fdFile)) == INVALID_HANDLE_VALUE)
{
wprintf(L"Path not found: [%s]\n", sDir);
return false;
}

do
{
//Find first file will always return "."
// and ".." as the first two directories.
if(wcscmp(fdFile.cFileName, L".") != 0
&& wcscmp(fdFile.cFileName, L"..") != 0)
{
//Build up our file path using the passed in
// [sDir] and the file/foldername we just found:
wsprintf(sPath, L"%s\\%s", sDir, fdFile.cFileName);

//Is the entity a File or Folder?
if(fdFile.dwFileAttributes &FILE_ATTRIBUTE_DIRECTORY)
{
wprintf(L"Directory: %s\n", sPath);
ListDirectoryContents(sPath); //Recursion, I love it!
}
else{
wprintf(L"File: %s\n", sPath);
}
}
}
while(FindNextFile(hFind, &fdFile)); //Find the next file.

FindClose(hFind); //Always, Always, clean things up!

return true;
}

ListDirectoryContents(L"C:\\Windows\\");

Is there a faster way than this to find all the files in a directory and all sub directories?

Try this iterator block version that avoids recursion and the Info objects:

public static IEnumerable<string> GetFileList(string fileSearchPattern, string rootFolderPath)
{
Queue<string> pending = new Queue<string>();
pending.Enqueue(rootFolderPath);
string[] tmp;
while (pending.Count > 0)
{
rootFolderPath = pending.Dequeue();
try
{
tmp = Directory.GetFiles(rootFolderPath, fileSearchPattern);
}
catch (UnauthorizedAccessException)
{
continue;
}
for (int i = 0; i < tmp.Length; i++)
{
yield return tmp[i];
}
tmp = Directory.GetDirectories(rootFolderPath);
for (int i = 0; i < tmp.Length; i++)
{
pending.Enqueue(tmp[i]);
}
}
}

Note also that 4.0 has inbuilt iterator block versions (EnumerateFiles, EnumerateFileSystemEntries) that may be faster (more direct access to the file system; less arrays)



Related Topics



Leave a reply



Submit