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 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) ;


         }


   };


}}}}


Comments (6)

  1. Uwe says:

    Those spaces before your semicolons really do look ugly 🙂

  2. ugly says:

    When is the nested namespaces being fixed in managed c++? That hack of doing it is just ugly to avoid the even uglier nesting.

    When is it going to be like

    namespace Microsoft::Services::Partners::ISV ??

  3. Ian says:

    Can I use it from C# after dll is compiled.

    Is it managed component ?

    Can you post dll.

  4. yvesdolc says:

    Uwe:

    What can I say? I love them.

    ugly:

    Well, you can call it a hack but in C++, those namespaces are really nested. In C# (if I remember correcly), it’s a long single namespace with dots in the name (e.g. "Microsoft.Services.Partners.ISV"). Also, when you do something like

    using namespace System::Collections ;

    you can later do

    Generic::List<int>

    which I do miss in C#!

    I’ll make sure I forward you feedback to the C++ team anyway. If I have an answer, I’ll post it back here.

    Just to be clear and once again, I do like C#.

    Ian: yes, that’s what I’m doing in my project. It is C++/CLI (check http://www.codeproject.com/managedcpp/cppcliintro01.asp) which you can find today in VC++ 2005. Managed C++ was the syntax in VC++ 2002 and 2003.

  5. Yves Dolce says:

    Regarding the comment made about my&amp;nbsp;Alternative to Directory.GetFiles and co.&amp;nbsp;entry:

    When…

  6. ripper234 says:

    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/