Click here to Skip to main content
Click here to Skip to main content

Win32 file name iteration STL way

By , 21 Nov 2004
 

Introduction

Several times I worked with file names, I usually used Win32 API such as ::FindFirstFile.. But it turns out that it's so boring work. Finally, I realized I can use STL's great feature, iterator, to handle file name iteration. That's why I made a simple STL iterator class for file name iteration.

Usage

win32_file_iterator itBegin("c:\\*.*"), itEnd;
std::copy(itBegin, itEnd, ostream_iterator<std::string>(cout, "\n"));

The code above shows the simplest way to use the class. Actually, you can use almost all of STL algorithm, I think..

win32_file_iterator itBegin("c:\\*.*"), itEnd;
std::vector<std::string> vec(itBegin, itEnd);

You also can fill the STL container by using the constructor that takes begin iterator and end iterator.

Actually, win32_file_iterator class' constructor takes three parameters. The first one is the filter string that is for calling ::FindFirstFile function. Second one is the flag that specifies whether dereferenced path is full path or not. For example, if it's true, the returned path string is c:\test\aa.txt, otherwise it'll be aa.txt only. The last parameter is the other flags which specify file attribute. For simplicity, I used Win32 API's FILE_ATTRIBUTE_XXX flags..

If you want to get only directory names, and which is full path, the code will look like this:

win32_file_iterator itBegin("c:\\*", true, FILE_ATTRIBUTE_DIRECTORY);

So easy, huh?

Source

#include <windows.h>
#include <iterator>
#include <string>

class win32_file_iterator : 
 public std::iterator<std::input_iterator_tag, std::string>
{
private:

 class internal_handle_data{
 public:
  internal_handle_data():_h(NULL), _ref(0){}
  void setHandle(HANDLE handle){ _h = handle; }
  HANDLE getHandle(){ return _h; }
  void incRef(){ _ref++; }
  unsigned decRef(){ return --_ref; }
  operator HANDLE(){ return _h; }

 private:
  HANDLE _h;
  unsigned _ref;
 };


public:

 win32_file_iterator(std::string strfilter, bool bFullPath = false, 
   int flag = FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_DIRECTORY):
   _bEnd(false), _bFullPath(bFullPath), _flag(flag){
        HANDLE h = ::FindFirstFile(strfilter.c_str(), &_wfd);
  _handle.setHandle(h);
  if(h == INVALID_HANDLE_VALUE){
   _bEnd = true;
  }else{
   _handle.incRef();
   std::string::size_type n1 = strfilter.find_last_of("\\");
   _strroot = strfilter.substr(0,n1+1);
   _chkvalid(_wfd);
  }
 }

 win32_file_iterator():_bEnd(true){}

 win32_file_iterator(win32_file_iterator& rhs){
  
  _handle = rhs._handle;
  _handle.incRef();
  _flag = rhs._flag;
  _bFullPath = rhs._bFullPath;
  _bEnd = rhs._bEnd;
  _wfd = rhs._wfd;
  _strfname = rhs._strfname;
  _strroot = rhs._strroot;  
 }

 ~win32_file_iterator(){
  if(_handle.decRef() == 0 && _handle.getHandle() != NULL ){
   FindClose(_handle);
  }
  
 }

 reference operator*(){
  return _strfname;
 }

 bool operator==(const win32_file_iterator& rhs) const{
  return (_bEnd == rhs._bEnd);
 }

 bool operator!=(const win32_file_iterator& rhs) const{
  return (_bEnd != rhs._bEnd);
 }


 win32_file_iterator& operator++(){
  _findnext();
  return *this;
 }

 win32_file_iterator& operator++(int){
  _findnext();
  return *this;
 }

private:

 void _findnext(){
  BOOL b = ::FindNextFile(_handle, &_wfd);
  if(b){
   _chkvalid(_wfd);
  }else{
   _bEnd = true;
  }
 }

 void _chkvalid(WIN32_FIND_DATA& _wfd){
  if(_wfd.dwFileAttributes & _flag){
   _getval(_wfd);
  }
  else{
   _findnext();
  }
 }

 void _getval(WIN32_FIND_DATA& wfd){
  if(_bFullPath)
   _strfname = _strroot+ wfd.cFileName;
  else
   _strfname = wfd.cFileName;
 }


private:
 int _flag;
 bool _bFullPath;
 bool _bEnd;
 internal_handle_data _handle;
 WIN32_FIND_DATA _wfd;
 std::string _strroot;
 std::string _strfname;
};

Comment

The code might have many terrible bugs. But what I want was to show the way we can use STL like iteration to find filenames. I wish it'll help you. You can use this code in whatever ways you want, comments are welcome..

And also check out boost::filesystem library.. it's well-written but a little bit heavy. It needs an additional DLL, I suppose.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

bektek
United States United States
Member
study, study, That's all I can say Smile | :)

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralSuggestionmemberRobert Bielik24 Nov '04 - 21:07 
For the internal handle, you could use the boost::shared_ptr handle wrapper idea (posted here on codeproject). That way the FindClose (or any handle closure) is neatly wrapped up within it.
 
These kinds of articles, simple good ideas, makes me happy Smile | :)
GeneralRe: Suggestionmemberbektek25 Nov '04 - 8:15 
Thanks for the good idea..
and complement.. Smile | :)
 


 
Best Regards.
Bektek
GeneralOne very minor commentmembersdoyle24 Nov '04 - 6:47 
Iterator class looks good and I like it (you got a 5 from me!) ...
 
In the constructor, the string parameter should be passed as a const reference, otherwise you will incur an unnecessary string copy during iterator creation with this constructor.
GeneralRe: One very minor commentmemberbektek24 Nov '04 - 10:44 
yeah.. exactly..
you're right..
 
Best Regards.
Bektek
GeneralStlSoft and WinSTLmemberNeville Franks23 Nov '04 - 23:18 
Readers may also be interested in WinSTL which includes file system iterators.
 
See: http://synesis.com.au/software/winstl/[^] and http://synesis.com.au/software/stlsoft/[^]
 
Matthew Wilson has made quite a contribution to the C++ community.
 
Neville Franks, Author of ED for Windows www.getsoft.com and Surfulater www.surfulater.com "Save what you Surf"
 

GeneralRe: StlSoft and WinSTLmemberbektek23 Nov '04 - 23:20 
It sounds good.. thanks for the good info..
I'll check it out soon..
 
Best Regards.
Bektek
GeneralRe: StlSoft and WinSTLmemberRobert Bielik25 Nov '04 - 9:14 
Sorry but you've lost me there. The thing about STL is that it is portable, cross-platform. Why would you want to make something so hardbound to Win32?? As good as it might be, if I'm going to learn something else than Win32, I might as well go for a cross-platform approach such as wxWidgets (previously wxWindows) http://www.wxwidgets.com. Else, the shared_ptr approach to Win32 handles suffices nicely.
 
/Rob

GeneralRe: StlSoft and WinSTLmemberNeville Franks25 Nov '04 - 9:23 
Hi Rob, Yes I agree. I personally use Boost and STL wherever it makes sense. I don't use WinSTL, however I thought it would be of interest to CP readers. I think some of the other libraries maybe portable, but I've not used any of them so I can't say for sure.
 
Neville Franks, Author of ED for Windows www.getsoft.com and Surfulater www.surfulater.com "Save what you Surf"
 

GeneralRe: StlSoft and WinSTLmember.:fl0yd:.2 Dec '04 - 5:01 
Robert Bielik wrote:
The thing about STL is that it is portable, cross-platform. Why would you want to make something so hardbound to Win32??
 
Counter-question: How would you have implemented the iterator in a cross-platform manner?
 
The short answer is: You cannot.
 
The longer answer is, that generic programming is about providing a portable interface, rather than a portable implementation. And that's exactly what the STL does itself. Take for example operator new, that directly or indirectly calls into OS specific API's. Would that make it really non-portable? Or wouldn't you agree that the common interface is sufficient for being portable. The same is true for the win32_file_iterator class, which exposes a portable interface, and should you ever find yourself in a situation to port to a different platform, all you really had to do is provide a platform-specific implementation, with the rest of your source base remaining unchanged.
 
On a similar note, wxWidgest looks exactly the same under the hood. It's just that you never really get in touch with those implementation details. Your mistake, however, is that you deduce from a portable interface that the implementation is equally platform-independent.
 
.f
GeneralRe: StlSoft and WinSTLmemberRobert Bielik2 Dec '04 - 19:59 
Sorry, no mistake on my part. You must have misunderstood. I have no problem with the _implementation_ being hardwired to the platform. As you said, implementation cannot be cross-platform. Of course it is the interfaces that are to be abstracted. But WinSTL (which was the topic) has some interfaces that only makes sense on a Windows platform (registry/windows directory etc), and rightfully so, since it doesn't purport being cross-platform.
 
So, again: If you need to learn something NEW, I'd recommend going for a cross-platform library (like wxWidgets f.i.), because that would give a head start if you'd want to write cross-platform software. Otherwise simpler solutions suffice.
GeneralLooks nice.memberrtw170123 Nov '04 - 17:16 
I'll give this a try in my game. I'm using stl and not looking to add boost as well. Was looking for a quick way to get screenshot## and find highest one so I can save yet another. Er before I get too excited is this only for .net? I'm just using regular unleaded c++. Well if not guess I will have to delve into boost.
GeneralTake a look at boost::filesystemmemberPhan Manh Dan22 Nov '04 - 22:33 
boost::filesystem provides a portable way to iterate over files and directories.
http://www.boost.org/libs/filesystem/doc/index.htm
 
good luck!
 
Dan.
GeneralRe: Take a look at boost::filesystemmemberbektek23 Nov '04 - 9:16 
I know, I know as I said Smile | :)
GeneralRe: Take a look at boost::filesystemmemberoccam26 Nov '04 - 5:37 
I did, and decided not to use it, because it does not support Unicode on Win32. That means it only works for file names from a single code page.
 
Keith
GeneralRe: Take a look at boost::filesystemsussAnonymous26 Nov '04 - 22:09 
Further, boost::filesystem has problems while compiling in MS VC6.
 
Dan.
GeneralRe: Take a look at boost::filesystemmemberoccam27 Nov '04 - 1:07 
It's a bit harsh blaming boost libraries for not being usable with MS VC6. It does not conform to the relevant C++ standards, whereas VC7.1 does.
 
Keith

GeneralRe: Take a look at boost::filesystemmembernastanet28 Nov '04 - 22:24 
I tried to compile boost library, but i had some prolems... Library compiled without any error, but I couldn't include it into my project.
I'm using VC++ 6 and STLport-4.5.3, I gave right options during compilation (msvc-stlport e STLPORT_PATH) and I inserted the right path for includes and libraries in Options->Directories
When I compile my project in Debug mode, VC gives me this error:
warning: STLPort debug versions are built with /D_STLP_DEBUG=1
c:\librerie\boost_1_32_0\boost\config\auto_link.hpp(170) : fatal error C1189: #error : "Build options aren't compatible with pre-built libraries"
Otherwise, when I compile in Release mode sometimes VC gives me link error - unresolved externals!
Any ideas? Thank you very much
 
Gianluca Nastasi
GeneralRe: Take a look at boost::filesystemmemberbektek29 Nov '04 - 0:17 
I'm not pretty sure.. anyway are you trying to use boost::filesystem library?
If you are, just try to change the code generation mode to 'multithreaded mode'
 
I remember I did it, and then succeeded to compile it.
But It caused kinda infinite loop at runtime as I remember,,
It might have some bugs related to boost::thread library with VS6
That's why I changed my IDE to vs.net 2003..
then everything is OK and fine..
 
I hope it helps you..

 
Best Regards.
Bektek
GeneralRe: Take a look at boost::filesystemmembernastanet29 Nov '04 - 0:36 
Yes, I'm trying to use boost::filesystem...
I setted the right options in code generation too, but I can't work it out Frown | :(
Thanks a lot!
Sadly, I must use VC++ 6 'couse of my job!
I'd prefer programming for linux with gcc! Smile | :)
Bye
 
Gianluca Nastasi
QuestionCould you explain?memberWREY22 Nov '04 - 5:28 
If what you're doing, is simply iterating through a directory (or directories) for a particular file, why the need for reference counting?
 
Secondly, you're using a conversion operator to dereference the '_strfname' object. What I don't understand is the return type you're using, and why couldn't the two operations be consolidated (viz. those routines in which the conversion operator is used, with the work of the conversion operator itself)? It seemed to me you could have ended up with a single routine that would have accomplished both activities.
 
A handy little tool, nonetheless.
 
Smile | :)
 
William
 
Fortes in fide et opere!
AnswerRe: Could you explain?memberbektek22 Nov '04 - 6:33 
Thanks for your comment..
The reason why I used reference counting was.. when the iterator class gets copied, the HANDLE which got from ::FindFirstFile API is closed in the copied class in my implementation..
That's why I needed reference counting,,
But I'm so sure there are much better ways to do accomplish this..
 
Secondly,, which conversion operators do you mean?
Could you quote some codes?
 

 
Best regards.. Rose | [Rose]
bektek
GeneralRe: Could you explain?memberWREY23 Nov '04 - 3:45 
I'm sorry; my error. I know I said conversion operator. I meant the overloading of the dereferencing operator.
 
The confusion was my fault.
 
Cry | :((
 
William
 
Fortes in fide et opere!
GeneralLike you said...memberNemanja Trifunovic21 Nov '04 - 14:26 
... check out Boost Filesystem. It is very easy to use, does not require additional dlls and it is portable, peer-reviewed and tested Smile | :)
 


My programming blahblahblah blog. If you ever find anything useful here, please let me know to remove it.
GeneralRe: Like you said...memberbektek21 Nov '04 - 16:34 
yeah... I strongly agree with you..
I love boost::filesystem library... too... and other libraries..
 
But my code might be helpful for the begginers who are interested in implementation of iterator.. Smile | :)
GeneralRe: Like you said...memberNemanja Trifunovic22 Nov '04 - 2:02 
Don't get me wrong. I never implied your effort was worthless. Just wanted to point out that for the production code people might prefer to use Boost Filesystem.
 


My programming blahblahblah blog. If you ever find anything useful here, please let me know to remove it.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 21 Nov 2004
Article Copyright 2004 by bektek
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid