Alternative to Directory.GetFiles and co.
(8/8/05: I fixed the source code)
Just like String::Split, I dislike those GetFiles methods that put more load on the managed heap when all I need is just one piece of information among the several it gives me (130,000 filenames in my project!).
So I wrote the following to wrap the FindFirstFile/FindNextFile/FindClose and it allows me to do the following:
foreach( FoundFileData ffd in new FilesFinder( @"C:\Windows\*.DLL" ) )
Console.WriteLine( "\"{0}\" {1} {2} {3}", ffd.FileName, ffd.Size, ffd.CreationTime, ffd.Attributes ) ;
I'm sure this can be improved so don't hesitate to comment.and let's make it better together! I compiled it with VC++ 2005 July CTP.
// FilesFinder.h
#pragma once
#include <Windows.h>
#include <vcclr.h>
#include <new>
using namespace System;
using namespace System::Collections::Generic ;
using namespace System::IO ;
using namespace System::Diagnostics ;
namespace Microsoft { namespace Services { namespace Partners { namespace ISV
{
public ref class FoundFileData
{
internal:
FoundFileData( const WIN32_FIND_DATA % win32FindData )
:Attributes( (FileAttributes) win32FindData.dwFileAttributes ),
CreationTime( DateTime::FromFileTime( ((UInt64) win32FindData.ftCreationTime.dwHighDateTime << 32) + win32FindData.ftCreationTime.dwLowDateTime) ),
LastAccessTime( DateTime::FromFileTime(((UInt64) win32FindData.ftLastAccessTime.dwHighDateTime << 32) + win32FindData.ftLastAccessTime.dwLowDateTime) ),
LastWriteTime( DateTime::FromFileTime(((UInt64) win32FindData.ftLastWriteTime.dwHighDateTime << 32) + win32FindData.ftLastWriteTime.dwLowDateTime) ),
Size( ((UInt64) win32FindData.nFileSizeHigh << 32) + win32FindData.nFileSizeLow ),
FileName( gcnew String(win32FindData.cFileName) ),
AlternateFileName( gcnew String(win32FindData.cAlternateFileName) )
{
}
public:
initonly FileAttributes Attributes ;
initonly DateTime CreationTime;
initonly DateTime LastAccessTime;
initonly DateTime LastWriteTime;
initonly UInt64 Size ;
initonly String ^ FileName;
initonly String ^ AlternateFileName ;
} ;
public ref class FilesEnumerator : IEnumerator<FoundFileData ^>
{
initonly String ^ _fileName ;
WIN32_FIND_DATA * _win32FindData ;
HANDLE _findHandle ;
public:
FilesEnumerator( String ^ fileName )
{
_fileName = fileName ;
_findHandle = INVALID_HANDLE_VALUE ;
try
{
_win32FindData = new WIN32_FIND_DATA() ;
}
catch( const std::bad_alloc & e )
{
throw gcnew OutOfMemoryException( gcnew String( e.what() ) ) ;
}
}
FilesEnumerator( FilesEnumerator % filesEnumerator )
{
System::Diagnostics::Debug::Assert( false, "TODO" ) ;
}
~FilesEnumerator()
{
FilesEnumerator::!FilesEnumerator() ; // 8/8/05: added (Thanks Andy Rich). See https://blogs.msdn.com/arich/archive/2005/06/09/427389.aspx
}
!FilesEnumerator()
{
if ( _findHandle != INVALID_HANDLE_VALUE )
{
FindClose(_findHandle);
_findHandle = INVALID_HANDLE_VALUE ;
}
if ( _win32FindData != NULL )
{
delete _win32FindData ;
_win32FindData = NULL ;
}
}
virtual property Object ^ CurrentObject // 8/8/05: name change
{
Object ^ get () = System::Collections::IEnumerator::Current::get
{
return Current::get() ;
}
}
virtual property FoundFileData ^ Current
{
FoundFileData ^ get ()
{
if ( _findHandle == INVALID_HANDLE_VALUE )
throw gcnew InvalidOperationException( "MoveNext() must be called first" ) ;
return gcnew FoundFileData(*_win32FindData) ;
}
}
virtual bool MoveNext()
{
if (_findHandle == INVALID_HANDLE_VALUE)
{
{
pin_ptr<const wchar_t> nativefileName = PtrToStringChars(_fileName) ;
_findHandle = FindFirstFile( nativefileName, _win32FindData);
}
if (_findHandle == INVALID_HANDLE_VALUE)
{
DWORD lastError = GetLastError() ;
if ( lastError == ERROR_FILE_NOT_FOUND )
return false ;
throw gcnew System::ComponentModel::Win32Exception( lastError ) ;
}
}
else
{
if ( ! FindNextFile( _findHandle, _win32FindData ) )
{
DWORD lastError = GetLastError() ;
if ( lastError == ERROR_NO_MORE_FILES )
{
return false ;
}
else
{
throw gcnew System::ComponentModel::Win32Exception( lastError ) ;
}
}
}
return true ;
}
virtual void Reset()
{
if ( _findHandle != INVALID_HANDLE_VALUE )
{
FindClose(_findHandle);
_findHandle = INVALID_HANDLE_VALUE ;
}
}
} ;
public ref class FilesFinder : public IEnumerable<FoundFileData ^>
{
String ^ _fileName ;
public:
FilesFinder( String ^ fileName ) : _fileName(fileName)
{
}
// 8/8/05: name change
virtual System::Collections::IEnumerator ^ GetObjectEnumerator() = System::Collections::IEnumerable::GetEnumerator
{
return GetEnumerator() ;
}
virtual IEnumerator<FoundFileData ^> ^ GetEnumerator()
{
return gcnew FilesEnumerator(_fileName) ;
}
};
}}}}
Comments
- Anonymous
August 06, 2005
Those spaces before your semicolons really do look ugly :-) - Anonymous
August 06, 2005
The comment has been removed - Anonymous
August 07, 2005
The comment has been removed - Anonymous
August 07, 2005
The comment has been removed - Anonymous
August 09, 2005
Regarding the comment made about my&nbsp;Alternative to Directory.GetFiles and co.&nbsp;entry:
When... - Anonymous
February 16, 2009
Great post, thanks for the code. I translated it to C# in order to get uniform behavior on 64bit machines (instead of compiling it to a separate C++ DLL which is then platform-specific) (I don't like compiling C++ code from within a pure C# project). The C# version can be found here: http://ripper234.com/p/c-alternative-to-directorygetfiles/