I have a C++ program that when starts, gets the list of all files in a directory and does some processing with them (packetizes them for transmission over network). The current version is static and the program only sees the files at the time the program starts. Now I want to change it to a dynamic version where it can continuously see the new files added in the directory (let's assume they have a meaningful incremented name; for instance f1, f2, f3, ... so next one coming would be f4. This way I can use some index).
Right now this is the piece of code (static reading of all files):
DIR *dp;
struct dirent *dirp;
char * dir = new char[inData->dir.length() + 1];
strcpy(dir, inData->dir.c_str());
dp = opendir(dir);
int file_index = 0;
int total_files = DataUtil::count_files(dp);
if (readdir(dp) == NULL)
rewinddir(dp);
//for each file in the directory
while ((dirp = readdir( dp ))) {
string f_name = dirp->d_name;
if (f_name != "." && f_name != "..") { // except . and ..
//Do some processing
my_function(f_name, file_index);
file_index++;
}
}
closedir(dp);
I modified the code using the rewinddir(dp); function and file_index to notice new files. But it doesn't work. Where is the problem?
DIR *dp;
struct dirent *dirp;
char * dir = new char[inData->dir.length() + 1];
strcpy(dir, inData->dir.c_str());
dp = opendir(dir);
int file_index = 0;
int total_files = MyDataUtil::count_files(dp);
if (readdir(dp) == NULL)
rewinddir(dp);
//for each file in the directory
while ((dirp = readdir( dp ))) {
string f_name = dirp->d_name;
if (f_name != "." && f_name != "..") {
//skip files already read
for (int var = 0; var < file_index; var++) {
readdir(dp);
}
//Do some processing
my_function(f_name, file_index);
file_index++;
//reset dir stream, and update total_files
rewinddir(dp);
total_files = MyDataUtil::count_files(dp);
}
}
closedir(dp);
The total_files function:
int MyDataUtil::count_files(DIR *dp){
struct dirent *dirp;
int num = 0;
while ((dirp = readdir( dp ))) {
string f_name = dirp->d_name;
if (f_name != "." && f_name != "..")
num++;
}
return num;
}
Here is the rewinddir documentation. It says ' might or might not be returned'!
Description:
The rewinddir function is used to reinitialize the directory stream dirstream, so that if you call readdir
it returns information about the first entry in the directory again. This function also notices if files
have been added or removed to the directory since it was opened with opendir. (Entries for these files
might or might not be returned by readdir if they were added or removed since you last called opendir
or rewinddir.)
Use inotify on Linux to get asynchronous notifications when files are added / removed / changed in a directory of your choice
http://man7.org/linux/man-pages/man7/inotify.7.html
Here is another way, maybe you can try it.
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <list>
#include <Locale.h>
using namespace std;
void ListFile(std::list<std::wstring> &list_Steady)
{
setlocale(LC_ALL, "chs");
WIN32_FIND_DATAW FindFileData;
HANDLE hFind;
LPCWSTR s = L"C:\\Test_Monitor\\*.*";//The path of Directory you want to Monitor
hFind = FindFirstFileW(s, &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
printf ("FindFirstFile failed (%d)\n", GetLastError());
return ;
}
else
{
wcout << "The first file found is " << FindFileData.cFileName << endl;
list_Steady.push_back(FindFileData.cFileName);
}
while(FindNextFile(hFind, &FindFileData))
{
wcout << "The next file found is " << FindFileData.cFileName << endl;
list_Steady.push_back(FindFileData.cFileName);
}
FindClose(hFind);
hFind = NULL;
}
void CompareFile(std::list<std::wstring> &new_list , std::list<std::wstring> &steady_list)
{
bool bFound = false;
for (std::list<std::wstring>::iterator it = new_list.begin(); it != new_list.end(); ++it)
{
bFound = false;
for (std::list<std::wstring>::iterator steady_it = steady_list.begin(); steady_it != steady_list.end(); ++steady_it)
{
//Compare to origin list, if file still exist will return true
if(it->compare(steady_it->c_str()) == 0)
{
bFound = true;
}
}
//else this is a new file
if(!bFound)
{
wcout << it->c_str() << L" new file detected" << endl;
}
}
bool bfound2 = false;
for (std::list<std::wstring>::iterator it = steady_list.begin(); it != steady_list.end(); ++it)
{
bfound2 = false;
for (std::list<std::wstring>::iterator new_it = new_list.begin(); new_it != new_list.end(); ++new_it)
{
//Compare to origin list, if file still exist will return true
if(it->compare(new_it->c_str()) == 0)
{
bfound2 = true;
}
}
//else this file has been deleted
if(!bfound2)
{
wcout<< it->c_str() << L" This file has disappeared" << endl;
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
list<wstring> Steady_Directory;
list<wstring> New_Directory;
DWORD WaitStatus = 0;
Steady_Directory.clear(); New_Directory.clear(); //be sure that list is clean
ListFile(Steady_Directory);
HANDLE FileChange = INVALID_HANDLE_VALUE;
FileChange = ::FindFirstChangeNotification(L"C:\\Test_Monitor",FALSE,FILE_NOTIFY_CHANGE_FILE_NAME);
if(FileChange == INVALID_HANDLE_VALUE)
{
cout << "LOG : FAIL to get File Change, With error code: " << ::GetLastError() << endl;
return -1;
}
WaitStatus = ::WaitForSingleObject(FileChange, INFINITE);
if (WaitStatus == WAIT_OBJECT_0)
{
ListFile(New_Directory);
CompareFile(New_Directory, Steady_Directory);
}
system("PAUSE");
return 0;
}
I use two list, one stand for old state, the other is for the next state, and use ::FindFirstChangeNotification to subscribe specific directory's state.
Any file change in the directory will set the HANDLE Filechange, then will call the function CompareFile to make sure which file has been added or deleted.
You can make a for/while loop to make this monitor work constantly
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I am looking for something that help with my code. I just want to analyse my program files folder on windows and print all of .exe files in this folder. I wrote something but I need some help and after that I'll make the .exe files to button but this is not my challenge right now the first part is my issue :)
int dirListFiles(char* startDir)
{
HANDLE hFind;
WIN32_FIND_DATAA wfd;
char path[MAX_PATH];
sprintf(path, "%s\\*", startDir);
std::string fileName;
std::string s_path = startDir;
std::string fullPath;
fprintf(stdout, "In Directory \"%s\"\n\n", startDir);
if ((hFind = FindFirstFileA(path, &wfd)) == INVALID_HANDLE_VALUE)
{
printf("FindFirstFIle failed on path = \"%s\"\n", path);
abort();
}
BOOL cont = TRUE;
while (cont == TRUE)
{
if ((strncmp(".", wfd.cFileName, 1) != 0) && (strncmp("..", wfd.cFileName, 2) != 0))
{
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
sprintf(path, "%s\\%s", startDir, wfd.cFileName);
dirListFiles(path);
}
else
{
fileName = wfd.cFileName;
fullPath = s_path + "\\" + fileName;
std::string fileExt = PathFindExtensionA(fullPath.c_str()); //the big"A"in PathFindExtensionA is very important :)((()
if (fileExt == ".exe")
{
std::cout << fileExt << std::endl;
}
}
cont = FindNextFileA(hFind, &wfd);
}
FindClose(hFind);
}
}
int main()
{
return 0; //this part is not very important for me right now
}
this is working very well
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <iostream>
#include <string>
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
HANDLE hFind;
WIN32_FIND_DATAA wfd;
int counter = 0;
int counter2 = 0;
int dirListFiles(const char* startDir)
{
HANDLE hFind;
WIN32_FIND_DATAA wfd;
char path[MAX_PATH];
sprintf(path, "%s\\*", startDir);
std::string fileName;
std::string s_path = startDir;
std::string fullPath;
//fprintf(stdout, "In Directory \"%s\"\n\n", startDir);
if ((hFind = FindFirstFileA(path, &wfd)) == INVALID_HANDLE_VALUE)
{
printf("FindFirstFIle failed on path = \"%s\"\n", path);
//abort();
}
BOOL cont = TRUE;
while (cont == TRUE)
{
if ((strncmp(".", wfd.cFileName, 1) != 0) && (strncmp("..", wfd.cFileName, 2) != 0))
{
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
counter2++;
sprintf(path, "%s\\%s", startDir, wfd.cFileName);
dirListFiles(path);
}
else
{
counter++;
fileName = wfd.cFileName;
fullPath = s_path + "\\" + fileName;
std::string fileExt = PathFindExtension(fullPath.c_str());
if (fileExt == ".exe")
{
std::cout << fileName << "\n";
}
}
}
cont = FindNextFile(hFind, &wfd);
}
FindClose(hFind);
return 0;
}
int main(int argc, char *argv[])
{
const char* myfuckingstr = argv[1];
dirListFiles(myfuckingstr);
std::cout << "Counts: " << counter2 << "\n";
return 0;
}
just don't give a fake negative
test it and the leave command;)
I'm trying to get all of the file with a certain pattern from a specific directory.
for example "ia_aiq_usecase_*.bin" from "c:\temp"
my windows code works fine.
however in linux I do not want to use boost. and I can't seem to find a way to get all the files with a certain pattern.
Can you please help ?
Is there a way to do it cross platform?
const std::vector<std::string> OS::GetAllFiles(std::string directoryPath, std::string filePattern)
{
#ifdef _WIN32
std::vector<std::string> files;
std::string pathAndPattern = directoryPath + OS::PathSeperator() + filePattern;
WIN32_FIND_DATA fd;
HANDLE hFind = ::FindFirstFile(ToWString(pathAndPattern).c_str(), &fd);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
files.push_back(FromWString(fd.cFileName));
}
}
while (::FindNextFile(hFind, &fd));
{
::FindClose(hFind);
}
}
return files;
#else
std::vector<std::string> files;
std::string search_path = directoryPath + OS::PathSeperator() + filePattern;
DIR *dp;
struct dirent *dirp;
if ((dp = opendir(directoryPath.c_str())) == NULL)
{
std::cout << "Error(" << errno << ") opening " << directoryPath << std::endl;
return errno;
}
while ((dirp = readdir(dp)) != NULL) {
files.push_back(string(dirp->d_name));
}
closedir(dp);
return 0;
#endif
}
i'm getting error:Debug Error R6010 -abort() has been called
in my code :
bool ListFiles(wstring path, wstring mask, vector<wstring>& files) {
HANDLE hFind = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA ffd;
wstring spec;
stack<wstring> directories;
directories.push(path);
files.clear();
while (!directories.empty()) {
path = directories.top();
spec = path + L"\\" + mask;
directories.pop();
hFind = FindFirstFile(spec.c_str(), &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return false;
}
do {
if (wcscmp(ffd.cFileName, L".") != 0 && wcscmp(ffd.cFileName, L"..") != 0) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
directories.push(path + L"/" + ffd.cFileName);
}
else {
files.push_back(path + L"/" + ffd.cFileName);
}
}
} while (FindNextFile(hFind, &ffd) != 0);
if (GetLastError() != ERROR_NO_MORE_FILES) {
FindClose(hFind);
return false;
}
FindClose(hFind);
hFind = INVALID_HANDLE_VALUE;
}
return true;}
void findText(std::string filename, std::string word , promise<string> &prom) {
std::ifstream f(filename);
std::string s;
std::string notFound = "no";
bool found = false;
if (!f) {
std::cout << "il file non esiste"<<std::endl;
}
while (f.good()) {
std::getline(f, s);
if (s.find(word, 0) != string::npos) {
found = true;
}
}
if (found) {
//cout << "Parola trovata in -> " << filename << endl;
prom.set_value_at_thread_exit(filename);
}
else {
prom.set_value_at_thread_exit(notFound);
}
f.close();}
int main(int argc, char* argv[]){
//vector<std::thread> th;
vector<future<string>> futures;
vector<wstring> files;
string search = "ciao";
string notFound = "no";
if (ListFiles(L"pds", L"*", files)) {
for (vector<wstring>::iterator it = files.begin(); it != files.end(); ++it) {
//wcout << it->c_str() << endl;
wstring ws(it->c_str());
string str(ws.begin(), ws.end());
// Show String
//cout << str << endl;
//Creo una promise per ogni thread in cui andrò a cercare il risulato
std::promise<string> prom;
futures.push_back(prom.get_future());
std::thread(findText,str,search,std::ref(prom)).detach();
}
}
for (int i = 0; i < futures.size(); i++){
futures.at(i).wait();
if (futures.at(i).get().compare(notFound)!=0)
cout << "Parola trovata in ->" <<futures.at(i).get()<<endl;
}
return 0;}
I've tried before without using promises and making each thread printing the name of file if word was found and it worked.
So i don't know why using promises and future to retrive this value cause me this problem...
I'm using VS 2013
Lets take a close look at these lines lines:
for (...) {
...
std::promise<string> prom;
...
std::thread(findText,str,search,std::ref(prom)).detach();
}
First you create a local variable prom, and then you pass a reference to that variable to the thread.
The problem with this is that once the loop iterates, the variable prom goes out of scope and the object is destructed. The reference you once had no longer have anything it references. Using the reference will lead to undefined behavior.
So the solution is to not use references (or pointers to the prom variable), which leads to problems because std::promise can't be copied. It can, however, be moved:
std::thread(findText,str,search,std::move(prom)).detach();
Fir this you might need to make the thread function take the promise argument as a rvalue reference:
void findText(std::string filename, std::string word , promise<string> &&prom) {
...
}
If the above solution doesn't work, then you could use dynamic allocation using the new C++11 smart pointers like std::unique_ptr.
Then the thread function should take the smart pointer by value, like
void findText(std::string filename, std::string word , std::unique_ptr<std::promise<string>> prom) {
...
}
And you create the thread like
auto prom = std::make_unique<std::promise<std::string>>();
// Or if you don't have std::make_unique
// std::unique_ptr<std::promise<std::string>> prom(new std::promise<std::string>);
futures.push_back(prom->get_future();
std::thread(findText,str,search,std::move(prom)).detach();
Remember that in your thread function (findText) the variable prom is a pointer and you need to use the arrow operator when using it, like e.g.
prom->set_value_at_thread_exit(filename);
// ^^
// Note "arrow" operator here
I'm trying to remove directories using this code, but this wouldn't work. And, I couldn't found the issue. Maybe lack of privileges on the deletion operation on Windows?
Here is the code that I'm using:
#define _CRT_SECURE_NO_DEPRECATE
#include <string>
#include <iostream>
#include <windows.h>
#include <conio.h>
int DeleteDirectory(const std::string &refcstrRootDirectory,
bool bDeleteSubdirectories = true)
{
bool bSubdirectory = false; // Flag, indicating whether
// subdirectories have been found
HANDLE hFile; // Handle to directory
std::string strFilePath; // Filepath
std::string strPattern; // Pattern
WIN32_FIND_DATA FileInformation; // File information
strPattern = refcstrRootDirectory + "\\*.*";
hFile = ::FindFirstFile((LPCWSTR)strPattern.c_str(), &FileInformation);
if (hFile != INVALID_HANDLE_VALUE)
{
do
{
if (FileInformation.cFileName[0] != '.')
{
strFilePath.erase();
strFilePath = refcstrRootDirectory + "\\" + (char*)FileInformation.cFileName;
if (FileInformation.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (bDeleteSubdirectories)
{
// Delete subdirectory
int iRC = DeleteDirectory(strFilePath, bDeleteSubdirectories);
if (iRC)
return iRC;
}
else
bSubdirectory = true;
}
else
{
// Set file attributes
if (::SetFileAttributes((LPCWSTR)strFilePath.c_str(),
FILE_ATTRIBUTE_NORMAL) == FALSE)
return ::GetLastError();
// Delete file
if (::DeleteFile((LPCTSTR)strFilePath.c_str()) == FALSE)
return ::GetLastError();
}
}
} while (::FindNextFile(hFile, &FileInformation) == TRUE);
// Close handle
::FindClose(hFile);
DWORD dwError = ::GetLastError();
if (dwError != ERROR_NO_MORE_FILES)
return dwError;
else
{
if (!bSubdirectory)
{
// Set directory attributes
if (::SetFileAttributes((LPCWSTR)refcstrRootDirectory.c_str(),
FILE_ATTRIBUTE_NORMAL) == FALSE)
return ::GetLastError();
// Delete directory
if (::RemoveDirectory((LPCWSTR)refcstrRootDirectory.c_str()) == FALSE)
return ::GetLastError();
}
}
}
return 0;
}
int main()
{
int iRC = 0;
std::string strDirectoryToDelete = "C:\\Users\\AbysCo\\Desktop\\del";
// Delete 'c:\mydir' without deleting the subdirectories
iRC = DeleteDirectory(strDirectoryToDelete, false);
if (iRC)
{
std::cout << "Error " << iRC << std::endl;
return -1;
}
// Delete 'c:\mydir' and its subdirectories
iRC = DeleteDirectory(strDirectoryToDelete);
if (iRC)
{
std::cout << "Error " << iRC << std::endl;
return -1;
}
// Wait for keystroke
_getch();
return 0;
}
OS: Windows 7 SP1.
Any brilliant idea, please?
The Windows API already comes with a function to delete entire directories. That is SHFileOperation. And on Vista or later you use IFileOperation for the same purpose.
Not only does this make the process trivial, not much more than a one liner, but it has other benefits:
You can put the deleted directory into the recycle bin if you choose.
You can show the standard system progress dialog if you choose.
i have a program that search for files of a particular extention(.apk) in a particular logical drive(C:). my system has 3 more partitions :- D: E: F: and these also contains apk files. now i want that my program will also search in these logical drives for the apk's files. how i can do this. please anybody have some suggestion then help me, am trying this since morning. here is my code.....
int SearchDirectory(std::vector<std::string> &refvecFiles,
const std::string &refcstrRootDirectory,
const std::string &refcstrExtension,
bool bSearchSubdirectories = true)
{
std::string strFilePath; // Filepath
std::string strPattern; // Pattern
std::string strExtension; // Extension
HANDLE hFile; // Handle to file
WIN32_FIND_DATA FileInformation; // File information
strPattern = refcstrRootDirectory + "\\*.*";
hFile = FindFirstFile(strPattern.c_str(), &FileInformation);
if(hFile != INVALID_HANDLE_VALUE)
{
do
{
if(FileInformation.cFileName[0] != '.')
{
strFilePath.erase();
strFilePath = refcstrRootDirectory + "\\" + FileInformation.cFileName;
if(FileInformation.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if(bSearchSubdirectories)
{
// Search subdirectory
int iRC = SearchDirectory(refvecFiles,
strFilePath,
refcstrExtension,
bSearchSubdirectories);
if(iRC)
return iRC;
}
}
else
{
// Check extension
strExtension = FileInformation.cFileName;
strExtension = strExtension.substr(strExtension.rfind(".") + 1);
if(strExtension == refcstrExtension)
{
// Save filename
refvecFiles.push_back(strFilePath);
}
}
}
} while(FindNextFile(hFile, &FileInformation) == TRUE);
// Close handle
FindClose(hFile);
DWORD dwError = GetLastError();
if(dwError != ERROR_NO_MORE_FILES)
return dwError;
}
return 0;
}
int main()
{
int iRC = 0;
std::vector<std::string> vecAPKFiles;
//std::vector<std::string> vecTxtFiles;
// Search 'c:' for '.apk' files including subdirectories
iRC = SearchDirectory(vecAPKFiles, "c:", "apk");
if(iRC)
{
std::cout << "Error " << iRC << std::endl;
return -1;
}
// Print results
for(std::vector<std::string>::iterator iterAvi = vecAPKFiles.begin();
iterAvi != vecAPKFiles.end();
++iterAvi)
std::cout << *iterAvi << std::endl;
TCHAR szDrive[] = (" A:");
DWORD uDriveMask = GetLogicalDrives();
while(uDriveMask)
{
// Use the bitwise AND, 1â€"available, 0-not available
if(uDriveMask & 1)
printf("%s ", (const char *)szDrive);
// increment, check next drive
++szDrive[1];
// shift the bitmask binary right
uDriveMask >>= 1;
}
printf("\n ");
// Wait for keystroke
_getch();
return 0;
}
You've got a bit weird drive string char szDrive[] = " A:"; (Even with the half-baked TCHAR stuff removed). I'd use char szDrive[] = "A:\"; instead, and increment ++szDrive[0];. You can then pass szDrive to SearchDirectory()