293 lines
11 KiB
C++
293 lines
11 KiB
C++
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Enumdir.cpp
|
|
|
|
Abstract:
|
|
|
|
Public functions exposed by the directory
|
|
enumeration class.
|
|
|
|
Notes:
|
|
|
|
Unicode only right now.
|
|
|
|
History:
|
|
|
|
02/21/2001 rparsons Created
|
|
|
|
--*/
|
|
|
|
#include "enumdir.h"
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function walks a directory tree, depth first, calling the
|
|
passed enumeration routine for each directory and file found
|
|
in the tree. The enumeration routine is passed the full path
|
|
of the file, the directory information associated with the file
|
|
and an enumeration parameter that is uninterpreted by this
|
|
function.
|
|
|
|
Arguments:
|
|
|
|
DirectoryPath - Absolute or relative path to the directory that
|
|
will is the root of the tree to enumerate.
|
|
|
|
EnumerateRoutine - Pointer to an enumeration routine to call
|
|
for each file and directory found.
|
|
|
|
fEnumSubDirs - Indicates if we should enumerate
|
|
subdirectories.
|
|
|
|
EnumerateParameter - Uninterpreted 32-bit value that is passed
|
|
to the EnumerationRoutine each time it is called.
|
|
|
|
Return Value:
|
|
|
|
TRUE if operation was successful. Otherwise returns FALSE and
|
|
extended error information is available from GetLastError()
|
|
|
|
--*/
|
|
BOOL
|
|
CEnumDir::EnumerateDirectoryTree(
|
|
IN LPCWSTR DirectoryPath,
|
|
IN PDIRECTORY_ENUMERATE_ROUTINE EnumerateRoutine,
|
|
IN BOOL fEnumSubDirs,
|
|
IN PVOID EnumerateParameter
|
|
)
|
|
{
|
|
BOOL fResult;
|
|
VIRTUAL_BUFFER Buffer;
|
|
PENUMERATE_DIRECTORY_STATE State;
|
|
PENUMERATE_DIRECTORY_STACK Stack;
|
|
WIN32_FIND_DATA FindFileData;
|
|
|
|
//
|
|
// Create a virtual buffer with an initial committed size of
|
|
// our directory state buffer, and a maximum reserved size of
|
|
// the longest possible full path based on the maximum depth
|
|
// we handle and the maximum length of each path component.
|
|
//
|
|
if (!this->CreateVirtualBuffer(&Buffer,
|
|
sizeof(ENUMERATE_DIRECTORY_STATE),
|
|
sizeof(ENUMERATE_DIRECTORY_STATE) +
|
|
MAX_DEPTH * MAX_PATH)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// This buffer will be used to maintain a stack of directory
|
|
// search handles, as well as accumulate the full path string
|
|
// as we descend the directory tree.
|
|
//
|
|
State = (PENUMERATE_DIRECTORY_STATE)Buffer.Base;
|
|
State->Depth = 0;
|
|
Stack = &State->Stack[0];
|
|
|
|
//
|
|
// Enter a try ... finally block so we can insure that we clean
|
|
// up after ourselves on exit.
|
|
//
|
|
__try {
|
|
|
|
//
|
|
// First translate the passed in DirectoryPath into a fully
|
|
// qualified path. This path will be the initial value in
|
|
// our path buffer. The initial allocation of the path buffer
|
|
// is big enough for this initial request, so does not need
|
|
// to be guarded by a try ... except clause.
|
|
//
|
|
if (GetFullPathName(DirectoryPath, MAX_PATH, State->Path, &Stack->PathEnd)) {
|
|
|
|
//
|
|
// Now enter a try ... except block that will be used to
|
|
// manage the commitment of space in the path buffer as
|
|
// we append subdirectory names and file names to it.
|
|
// Using the virtual buffer allows us to handle full
|
|
// path names up to 16KB in length, with an initial
|
|
// allocation of 4KB.
|
|
//
|
|
__try {
|
|
|
|
//
|
|
// Walk the directory tree. The outer loop is executed
|
|
// once for each directory in the tree.
|
|
//
|
|
while (TRUE) {
|
|
|
|
startDirectorySearch:
|
|
|
|
//
|
|
// Find the end of the current path, and make sure
|
|
// there is a trailing path separator.
|
|
//
|
|
Stack->PathEnd = wcschr( State->Path, '\0' );
|
|
if (Stack->PathEnd > State->Path && Stack->PathEnd[ -1 ] != '\\') {
|
|
*(Stack->PathEnd)++ = '\\';
|
|
}
|
|
|
|
//
|
|
// Now append the wild card specification that will
|
|
// let us enumerate all the entries in this directory.
|
|
// Call FindFirstFile to find the first entry in the
|
|
// directory.
|
|
//
|
|
wcscpy( Stack->PathEnd, L"*.*" );
|
|
Stack->FindHandle = FindFirstFile( State->Path,
|
|
&FindFileData
|
|
);
|
|
if (Stack->FindHandle != INVALID_HANDLE_VALUE) {
|
|
|
|
//
|
|
// Entry found. Now loop through the entire
|
|
// directory processing each entry found,
|
|
// including the first one.
|
|
//
|
|
do {
|
|
|
|
//
|
|
// Ignore bogus pseudo-directories that are
|
|
// returned by some file systems (e.g. FAT).
|
|
//
|
|
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
|
|
(!wcscmp(FindFileData.cFileName, L".") ||
|
|
!wcscmp(FindFileData.cFileName, L"..") ||
|
|
!wcscmp(FindFileData.cFileName, L"System Volume Information" ) ||
|
|
!wcscmp(FindFileData.cFileName, L"Recycler"))
|
|
)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Copy the file name portion from the current
|
|
// directory entry to the last component in the
|
|
// path buffer.
|
|
//
|
|
wcscpy( Stack->PathEnd, FindFileData.cFileName);
|
|
|
|
//
|
|
// Call the supplied enumeration routine with the
|
|
// full path we have built up in the path buffer,
|
|
// the directory information for this directory
|
|
// entry and the supplied enumeration parameter.
|
|
//
|
|
(*EnumerateRoutine)(State->Path, &FindFileData, EnumerateParameter);
|
|
|
|
//
|
|
// If this is entry is a subdirectory, then it is
|
|
// time to recurse. Do this by incrementing the
|
|
// stack pointer and depth and jumping to the top
|
|
// of the outer loop to process current contents
|
|
// of the path buffer as a fully qualified name of
|
|
// a directory.
|
|
//
|
|
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && fEnumSubDirs) {
|
|
Stack++;
|
|
State->Depth++;
|
|
goto startDirectorySearch;
|
|
restartDirectorySearch: ;
|
|
}
|
|
|
|
//
|
|
// Here to find the next entry in the current directory.
|
|
//
|
|
}
|
|
|
|
while (FindNextFile( Stack->FindHandle, &FindFileData));
|
|
|
|
//
|
|
// No more entries in the current directory, so close
|
|
// the search handle and fall into the code that will
|
|
// pop our stack of directory seacrh handles.
|
|
//
|
|
|
|
FindClose(Stack->FindHandle);
|
|
}
|
|
|
|
//
|
|
// Here when done with a directory. See if we are pushed
|
|
// inside another directory. If not, then we are done
|
|
// enumerating the whole tree, so break out of the loop.
|
|
//
|
|
if (!State->Depth) {
|
|
fResult = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We were pushed within another directory search,
|
|
// so pop the stack to restore its search handle
|
|
// and path buffer position and resume the search
|
|
// within that directory.
|
|
//
|
|
State->Depth--;
|
|
--Stack;
|
|
goto restartDirectorySearch;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Any of the code that appends to the path buffer within
|
|
// the above try ... except clause can cause an access
|
|
// violation if the path buffer becomes longer than its
|
|
// current committed size. This exception filter
|
|
// will dynamically commit additional pages as needed
|
|
// and resume execution.
|
|
//
|
|
_except( this->VirtualBufferExceptionFilter(GetExceptionCode(),
|
|
GetExceptionInformation(),
|
|
&Buffer)) {
|
|
|
|
//
|
|
// We will get here if the exception filter was unable to
|
|
// commit the memory.
|
|
//
|
|
fResult = FALSE;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Initial GetFullPathName failed, so return a failure.
|
|
//
|
|
fResult = FALSE;
|
|
}
|
|
}
|
|
|
|
__finally {
|
|
|
|
//
|
|
// Here on our way out of the outer try ... finally block.
|
|
// Make sure all our search handles have been closed and then
|
|
// free the virtual buffer. The only way this code is not
|
|
// executed is if code within the try ... finally block
|
|
// called ExitThread or ExitProcess, or an external thread
|
|
// or process terminated this thread or process.
|
|
//
|
|
// In the case of process death, this is not a problem, because
|
|
// process terminate closes all open handles attached to the process
|
|
// and frees all private virtual memory that is part of the address
|
|
// space of the process.
|
|
//
|
|
// In the case ot thread death, the code below is not executed if
|
|
// the thread terminates via ExitThread in the context of the
|
|
// try .. finally or if an external thread, either in this process
|
|
// or another process called TerminateThread on this thread.
|
|
//
|
|
while (State->Depth--) {
|
|
--Stack;
|
|
FindClose(Stack->FindHandle);
|
|
}
|
|
|
|
this->FreeVirtualBuffer(&Buffer);
|
|
}
|
|
|
|
return fResult;
|
|
} |