Share via


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&amp;nbsp;Alternative to Directory.GetFiles and co.&amp;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/