Recursive hard disk search with FindFirstFile & FindNextFile C++ - c++

I am failing to see where i am going wrong. This current code skips straight to closefile. NOt processing any files, i may just be missing something obvious and it has been a long day.
My function is meant to search the hard disk (c:) for a given file. EG example.txt. &strFilePath here would be used in the FindFirstFile declaration.
Any help would be appeciated.
Thanks.
String Copy::SearchDrive( const String& strFile, const String& strFilePath, const bool& bRecursive, const bool& bStopWhenFound ) const
{
HANDLE hFile;
WIN32_FIND_DATA file;
hFile = FindFirstFile("C:\\", &file);
String strFoundFilePath = "";
if ( hFile )
{
while ( FindNextFile( hFile, &file))
{
String strTheNameOfTheFile = file.cFileName;
// It could be a directory we are looking at
// if so look into that dir
if ( file.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY
&& bRecursive )
{
String strNewFilePath = strFilePath + "\\";
strNewFilePath += strTheNameOfTheFile;
SearchDrive( strFile, strNewFilePath, bRecursive, bStopWhenFound );
}
else
{
if ( strTheNameOfTheFile == strFile )
{
strFoundFilePath = strFilePath;
strFoundFilePath += "\\";
strFoundFilePath += strFile;
/// TODO
// ADD TO COLLECTION TYPE
if ( bStopWhenFound )
{
break;
}
}
}
}
CloseHandle( hFile );
}
return strFoundFilePath;
}

You have quite a few logic bugs in your code. Try this instead (you did not indicate which compiler you are using, so I am assuming C++Builder, which has an uppercase-S String class. Adjust the code as needed if you are using a different compiler):
String Copy::SearchDrive(const String& strFile, const String& strFilePath, const bool& bRecursive, const bool& bStopWhenFound) const
{
String strFoundFilePath;
WIN32_FIND_DATA file;
String strPathToSearch = strFilePath;
if (!strPathToSearch.IsEmpty())
strPathToSearch = IncludeTrailingPathDelimiter(strPathToSearch);
HANDLE hFile = FindFirstFile((strPathToSearch + "*").c_str(), &file);
if (hFile != INVALID_HANDLE_VALUE)
{
do
{
String strTheNameOfTheFile = file.cFileName;
// It could be a directory we are looking at
// if so look into that dir
if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if ((strTheNameOfTheFile != ".") && (strTheNameOfTheFile != "..") && (bRecursive))
{
strFoundFilePath = SearchDrive(strFile, strPathToSearch + strTheNameOfTheFile, bRecursive, bStopWhenFound);
if (!strFoundFilePath.IsEmpty() && bStopWhenFound)
break;
}
}
else
{
if (strTheNameOfTheFile == strFile)
{
strFoundFilePath = strPathToSearch + strFile;
/// TODO
// ADD TO COLLECTION TYPE
if (bStopWhenFound)
break;
}
}
}
while (FindNextFile(hFile, &file));
FindClose(hFile);
}
return strFoundFilePath;
}
String strFoundFilePath = SearchDrive("file.ext", "C:\\", ...);
UPDATE: An alternative implementation of SearchDrive() that does not keep multiple search handles open while recursing through sub-directories:
#include <memory>
String Copy::SearchDrive(const String& strFile, const String& strFilePath, const bool& bRecursive, const bool& bStopWhenFound) const
{
String strFoundFilePath;
WIN32_FIND_DATA file;
String strPathToSearch = strFilePath;
if (!strPathToSearch.IsEmpty())
strPathToSearch = IncludeTrailingPathDelimiter(strPathToSearch);
HANDLE hFile = FindFirstFile((strPathToSearch + "*").c_str(), &file);
if (hFile != INVALID_HANDLE_VALUE)
{
std::auto_ptr<TStringList> subDirs;
do
{
String strTheNameOfTheFile = file.cFileName;
// It could be a directory we are looking at
// if so look into that dir
if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if ((strTheNameOfTheFile != ".") && (strTheNameOfTheFile != "..") && (bRecursive))
{
if (subDirs.get() == NULL)
subDirs.reset(new TStringList);
subDirs->Add(strPathToSearch + strTheNameOfTheFile);
}
}
else
{
if (strTheNameOfTheFile == strFile)
{
strFoundFilePath = strPathToSearch + strFile;
/// TODO
// ADD TO COLLECTION TYPE
if (bStopWhenFound)
break;
}
}
}
while (FindNextFile(hFile, &file));
FindClose(hFile);
if (!strFoundFilePath.IsEmpty() && bStopWhenFound)
return strFoundFilePath;
if (subDirs.get() != NULL)
{
for (int i = 0; i < subDirs->Count; ++i)
{
strFoundFilePath = SearchDrive(strFile, subDirs->Strings[i], bRecursive, bStopWhenFound);
if (!strFoundFilePath.IsEmpty() && bStopWhenFound)
break;
}
}
}
return strFoundFilePath;
}

Your condition is incorrect, you should compare to to INVALID_HANDLE_VALUE
if ( hFile != INVALID_HANDLE_VALUE)
Besides you skip the first matching file returned by FindFirstFile, is that what you want?
Also I believe you need a wildcard c:\\* otherwise it will only match c:\\ itself
hFile = FindFirstFile("C:\\*", &file);

Related

Copy all files .doc or .docx in folder and subfolder into another folder

I am new to C++ and winapi, currently working on a project to create a winapi application with a function to copy all files .doc and .docx in one drive to another folder.
Below is what I have done and it doesn't seem to work:
Can anyone show me how to do this properly ?
void cc(wstring inputstr) {
TCHAR sizeDir[MAX_PATH];
wstring search = inputstr + TEXT("\\*");
wcscpy_s(sizeDir, MAX_PATH, search.c_str());
WIN32_FIND_DATA findfiledata;
HANDLE Find = FindFirstFile(sizeDir, &findfiledata);
do {
if (findfiledata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (!wcscmp(findfiledata.cFileName, TEXT(".")) || !wcscmp(findfiledata.cFileName, TEXT(".."))) continue;
//checking folder or file
wstring dirfolder = inputstr + TEXT("\\") + findfiledata.cFileName;
cc(dirfolder);
}
else {
wstring FileSearch = findfiledata.cFileName;
//.doc or docx
if (!wcscmp(FileSearch.c_str(), L".doc") || !wcscmp(FileSearch.c_str(), L".docx")) {
TCHAR src[256] = L"D:\\test\\";
wstring dirsrc = inputstr + TEXT("\\") + findfiledata.cFileName;
_tprintf(TEXT(" %s \n"), dirsrc.c_str());
wcscat_s(src, findfiledata.cFileName);
CopyFile(dirsrc.c_str(), src, TRUE);
}
}
} while (FindNextFile(Find, &findfiledata) != 0);
FindClose(Find);
}
The inputstr here when i call the function is the drive that i want to search like cc(L"D:");
if (!wcscmp(FileSearch.c_str(), L".doc") || !wcscmp(FileSearch.c_str(), L".docx"))
This is comparing the whole file name. We only need to compare the file extension. PathFindExtension can be used to find the file extension:
const wchar_t* ext = PathFindExtension(findfiledata.cFileName);
if (_wcsicmp(ext, L".doc") == 0 || _wcsicmp(ext, L".docx") == 0)
{
const std::wstring path = inputstr + L"\\" + findfiledata.cFileName;
std::wcout << path << '\n';
}
findfiledata should be zero initialized.
Adding CopyFile inside that recursive function may cause problems. Because FindNextFile could see the new copied file, and the function tries to copy it again.
You could instead save the result in a vector of strings, then copy the file once cc is finished.
void cc(const std::wstring &inputstr, std::vector<std::wstring> &vec)
{
std::wstring wildcard{ inputstr + L"\\*" };
WIN32_FIND_DATA find = { 0 };
HANDLE handle = FindFirstFile(wildcard.c_str(), &find);
if (handle == INVALID_HANDLE_VALUE)
return;
do
{
if (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (!wcscmp(find.cFileName, L".") || !wcscmp(find.cFileName, L".."))
continue;
const std::wstring dir = inputstr + L"\\" + find.cFileName;
cc(dir, vec);
}
else
{
const wchar_t* ext = PathFindExtension(find.cFileName);
if (_wcsicmp(ext, L".doc") == 0 || _wcsicmp(ext, L".docx") == 0)
{
const std::wstring path = inputstr + L"\\" + find.cFileName;
vec.push_back(path);
}
}
} while (FindNextFile(handle, &find) != 0);
FindClose(handle);
}
Used as
std::vector<std::wstring> result;
cc(L"D:\\test", result);
for (const auto& e : result)
std::wcout << e << '\n';
Note, PathFindExtension requires additional headers and libraries. If it's not available for some reason, and std::filesystem is not available, here is a do it yourself method:
std::wstring test = findfiledata.cFileName;
auto dot = test.find_last_of(L'.');
if (dot != std::wstring::npos)
{
auto ext = test.substr(dot);
for (auto& e : ext) e = towlower(e);
if (ext == L".doc" || ext == L".docx")
{
std::wstring path = inputstr + L"\\" + findfiledata.cFileName;
std::wcout << path << '\n';
}
}

Store Data in memory to access later on

New to C++. I have a code that spins thru directories to look for a specific file (123.txt) and lists the result in a textbox. What i need to do is store these result in memory so i can access it later. An array maybe? I'm not exactly sure how that's done.
Here's the code to execute it:
outfile1.open("pxutil1.log");
DWORD dwSize = MAX_PATH;
char szLogicalDrives[MAX_PATH];
DWORD dwResult = GetLogicalDriveStrings(dwSize, szLogicalDrives);
if (dwResult == 0)
{
// error handling...
}
else if (dwResult > MAX_PATH)
{
// not enough buffer space...
}
else
{
for (char* szSingleDrive = szLogicalDrives; *szSingleDrive != 0; szSingleDrive += (lstrlenA(szSingleDrive) + 1))
{
if (GetDriveTypeA(szSingleDrive) == DRIVE_FIXED)
FindFile(szSingleDrive);
}
}
Here's the code to search for 123.txt and list the restult in a logfile.
std::string dir = directory;
if ((!dir.empty()) && (dir.back() != '\\') && (dir.back() != '/'))
dir += '\\';
WIN32_FIND_DATAA file;
HANDLE search_handle = FindFirstFileA((dir + "*").c_str(), &file);
if (search_handle == INVALID_HANDLE_VALUE)
{
if (GetLastError() != ERROR_FILE_NOT_FOUND)
{
// error handling...
//::MessageBox(NULL, "File not found", "", MB_OK);
}
}
else
{
do
{
if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if ((lstrcmpA(file.cFileName, ".") != 0) && (lstrcmpA(file.cFileName, "..") != 0))
{
//FindFile(dir + file.cFileName);
if (!ExcludeDir(dir + file.cFileName))
{
FindFile(dir + file.cFileName);
}
}
}
else
{
if (lstrcmpA(file.cFileName, "123.txt") == 0)
{
outfile1 << dir.c_str() << endl; //write to log file
}
}
} while (FindNextFileA(search_handle, &file));
if (GetLastError() != ERROR_NO_MORE_FILES)
{
::MessageBox(NULL, "No more files", "", MB_OK);
}
FindClose(search_handle);
}
I thought maybe i could add this to the code, but it doesn't work
string listOfDir[20]
std::string(dir) >> listOfDir;
The standard C++ language does not have overloads of operator>> to input arrays.
The following does not work:
std::cin >> listOfDir;
The C++ language does not have any functions to split a string.
The following does not work:
std::string(dir) >> listOfDir;
The compiler can't find any overload of operator>> that takes a std::string parameter and an array parameter. The statement would be equivalent of:
operator>>(std::string, std::string[]);
In summary, you'll need to write the code yourself to parse or split a string; or you can search the internet for a string library.
If you use std::vector, this code may be more useful:
std::vector<std::string> listOfDir;
//...
listOfDir.push_back(dir);
The above code fragment appends a copy of dir to the vector listOfDir.

zlib minizip unpacked executables are corrupted

I'm trying to use miniunzip to extract some files. It works on Linux. On Windows, it throws no errors, but if the file is executable, the resulting binary doesn't work. I get a message window with a message about incompatibility with 64-bit Windows. If I use another utility, such as 7-zip, to unpack it, everything works fine, so the problem is here in my code. Here is the class method that does all the work.
bool FileHandler::unzip( string inputFile, string outputDirectory )
{
if (!fileExists(inputFile)) {
this->errorMessage = "Can't find file at " + inputFile;
return false;
}
unzFile zipFile = unzOpen(inputFile.c_str());
if( zipFile == nullptr ){
this->errorMessage = "FileHandler::unzip failed to open input file";
return false;
}
vector<string> files;
vector<string> folders;
unz_global_info globalInfo;
int err = unzGetGlobalInfo( zipFile, &globalInfo );
if (unzGoToFirstFile(zipFile) != UNZ_OK) {
this->errorMessage = "FileHandler::unzip failed calling unzGoToFirstFile";
return false;
}
for ( unsigned long i=0; i < globalInfo.number_entry && err == UNZ_OK; i++ ){
char filename[FILENAME_MAX];
unz_file_info subFileInfo;
err = unzGetCurrentFileInfo( zipFile, &subFileInfo, filename,
sizeof(filename), NULL, 0, NULL, 0);
if ( err == UNZ_OK )
{
char nLast = filename[subFileInfo.size_filename-1];
if ( nLast =='/' || nLast == '\\' )
{
folders.push_back(filename);
}
else
{
files.push_back(filename);
}
err = unzGoToNextFile(zipFile);
}
}
for ( string & folder : folders ){
string strippedFolder = folder.substr(0, folder.length()-1);
string dirPath = normalizePath(outputDirectory+"/"+strippedFolder);
if( ! makeDirectory( dirPath ) ){
this->errorMessage = "FileHandler::unzip Failed to create directory "+dirPath;
return false;
}
}
for ( auto it = files.begin(); it != files.end(); it++ ){
if( zipFile == 0 ){
this->errorMessage = "FileHandler::unzip invalid unzFile object at position 1";
return false;
}
string filename = *it;
//string filepath = outputDirectory + "/" + *it;
string filepath = normalizePath( outputDirectory + "/" + *it );
const char * cFile = filename.c_str();
const char * cPath = filepath.c_str();
int err = unzLocateFile( zipFile, cFile, 0 );
if ( err != UNZ_OK ){
this->errorMessage = "FileHandler::unzip error locating sub-file.";
return false;
}
err = unzOpenCurrentFile( zipFile );
if( err != UNZ_OK ){
this->errorMessage = "FileHandler::unzip error opening current file";
return false;
}
ofstream fileStream{ cPath };
// Need an ostream object here.
if( fileStream.fail() ){
this->errorMessage = "FileHandler::unzip error opening file stream at "+string(cPath);
return false;
}
unz_file_info curFileInfo;
err = unzGetCurrentFileInfo( zipFile, &curFileInfo, 0, 0, 0, 0, 0, 0);
if ( err != UNZ_OK )
{
this->errorMessage = "FileHandler::unzip failed to read size of file";
return false;
}
unsigned int size = (unsigned int)curFileInfo.uncompressed_size;
char * buf = new char[size];
size = unzReadCurrentFile( zipFile, buf, size );
if ( size < 0 ){
this->errorMessage = "FileHandler::unzip unzReadCurrentFile returned an error. ";
return false;
}
fileStream.write( buf, size );
fileStream.flush();
delete [] buf;
fileStream.close();
#ifndef _WIN32
vector<string> parts = splitString(filename, ".");
if( parts.size() == 1 ){ // In linux, assume file without extension is executable
mode_t old_mask = umask( 000 );
chmod( cPath, S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH );
umask( old_mask );
}
#endif
unzCloseCurrentFile( zipFile );
}
unzClose(zipFile);
return true;
}
std::ostream opens files in text mode by default, you need to make it use binary mode instead.
On Linux there doesn't seem to be any difference between text and binary modes. But on Windows, attempting to write \n into a text file produces \r\n, currupting your data.
You need to change this line
ofstream fileStream{ cPath };
to
ofstream fileStream{ cPath, ostream::out | ostream::binary };

recursive file search

I'm trying to figure out how to work this thing out .. For some reason, it ends at a certain point.. I'm not very good at recursion and I'm sure the problem lies somewhere there..
Also, even if I checked for cFileName != "..", it still shows up at the end, not sure why but the "." doesn't show up anymore..
void find_files( wstring wrkdir )
{
wstring temp;
temp = wrkdir + L"\\" + L"*";
fHandle = FindFirstFile( temp.c_str(), &file_data );
if( fHandle == INVALID_HANDLE_VALUE )
{
return;
}
else
{
while( FindNextFile( fHandle, &file_data ) )
{
if( file_data.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY &&
wcscmp(file_data.cFileName, L".") != 0 &&
wcscmp(file_data.cFileName, L"..") != 0 )
{
find_files( wrkdir + L"\\" + file_data.cFileName );
}
else if( file_data.dwFileAttributes != FILE_ATTRIBUTE_HIDDEN &&
file_data.dwFileAttributes != FILE_ATTRIBUTE_SYSTEM )
{
results << wrkdir << "\\" << file_data.cFileName << endl;
}
}
}
}
After changing those, the program doesn't enumerate the remaining files left..
For example, if there is a sub folder named test, it enumerates everything inside test but doesn't finish enumerating the files inside the original directory specified.
From the FindFirstFile documentation:
If the function fails or fails to
locate files from the search string in
the lpFileName parameter, the return
value is INVALID_HANDLE_VALUE and the
contents of lpFindFileData are
indeterminate.
You should only exit from the one iteration not the whole program:
if( fHandle == INVALID_HANDLE_VALUE )
{
return;
}
And this may solve your other problem:
else if( file_data.dwFileAttributes != FILE_ATTRIBUTE_HIDDEN &&
file_data.dwFileAttributes != FILE_ATTRIBUTE_SYSTEM &&
wcscmp(file_data.cFileName, L".") != 0 &&
wcscmp(file_data.cFileName, L"..") != 0
)
{
results << wrkdir << "\\" << file_data.cFileName << endl;
}
Also see #fretje's answer as well. It gives another problem that your code has.
Updated new: You need to use fHandle as a local variable as well, not global variable.
Change to:
HANDLE fHandle = FindFirstFile( temp.c_str(), &file_data );
You are changing the value of your local wrkdir variable:
wrkdir = wrkdir + L"\\" + file_data.cFileName;
find_files( wrkdir );
I think you have to call find_files there like this:
find_files( wrkdir + L"\\" + file_data.cFileName );
and not change the value of wrkdir.
There are still several bugs in your code. Try this instead:
void find_files( wstring wrkdir )
{
wstring wrkdirtemp = wrkdir;
if( !wrkdirtemp.empty() && (wrkdirtemp[wrkdirtemp.length()-1] != L'\\') )
{
wrkdirtemp += L"\\";
}
WIN32_FIND_DATA file_data = {0};
HANDLE hFile = FindFirstFile( (wrkdirtemp + L"*").c_str(), &file_data );
if( hFile == INVALID_HANDLE_VALUE )
{
return;
}
do
{
if( file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
{
if( (wcscmp(file_data.cFileName, L".") != 0) &&
(wcscmp(file_data.cFileName, L"..") != 0) )
{
find_files( wrkdirtemp + file_data.cFileName );
}
}
else
{
if( (file_data.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) == 0 )
{
results << wrkdirtemp << file_data.cFileName << endl;
}
}
}
while( FindNextFile( hFile, &file_data );
FindClose( hFile );
}
Recursive file search with dirent.h
#include <iostream>
#include <dirent.h>
#include <string.h>
bool isUpDirecory(const char* directory) {
if (strcmp(directory, "..") == 0 || strcmp(directory, ".") == 0)
return true;
else
return false;
}
bool findFile(const std::string& fileName, const std::string& path,
std::string& resultPath) {
dirent* entry;
DIR* dir = opendir(path.c_str());
if (dir == NULL)
return false;
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == DT_REG) {
if (fileName.compare(entry->d_name) == 0) {
resultPath = path + "/" + entry->d_name;
closedir(dir);
return true;
}
}
}
rewinddir(dir);
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == DT_DIR) {
if (!isUpDirecory(entry->d_name)) {
std::string nextDirectoryPath = path + "/" + entry->d_name;
bool result = findFile(fileName, nextDirectoryPath, resultPath);
if (result == true) {
closedir(dir);
return true;
}
}
}
}
closedir(dir);
return false;
}
int main() {
std::string path;
bool result = findFile("text.txt", "/home/lamerman/", path);
std::cout << path << std::endl;
return 0;
}
Also, check out the implementation of the CFileFind MFC class.
You still have errors in your code:
you ignore the results of the first search. you call FindFirstFile and handle if it fails. But if it succeeds you do not process already fetched file_data and overwrite it with FindNextFile.
You don't close the search handle. Use FindClose for that.
From your existing code it seems that fHandle is global - it shouldn't. It would break your recursion.
Also I think that you can resolve all the issues in your code by paying more attention to MSDN sample provided in FindFirstFile documentation.

In an MFC application, what's the easiest way to copy a file from one directory to another?

Should I create two CFile objects and copy one into the other character by character? Or is there something in the library that will do this for me?
I would just use the CopyFile Win32 API function, but the example code in the CFile::Open documentation shows how to copy files with CFile (using pretty much the method you suggest).
It depends on what you want to do. There are a number of ways to copy files:
CopyFile()
CopyFileEx()
SHFileOperation()
IFileOperation (replaces SHFileOperation() in Vista)
While I appreciate the previous answers, I have found that this FileOperations is a nice wrapper that mimics the way copy operations are performed in Windows Explorer, which also includes Copy, Move and Delete files and rename directories:
http://www.ucancode.net/Visual_C_Source_Code/Copy-Move-Delete-files-rename-directories-SHFileOperation-CFileFind-FindFirstFile-FindNextFile-mfc-example.htm
#include "stdafx.h"
#include "FileOperations.h"
//
// this code copy 'c:\source' directory and
// all it's subdirectories and files
// to the 'c:\dest' directory.
//
CFileOperation fo; // create object
fo.SetOverwriteMode(false); // reset OverwriteMode flag (optional)
if (!fo.Copy("c:\\source", "c:\\dest")) // do Copy
{
fo.ShowError(); // if copy fails show error message
}
//
// this code delete 'c:\source' directory and
// all it's subdirectories and files.
//
fo.Setucancode.netIfReadOnly(); // set ucancode.netIfReadonly flag (optional)
if (!fo.Delete("c:\\source")) // do Copy
{
fo.ShowError(); // if copy fails show error message
}
Here is the source code for completeness:
#include "resource.h"
#define PATH_ERROR -1
#define PATH_NOT_FOUND 0
#define PATH_IS_FILE 1
#define PATH_IS_FOLDER 2
class CFExeption
{
public:
CFExeption(DWORD dwErrCode);
CFExeption(CString sErrText);
CString GetErrorText() {return m_sError;}
DWORD GetErrorCode() {return m_dwError;}
private:
CString m_sError;
DWORD m_dwError;
};
//*****************************************************************************************************
class CFileOperation
{
public:
CFileOperation(); // constructor
bool Delete(CString sPathName); // delete file or folder
bool Copy(CString sSource, CString sDest); // copy file or folder
bool Replace(CString sSource, CString sDest); // move file or folder
bool Rename(CString sSource, CString sDest); // rename file or folder
CString GetErrorString() {return m_sError;} // return error description
DWORD GetErrorCode() {return m_dwError;} // return error code
void ShowError() // show error message
{MessageBox(NULL, m_sError, _T("Error"), MB_OK | MB_ICONERROR);}
void SetAskIfReadOnly(bool bAsk = true) // sets behavior with readonly files(folders)
{m_bAskIfReadOnly = bAsk;}
bool IsAskIfReadOnly() // return current behavior with readonly files(folders)
{return m_bAskIfReadOnly;}
bool CanDelete(CString sPathName); // check attributes
void SetOverwriteMode(bool bOverwrite = false) // sets overwrite mode on/off
{m_bOverwriteMode = bOverwrite;}
bool IsOverwriteMode() {return m_bOverwriteMode;} // return current overwrite mode
int CheckPath(CString sPath);
bool IsAborted() {return m_bAborted;}
protected:
void DoDelete(CString sPathName);
void DoCopy(CString sSource, CString sDest, bool bDelteAfterCopy = false);
void DoFileCopy(CString sSourceFile, CString sDestFile, bool bDelteAfterCopy = false);
void DoFolderCopy(CString sSourceFolder, CString sDestFolder, bool bDelteAfterCopy = false);
void DoRename(CString sSource, CString sDest);
bool IsFileExist(CString sPathName);
void PreparePath(CString &sPath);
void Initialize();
void CheckSelfRecursion(CString sSource, CString sDest);
bool CheckSelfCopy(CString sSource, CString sDest);
CString ChangeFileName(CString sFileName);
CString ParseFolderName(CString sPathName);
private:
CString m_sError;
DWORD m_dwError;
bool m_bAskIfReadOnly;
bool m_bOverwriteMode;
bool m_bAborted;
int m_iRecursionLimit;
};
//*****************************************************************************************************
C++ file:
#include "stdafx.h"
#include "resource.h"
#include "FileOperations.h"
//************************************************************************************************************
CFExeption::CFExeption(DWORD dwErrCode)
{
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dwErrCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
m_sError = (LPTSTR)lpMsgBuf;
LocalFree(lpMsgBuf);
m_dwError = dwErrCode;
}
CFExeption::CFExeption(CString sErrText)
{
m_sError = sErrText;
m_dwError = 0;
}
//************************************************************************************************************
CFileOperation::CFileOperation()
{
Initialize();
}
void CFileOperation::Initialize()
{
m_sError = _T("No error");
m_dwError = 0;
m_bAskIfReadOnly = true;
m_bOverwriteMode = false;
m_bAborted = false;
m_iRecursionLimit = -1;
}
void CFileOperation::DoDelete(CString sPathName)
{
CFileFind ff;
CString sPath = sPathName;
if (CheckPath(sPath) == PATH_IS_FILE)
{
if (!CanDelete(sPath))
{
m_bAborted = true;
return;
}
if (!DeleteFile(sPath)) throw new CFExeption(GetLastError());
return;
}
PreparePath(sPath);
sPath += "*.*";
BOOL bRes = ff.FindFile(sPath);
while(bRes)
{
bRes = ff.FindNextFile();
if (ff.IsDots()) continue;
if (ff.IsDirectory())
{
sPath = ff.GetFilePath();
DoDelete(sPath);
}
else DoDelete(ff.GetFilePath());
}
ff.Close();
if (!RemoveDirectory(sPathName) && !m_bAborted) throw new CFExeption(GetLastError());
}
void CFileOperation::DoFolderCopy(CString sSourceFolder, CString sDestFolder, bool bDelteAfterCopy)
{
CFileFind ff;
CString sPathSource = sSourceFolder;
BOOL bRes = ff.FindFile(sPathSource);
while (bRes)
{
bRes = ff.FindNextFile();
if (ff.IsDots()) continue;
if (ff.IsDirectory()) // source is a folder
{
if (m_iRecursionLimit == 0) continue;
sPathSource = ff.GetFilePath() + CString("\\") + CString("*.*");
CString sPathDest = sDestFolder + ff.GetFileName() + CString("\\");
if (CheckPath(sPathDest) == PATH_NOT_FOUND)
{
if (!CreateDirectory(sPathDest, NULL))
{
ff.Close();
throw new CFExeption(GetLastError());
}
}
if (m_iRecursionLimit > 0) m_iRecursionLimit --;
DoFolderCopy(sPathSource, sPathDest, bDelteAfterCopy);
}
else // source is a file
{
CString sNewFileName = sDestFolder + ff.GetFileName();
DoFileCopy(ff.GetFilePath(), sNewFileName, bDelteAfterCopy);
}
}
ff.Close();
}
bool CFileOperation::Delete(CString sPathName)
{
try
{
DoDelete(sPathName);
}
catch(CFExeption* e)
{
m_sError = e->GetErrorText();
m_dwError = e->GetErrorCode();
delete e;
if (m_dwError == 0) return true;
return false;
}
return true;
}
bool CFileOperation::Rename(CString sSource, CString sDest)
{
try
{
DoRename(sSource, sDest);
}
catch(CFExeption* e)
{
m_sError = e->GetErrorText();
m_dwError = e->GetErrorCode();
delete e;
return false;
}
return true;
}
void CFileOperation::DoRename(CString sSource, CString sDest)
{
if (!MoveFile(sSource, sDest)) throw new CFExeption(GetLastError());
}
void CFileOperation::DoCopy(CString sSource, CString sDest, bool bDelteAfterCopy)
{
CheckSelfRecursion(sSource, sDest);
// source not found
if (CheckPath(sSource) == PATH_NOT_FOUND)
{
CString sError = sSource + CString(" not found");
throw new CFExeption(sError);
}
// dest not found
if (CheckPath(sDest) == PATH_NOT_FOUND)
{
CString sError = sDest + CString(" not found");
throw new CFExeption(sError);
}
// folder to file
if (CheckPath(sSource) == PATH_IS_FOLDER && CheckPath(sDest) == PATH_IS_FILE)
{
throw new CFExeption("Wrong operation");
}
// folder to folder
if (CheckPath(sSource) == PATH_IS_FOLDER && CheckPath(sDest) == PATH_IS_FOLDER)
{
CFileFind ff;
CString sError = sSource + CString(" not found");
PreparePath(sSource);
PreparePath(sDest);
sSource += "*.*";
if (!ff.FindFile(sSource))
{
ff.Close();
throw new CFExeption(sError);
}
if (!ff.FindNextFile())
{
ff.Close();
throw new CFExeption(sError);
}
CString sFolderName = ParseFolderName(sSource);
if (!sFolderName.IsEmpty()) // the source is not drive
{
sDest += sFolderName;
PreparePath(sDest);
if (!CreateDirectory(sDest, NULL))
{
DWORD dwErr = GetLastError();
if (dwErr != 183)
{
ff.Close();
throw new CFExeption(dwErr);
}
}
}
ff.Close();
DoFolderCopy(sSource, sDest, bDelteAfterCopy);
}
// file to file
if (CheckPath(sSource) == PATH_IS_FILE && CheckPath(sDest) == PATH_IS_FILE)
{
DoFileCopy(sSource, sDest);
}
// file to folder
if (CheckPath(sSource) == PATH_IS_FILE && CheckPath(sDest) == PATH_IS_FOLDER)
{
PreparePath(sDest);
char drive[MAX_PATH], dir[MAX_PATH], name[MAX_PATH], ext[MAX_PATH];
_splitpath(sSource, drive, dir, name, ext);
sDest = sDest + CString(name) + CString(ext);
DoFileCopy(sSource, sDest);
}
}
void CFileOperation::DoFileCopy(CString sSourceFile, CString sDestFile, bool bDelteAfterCopy)
{
BOOL bOvrwriteFails = FALSE;
if (!m_bOverwriteMode)
{
while (IsFileExist(sDestFile))
{
sDestFile = ChangeFileName(sDestFile);
}
bOvrwriteFails = TRUE;
}
if (!CopyFile(sSourceFile, sDestFile, bOvrwriteFails)) throw new CFExeption(GetLastError());
if (bDelteAfterCopy)
{
DoDelete(sSourceFile);
}
}
bool CFileOperation::Copy(CString sSource, CString sDest)
{
if (CheckSelfCopy(sSource, sDest)) return true;
bool bRes;
try
{
DoCopy(sSource, sDest);
bRes = true;
}
catch(CFExeption* e)
{
m_sError = e->GetErrorText();
m_dwError = e->GetErrorCode();
delete e;
if (m_dwError == 0) bRes = true;
bRes = false;
}
m_iRecursionLimit = -1;
return bRes;
}
bool CFileOperation::Replace(CString sSource, CString sDest)
{
if (CheckSelfCopy(sSource, sDest)) return true;
bool bRes;
try
{
bool b = m_bAskIfReadOnly;
m_bAskIfReadOnly = false;
DoCopy(sSource, sDest, true);
DoDelete(sSource);
m_bAskIfReadOnly = b;
bRes = true;
}
catch(CFExeption* e)
{
m_sError = e->GetErrorText();
m_dwError = e->GetErrorCode();
delete e;
if (m_dwError == 0) bRes = true;
bRes = false;
}
m_iRecursionLimit = -1;
return bRes;
}
CString CFileOperation::ChangeFileName(CString sFileName)
{
CString sName, sNewName, sResult;
char drive[MAX_PATH];
char dir [MAX_PATH];
char name [MAX_PATH];
char ext [MAX_PATH];
_splitpath((LPCTSTR)sFileName, drive, dir, name, ext);
sName = name;
int pos = sName.Find("Copy ");
if (pos == -1)
{
sNewName = CString("Copy of ") + sName + CString(ext);
}
else
{
int pos1 = sName.Find('(');
if (pos1 == -1)
{
sNewName = sName;
sNewName.Delete(0, 8);
sNewName = CString("Copy (1) of ") + sNewName + CString(ext);
}
else
{
CString sCount;
int pos2 = sName.Find(')');
if (pos2 == -1)
{
sNewName = CString("Copy of ") + sNewName + CString(ext);
}
else
{
sCount = sName.Mid(pos1 + 1, pos2 - pos1 - 1);
sName.Delete(0, pos2 + 5);
int iCount = atoi((LPCTSTR)sCount);
iCount ++;
sNewName.Format("%s%d%s%s%s", "Copy (", iCount, ") of ", (LPCTSTR)sName, ext);
}
}
}
sResult = CString(drive) + CString(dir) + sNewName;
return sResult;
}
bool CFileOperation::IsFileExist(CString sPathName)
{
HANDLE hFile;
hFile = CreateFile(sPathName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
if (hFile == INVALID_HANDLE_VALUE) return false;
CloseHandle(hFile);
return true;
}
int CFileOperation::CheckPath(CString sPath)
{
DWORD dwAttr = GetFileAttributes(sPath);
if (dwAttr == 0xffffffff)
{
if (GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_PATH_NOT_FOUND)
return PATH_NOT_FOUND;
return PATH_ERROR;
}
if (dwAttr & FILE_ATTRIBUTE_DIRECTORY) return PATH_IS_FOLDER;
return PATH_IS_FILE;
}
void CFileOperation::PreparePath(CString &sPath)
{
if(sPath.Right(1) != "\\") sPath += "\\";
}
bool CFileOperation::CanDelete(CString sPathName)
{
DWORD dwAttr = GetFileAttributes(sPathName);
if (dwAttr == -1) return false;
if (dwAttr & FILE_ATTRIBUTE_READONLY)
{
if (m_bAskIfReadOnly)
{
CString sTmp = sPathName;
int pos = sTmp.ReverseFind('\\');
if (pos != -1) sTmp.Delete(0, pos + 1);
CString sText = sTmp + CString(" is read olny. Do you want delete it?");
int iRes = MessageBox(NULL, sText, _T("Warning"), MB_YESNOCANCEL | MB_ICONQUESTION);
switch (iRes)
{
case IDYES:
{
if (!SetFileAttributes(sPathName, FILE_ATTRIBUTE_NORMAL)) return false;
return true;
}
case IDNO:
{
return false;
}
case IDCANCEL:
{
m_bAborted = true;
throw new CFExeption(0);
return false;
}
}
}
else
{
if (!SetFileAttributes(sPathName, FILE_ATTRIBUTE_NORMAL)) return false;
return true;
}
}
return true;
}
CString CFileOperation::ParseFolderName(CString sPathName)
{
CString sFolderName = sPathName;
int pos = sFolderName.ReverseFind('\\');
if (pos != -1) sFolderName.Delete(pos, sFolderName.GetLength() - pos);
pos = sFolderName.ReverseFind('\\');
if (pos != -1) sFolderName = sFolderName.Right(sFolderName.GetLength() - pos - 1);
else sFolderName.Empty();
return sFolderName;
}
void CFileOperation::CheckSelfRecursion(CString sSource, CString sDest)
{
if (sDest.Find(sSource) != -1)
{
int i = 0, count1 = 0, count2 = 0;
for(i = 0; i < sSource.GetLength(); i ++) if (sSource[i] == '\\') count1 ++;
for(i = 0; i < sDest.GetLength(); i ++) if (sDest[i] == '\\') count2 ++;
if (count2 >= count1) m_iRecursionLimit = count2 - count1;
}
}
bool CFileOperation::CheckSelfCopy(CString sSource, CString sDest)
{
bool bRes = false;
if (CheckPath(sSource) == PATH_IS_FOLDER)
{
CString sTmp = sSource;
int pos = sTmp.ReverseFind('\\');
if (pos != -1)
{
sTmp.Delete(pos, sTmp.GetLength() - pos);
if (sTmp.CompareNoCase(sDest) == 0) bRes = true;
}
}
return bRes;
}
The Copy option in your code requires the dest file or folder to first exist otherwise this
if (CheckPath(sDest) == PATH_NOT_FOUND)
will always cause an error.