(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 http://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) ;

         }

   };

}}}}