Iterating files with boost::filesystem 3.0 - c++

I want to iterate over all files in a directory matching something "keyword.txt". I searched for some solutions in google and found this:
Can I use a mask to iterate files in a directory with Boost?
As i figured out later on, the "leaf()" function was replaced (source: http://www.boost.org/doc/libs/1_41_0/libs/filesystem/doc/index.htm -> goto section 'Deprecated names and features')
what i got so far is this, but it's not running. Sorry for this somehow stupid question, but im more or less a c++ beginner.
const std::string target_path( "F:\\data\\" );
const boost::regex my_filter( "keyword.txt" );
std::vector< std::string > all_matching_files;
boost::filesystem::directory_iterator end_itr; // Default ctor yields past-the-end
for( boost::filesystem::directory_iterator i( target_path ); i != end_itr; ++i )
{
// Skip if not a file
if( !boost::filesystem::is_regular_file( i->status() ) ) continue;
boost::smatch what;
// Skip if no match
if( !boost::regex_match( i->path().filename(), what, my_filter ) ) continue;
// File matches, store it
all_matching_files.push_back( i->path().filename() );
}

Try
i->path().filename().string()
this is the equivalent for i->leaf() in boost::filesystem 3.0
In your code:
// Skip if no match
if( !boost::regex_match( i->path().filename().string(), what, my_filter ) )
continue;
// File matches, store it
all_matching_files.push_back( i->path().filename().string() );

Related

c++ folder only search

I need to get a list of folders in the directory, but only the folders. No files are needed. Only folders. I use filters to determine if this is a folder, but they do not work and all files and folders are output.
string root = "D:\\*";
cout << "Scan " << root << endl;
std::wstring widestr = std::wstring(root.begin(), root.end());
const wchar_t* widecstr = widestr.c_str();
WIN32_FIND_DATAW wfd;
HANDLE const hFind = FindFirstFileW(widecstr, &wfd);
In this way, I check that it is a folder.
if (INVALID_HANDLE_VALUE != hFind)
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
How to solve a problem?
There are two ways to do this: The hard way, and the easy way.
The hard way is based on FindFirstFile and FindNextFile, filtering out directories as needed. You will find a bazillion samples that outline this approach, both on Stack Overflow as well as the rest of the internet.
The easy way: Use the standard directory_iterator class (or recursive_directory_iterator, if you need to recurse into subdirectories). The solution is as simple as1:
for ( const auto& entry : directory_iterator( path( L"abc" ) ) ) {
if ( is_directory( entry.path() ) ) {
// Do something with the entry
visit( entry.path() );
}
}
You will have to include the <filesystem> header file, introduced in C++17.
Note: Using the latest version of Visual Studio 2017 (15.3.5), this is not yet in namespace std. You will have to reference namespace std::experimental::filesystem instead.
1 Note in particular, that there is no need to filter out the . and .. pseudo-directories; those aren't returned by the directory iterators.
This function collects folders into given vector. If you set recursive to true, it will be scanning folders inside folders inside folders etc.
// TODO: proper error handling.
void GetFolders( std::vector<std::wstring>& result, const wchar_t* path, bool recursive )
{
HANDLE hFind;
WIN32_FIND_DATA data;
std::wstring folder( path );
folder += L"\\";
std::wstring mask( folder );
mask += L"*.*";
hFind=FindFirstFile(mask.c_str(),&data);
if(hFind!=INVALID_HANDLE_VALUE)
{
do
{
std::wstring name( folder );
name += data.cFileName;
if ( ( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
// I see you don't want FILE_ATTRIBUTE_REPARSE_POINT
&& !( data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) )
{
// Skip . and .. pseudo folders.
if ( wcscmp( data.cFileName, L"." ) != 0 && wcscmp( data.cFileName, L".." ) != 0 )
{
result.push_back( name );
if ( recursive )
// TODO: It would be wise to check for cycles!
GetFolders( result, name.c_str(), recursive );
}
}
} while(FindNextFile(hFind,&data));
}
FindClose(hFind);
}
Modified from https://stackoverflow.com/a/46511952/8666197

C++ Text file Reading and Parsing for text-based adventure game

I'm a bit of a noob when it comes to programming, but as a small project I decided I'd try to make a VERY simple text-based adventure game just for some fun and practice. I have no idea how these types of games are normally made, but I decided to make a text file containing all the actual text rather than typing it all in the code, so here's what my "gamelocationdata.txt" file currently looks like.
[castleStart]
{
=castleStart=
You find yourself in a dark room inside a castle.
The walls are made of mossy stone and the entire room has a very eerie atmosphere.
There is a green bottle on the floor.
There are 2 exits, east and south.
Both exits are large doorways with grand arches.
You can see a large room through the south exit, but the east exit looks very dark and
somewhat frightening.
What will you do?
#"look arch" castleStartLookArch
#"look wall" castleStartLookWall
#"look bottle" castleStartLookBottle itemcond: hasBottle "0"
#"take bottle" castleStartLookBottle itemcond: hasBottle "0"
#"pick bottle" castleStartLookBottle itemcond: hasBottle "0"
#"go south" castleHall
#"go east" castleDark loccond: hasBeenCastleDark "0"
#"wait" castleStartWait
}
[castleStartLookArch]
{
=castleStart=
The arches above the doors look very fancy.
You can't quite figure out why this tiny room deserves to be decorated as such.
#(castleStart)
}
[castleStartLookWall]
{
=castleStart=
The wall is made of stone masonry in an old-fashioned way.
Some stones are covered in moss and there are cobwebs in the corners.
#(castleStart)
}
[castleStartWait]
{
=castleStart=
You sit still and admire the wall for a while.
Nothing happens.
#(castleStart)
}
[castleStartLookBottle]
{
=castleStart=
You pick the bottle up. It is covered in cobwebs.
The bottle is green and the label reads "1337". It contains a purple liquid.
Do you want to put the bottle in your backpack?
#"yes" castleStartTakeBottle
#"no" castleStartNoTakeBottle
}
[castleStartTakeBottle]
{
=castleStart=
You take the bottle and put it in your backpack.
+item: Bottle1337
+itemcond: hasBottle "1"
#(castleStart)
}
[castleStartNoTakeBottle]
{
=castleStart=
You put the bottle back down again.
#(castleStart)
}
[useBottle1337]
{
=curLocation=
You open the bottle and drink its contents.
It tastes very sweet.
You suddenly feel slightly stronger and more powerful.
+strength: 5
+remove_item: Bottle1337
#(castleStart)
}
[castleHall]
{
=castleHall=
You walk though the southern doorway and enter the grand hall of the castle.
It seems like the entire castle is just as old and worn out as the walls in that room,
though still very nicely decorated.
There are only a few candles on the walls, and they are somehow lit despite
the castle seeming very empty. There is not not a person to be seen.
You can go back north or proceed south through the hall.
#(castleStart)
}
[castleDark]
{
=castleStart=
You slowly tread into the dark room to the east, looking
around you as your surroundings get darker and darker.
Suddenly, you hear a noise. It sounds like the growling of an angry dog!
Horrified, you hastily turn around and run back.
+loccond: hasBeenCastleDark "1"
#(castleStart)
}
I realize I may have bitten of more than I can chew, but this is how the formatting I made up is supposed to work:
Example: [castleStart] is the name of a "location", and the curly
braces that come after encapsulate everything that has to do with that
location.
Example: =castleStart= is the location to print for the player when
they ask where they currently are.
The stuff that comes after that is what will be printed on screen when the player
"enters" that location.
After the location text, there are a bunch of options that all start
with a "#".
Example: #"wait" castleStartWait If the player types "wait", they will
be taken to the "location" named [castleStartWait].
Example: #"look bottle" castleStartLookBottle itemcond: hasBottle "0"
If the player types "look bottle", they will be taken to the location
named [castleStartLookBottle] as long as they meet the "item
requirement" that they do not already have the bottle.
Example: #"go east" castleDark loccond: hasBeenCastleDark "0" If the
player types "go east", they will be taken to the location named
[castleDark] as long as they meet the "location requirement" that they
haven't already been there.
Example: #(castleStart) This will use the same options as the ones listed in [castleStart].
Example: +strength: 5 This should add 5 points to the player's "strength" stat when they enter the location and print some hardcoded message like "You have acquired 5 strength points!"
Now, here's the problem: How do I write the function that reads and parses the data of a specific location and stores them in specific std::strings?
For example, if I do
readAndParseLocationData( castleStart );
it should look for [castleStart] in the text file, then read what's between the equals signs (=castleStart=) and store that in "std::string printLoc", then read the text after and store in a "std::string locText" and so on.
This is all the code I have so far:
//main.cpp
#include <iostream>
#include <string>
#include "ClearScreen.h"
#include "LocationData.h"
int main()
{
ClearScreen();
std::cout << "I am a banana!\n\n"; // this is just a test
readAndParseLocationData( "castleHall" );
printLocationData( "castleStart" ); // this is supposed to be used for debugging the location data by printing it.
return 0;
}
--
//LocationData.cpp
#include "LocationData.h"
#include <fstream>
#include <iostream>
#include <string>
void readAndParseLocationData( std::string location )
{
location.insert( 0,"[" );
location.append( "]" );
std::ifstream locfile( "gamelocationdata.txt" );
if( locfile.is_open() )
{
std::string line;
bool foundFile = false;
for( unsigned int curLine = 0; getline( locfile,line ); curLine++ )
{
if( line.find( location ) != std::string::npos )
{
std::cout << "found: " << location << ", line: " << curLine << "\n";
foundFile = true;
}
}
if( !foundFile )
{
std::cout << "\nERROR: Location " << location << " not found in data file!\n";
}
locfile.close();
}
else
{
std::cout << "\nERROR: Unable to open location data file!\n";
}
}
void printLocationData( std::string location )
{
//TODO: Implement
}
All I've managed to make it do (through extensive googling) is look for the location name and print what line it's on to the console.
I'm using Visual Studio Express 2013 on Windows 7.
I'd also love to hear if there is any way to improve my code or formatting in general!
It seems that what you want to do is parse the file just-in-time as you come across the locations you wish to access. This is a good idea if you expect your game to become too large to feasibly parse once and store in memory, but for a small example like this, it's probably unnecessary.
However, if you wish to implement this for learning purposes, there are a few things to look into. First of all, your current idea would involve re-parsing the file every time you want to look at the some location. A better idea would be to devise some kind of representation for the location data and implement a cache of some kind. A possible way to do this (only taking the names and location text into account) would be:
class Location
{
private:
std::string name;
std::string displayName;
std::string locationText;
}
std::unordered_map<std::string, Location> cache;
Location& readAndParseLocationData( std::string location )
{
//if we have already parsed this location
if (cache.count(location))
{
return cache[location];
}
else
{
Location parsed_location{};
//do the parsing
cache[location] = parsed_location;
}
}
In order to actually do the parsing, I would write a simple recursive descent parser. For your case, it would look something like this (in pseudocode):
Until we have found the location:
Look for a location
If this location matches:
Read the location into a string
Look for a opening brace
Read the location display name into a string
Read the rest into a string up until a closing brace
For future readers who happen to stumble upon this 2 year old question, here's the final code I ended up using to parse the location data.
(It's old and not pretty by any means, but note that the solution to my main problem was learning about std::getline and the various manipulation methods of std::string - mainly find() and substr().)
struct Location final
{
std::string displayName;
std::string optionsName;
std::string locationText;
std::string defaultOption;
std::string shop;
std::vector<int> battleLevel;
std::vector<int> battleChanceLevel;
std::vector<int> battleChancePercentage;
std::vector<std::string> battleEnemy;
std::vector<std::string> battleChanceEnemy;
std::vector<std::string> addItems;
std::vector<std::string> addWeapons;
std::vector<std::string> addConds;
std::vector<std::string> addStats;
std::vector<std::string> removeItems;
std::vector<std::string> removeWeapons;
std::vector<std::string> removeConds;
std::vector<std::string> removeStats;
std::unordered_set<std::string> options;
std::unordered_map<std::string, std::string> resultLoc;
std::unordered_map<std::string, std::string> conds;
};
std::unordered_map<std::string, Location> locationCache;
std::unordered_map<std::string, std::string> locationNewFileCache;
Location& loadLocationData( const std::string& location, const std::string& filename, const bool overRideData, const bool dataFile )
{
if( ::locationCache.count( location ) && overRideData == false ) // If we already parsed this location...
return ::locationCache[ location ]; // Return cached data.
else
{
auto filePath = std::string( "game/data/" );
if( !dataFile )
filePath.append( "locations/" );
std::ifstream locFile( filePath + filename );
if( locFile.is_open() )
{
bool foundLoc = false;
for( std::string line; std::getline(locFile, line); )
{
Location parsedLocation;
if( line.find( "[" + location + "]" ) != std::string::npos )
{
foundLoc = true;
// Parse optionsname/next filename.
std::string optsName; std::getline( locFile, optsName );
if( optsName.find( ".txt" ) != std::string::npos )
::locationNewFileCache[ location ] = optsName;
else
parsedLocation.optionsName = optsName;
// Parse display name.
std::string dispName; std::getline( locFile, dispName );
parsedLocation.displayName = dispName;
// Parse location text.
std::string locText;
for( std::string scanLine; ( (scanLine.empty()) ? true : scanLine.back() != '}' ) && std::getline( locFile, scanLine ); )
{
if( !scanLine.empty() && scanLine.front() == '{' )
locText = scanLine;
else
locText.append( "\n" + scanLine );
}
if( locText.size() >= 2 )
{
locText.erase( locText.begin() ); // Remove { at beginning.
locText.pop_back(); // Remove } at end.
}
parsedLocation.locationText = locText;
// Parse rest.
bool endReached = false;
for( std::string scanLine; !endReached && std::getline( locFile, scanLine ); )
{
if( !scanLine.empty() )
{
switch( scanLine.front() )
{
case '*': endReached = true; break; // END
case '#': // OPTION / DEFAULT OPTION
if( scanLine.at( 1 ) == '"' ) // OPTION
{
scanLine.erase( 0, 2 );
auto optionName = scanLine.substr( 0, scanLine.find( '\"' ) );
parsedLocation.options.insert( optionName );
scanLine.erase( 0, scanLine.find( '\"' ) + 2 );
auto optionResultLoc = scanLine.substr( 0, scanLine.find( ';' ) );
parsedLocation.resultLoc[ optionName ] = optionResultLoc;
scanLine.erase( 0, scanLine.find( ';' ) + 1 );
if( !scanLine.empty() ) // if the option has conditions...
{
auto condType = scanLine.substr( 0, scanLine.find( ':' ) );
scanLine.erase( 0, scanLine.find( ':' ) + 2 );
if( condType == "loccond" || condType == "itemcond" || condType == "statcond" )
parsedLocation.conds[ optionName ] = scanLine;
else
logError( "Invalid location data syntax in " + filename + "!", "Invalid condition" );
}
}
else if( scanLine.at( 1 ) == '(' ) // DEFAULT OPTION
{
scanLine.erase( 0, 2 );
parsedLocation.defaultOption = scanLine.substr( 0, scanLine.find( ')' ) );
}
else
logError( "Invalid location data syntax in " + filename + "!", "Neither option nor default option specified" );
break;
case '+': // ACTION (ITEM / STAT / CONDITION)
scanLine.erase( scanLine.begin() );
auto action = scanLine.substr( 0, scanLine.find( ':' ) );
scanLine.erase( 0, scanLine.find( ':' ) + 2 );
auto value = scanLine;
if( action == "item" )
parsedLocation.addItems.push_back( value );
else if( action == "remove_item" )
parsedLocation.removeItems.push_back( value );
else if( action == "weapon" )
parsedLocation.addWeapons.push_back( value );
else if( action == "remove_weapon" )
parsedLocation.removeWeapons.push_back( value );
else if( action == "itemcond" || action == "loccond" )
parsedLocation.addConds.push_back( value );
else if( action == "battle" )
{
auto enemyName = value.substr( 0, value.find( ' ' ) );
value.erase( 0, value.find( ' ' ) + 1 );
auto nEnemies = std::stoi( value.substr( 0, value.find( ',' ) ) );
value.erase( 0, value.find( ',' ) + 1 );
auto level = std::stoi( value );
for( auto i = 0; i < nEnemies; ++i )
{
parsedLocation.battleEnemy.push_back( enemyName );
parsedLocation.battleLevel.push_back( level );
}
}
else if( action == "battlechance" )
{
auto enemyName = value.substr( 0, value.find( ' ' ) );
value.erase( 0, value.find( ' ' ) + 1 );
auto chance = std::stoi( value.substr( 0, value.find( ',' ) ) );
value.erase( 0, value.find( ',' ) + 1 );
auto level = std::stoi( value );
parsedLocation.battleChanceEnemy.push_back( enemyName );
parsedLocation.battleChancePercentage.push_back( chance );
parsedLocation.battleChanceLevel.push_back( level );
}
else if( action == "shop" )
parsedLocation.shop = value;
else
parsedLocation.addStats.push_back( action + " " + value ); // Assume it's a stat.
break;
}
}
}
::locationCache[ location ] = parsedLocation;
return ::locationCache[ location ];
}
}
}
else
logError( "Unable to open location data file " + filename + "!" );
}
static Location dummyLocation;
return dummyLocation;
}

C++ Prepend DateTime to console output

Searched around and couldn't find any info on this besides redirecting to files so hopefully someone can help me out.
I have a console application that launches and hooks another process, by default the new process output displays in the first console application.
What I would like to do is prepend a datetime value to all the output, the problem is that I don't control the output from the child process (3rd party app) so the easy solution of adding a datetime to all printed values is unavailable. Is it possible to prepend a string to all stdout?
Since you confirmed C++ (which implies a solution for
std::cout is what is needed, and not for stdout): the
obvious solution is a filtering streambuf:
class TimeStampStreambuf : public std::streambuf
{
std::streambuf* myDest;
std::ostream* myOwner;
bool myIsAtStartOfLine;
protected:
int overflow( int ch )
{
// To allow truly empty lines, otherwise drop the
// first condition...
if ( ch != '\n' && myIsAtStartOfLine ) {
std::string tmp = now();
// function now() should return the timestamp as a string
myDest->sputn( tmp.data(), tmp.size() );
}
myIsAtStartOfLine = ch == '\n';
ch = myDest->sputc( ch );
return ch;
}
public:
TimeStampStreambuf( std::streambuf* dest )
: myDest( dest )
, myOwner( nullptr )
, myIsAtStartOfLine( false )
{
}
TimeStampStreambuf( std::ostream& owner )
: myDest( dest.rdbuf() )
, myOwner( &owner )
, myIsAtStartOfLine( false )
{
myOwner->rdbuf( this );
}
~TimeStampStreambuf()
{
if ( myOwner != nullptr ) {
myOwner->rdbuf( myDest );
}
}
};
And to install it:
// scoped:
TimeStampStreambuf anyName( std::cout );
// Time stamping will be turned off when variable goes out
// of scope.
// unscoped:
std::streambuf* savedStreambuf = std::cout.rdbuf();
TimeStampStreambuf name( savedStreambuf );
// In this case, you have to restore the original streambuf
// yourself before calling exit.
As far as I know, the articles explaining this (from C++
Reports, Sept. 1998) are not on line.
EDIT:
I've actually found them:
http://gabisoft.free.fr/articles-en.html. I can't believe that
the link still works; it's been years since I had an account
with Free.

Adding a -q switch to a list of arguments

I am trying to figure a way to add an optional quiet switch to my command line arguments. The program I'm working on is a text to HTML converter, and at minimum requires a text sourcefile to be included in order for the program to run. What I am trying to get, is when a user enters -q anywhere in the argument list, the program will still run but suppress the output to the console. I have tried a few if statements and loops that will re assign argument values to my infile and outfile variables but those are not working either. The code can be found here: https://gist.github.com/anonymous/ab8ecfd09bddba0d4fcc. I am still relatively new to working with C++ so if you provide an explanation as to how to get closer to my goal in a simple way, I'd really appreciate it.
Something jumped out at me right away, you're testing if the argument equals -q by
if( strcmp( argv[1], "-q" ) != 0) //This is an example of what I am trying to do.
{
quiet = true;
infile.open( argv[2] );
}
which is incorrect. strcmp returns the lexical difference between the two strings compared :
http://www.cplusplus.com/reference/cstring/strcmp/
so I believe you want
if( strcmp( argv[1], "-q" ) == 0) //This is an example of what I am trying to do.
{
quiet = true;
infile.open( argv[2] );
}
Like I said, I haven't tested anything, it just jumped out at me.
edit
how I would parse in the sourcefile, destfile, and -q option
std::string sourceFile;
std::string destFile;
if ( argc == 3 )
{
sourceFile = std::string( argv[1] );
destFile = std::string( argv[2] );
}
else if ( argc == 4 )
{
// quiet mode is enabled
std::string arg1( argv[1] );
std::string arg2( argv[2] );
std::string arg3( argv[3] );
if ( arg1 != "-q" )
vec.push_back( std::string( arg1 );
if ( arg2 != "-q" )
vec.push_back( std::string( arg2 );
if ( arg3 != "-q" )
vec.push_back( std::string( arg3 );
if ( vec.size() != 2 )
{
// maybe error?
}
else
{
sourceFile = vec[0];
destFile = vec[1];
}
}
Certainly not as clean as possible, and I haven't tested it so there may be a small error.

Recursive file search using C++ MFC?

What is the cleanest way to recursively search for files using C++ and MFC?
EDIT: Do any of these solutions offer the ability to use multiple filters through one pass? I guess with CFileFind I could filter on *.* and then write custom code to further filter into different file types. Does anything offer built-in multiple filters (ie. *.exe,*.dll)?
EDIT2: Just realized an obvious assumption that I was making that makes my previous EDIT invalid. If I am trying to do a recursive search with CFileFind, I have to use *.* as my wildcard because otherwise subdirectories won't be matched and no recursion will take place. So filtering on different file-extentions will have to be handled separately regardless.
Using CFileFind.
Take a look at this example from MSDN:
void Recurse(LPCTSTR pstr)
{
CFileFind finder;
// build a string with wildcards
CString strWildcard(pstr);
strWildcard += _T("\\*.*");
// start working for files
BOOL bWorking = finder.FindFile(strWildcard);
while (bWorking)
{
bWorking = finder.FindNextFile();
// skip . and .. files; otherwise, we'd
// recur infinitely!
if (finder.IsDots())
continue;
// if it's a directory, recursively search it
if (finder.IsDirectory())
{
CString str = finder.GetFilePath();
cout << (LPCTSTR) str << endl;
Recurse(str);
}
}
finder.Close();
}
Use Boost's Filesystem implementation!
The recursive example is even on the filesystem homepage:
bool find_file( const path & dir_path, // in this directory,
const std::string & file_name, // search for this name,
path & path_found ) // placing path here if found
{
if ( !exists( dir_path ) ) return false;
directory_iterator end_itr; // default construction yields past-the-end
for ( directory_iterator itr( dir_path );
itr != end_itr;
++itr )
{
if ( is_directory(itr->status()) )
{
if ( find_file( itr->path(), file_name, path_found ) ) return true;
}
else if ( itr->leaf() == file_name ) // see below
{
path_found = itr->path();
return true;
}
}
return false;
}
I know it is not your question, but it is also easy to to without recursion by using a CStringArray
void FindFiles(CString srcFolder)
{
CStringArray dirs;
dirs.Add(srcFolder + "\\*.*");
while(dirs.GetSize() > 0) {
CString dir = dirs.GetAt(0);
dirs.RemoveAt(0);
CFileFind ff;
BOOL good = ff.FindFile(dir);
while(good) {
good = ff.FindNextFile();
if(!ff.IsDots()) {
if(!ff.IsDirectory()) {
//process file
} else {
//new directory (and not . or ..)
dirs.InsertAt(0,nd + "\\*.*");
}
}
}
ff.Close();
}
}
Check out the recls library - stands for recursive ls - which is a recursive search library that works on UNIX and Windows. It's a C library with adaptations to different language, including C++. From memory, you can use it something like the following:
using recls::search_sequence;
CString dir = "C:\\mydir";
CString patterns = "*.doc;abc*.xls";
CStringArray paths;
search_sequence files(dir, patterns, recls::RECURSIVE);
for(search_sequence::const_iterator b = files.begin(); b != files.end(); b++) {
paths.Add((*b).c_str());
}
It'll find all .doc files, and all .xls files beginning with abc in C:\mydir or any of its subdirectories.
I haven't compiled this, but it should be pretty close to the mark.
CString strNextFileName , strSaveLog= "C:\\mydir";
Find.FindFile(strSaveLog);
BOOL l = Find.FindNextFile();
if(!l)
MessageBox("");
strNextFileName = Find.GetFileName();
Its not working. Find.FindNextFile() returning false even the files are present in the same directory``