C++ recursive_directory_iterator miss some files - c++

I'm trying to get all files in directory through c++17 on my visual studio 2017 but I've just encountered a really weird problem. If I specify directory like this I can get all files without any problem:
for (auto& p : std::filesystem::recursive_directory_iterator("C:\\Users\\r00t\\AppData\\Roaming\\Mozilla")) {
if (std::filesystem::is_regular_file(p.path())) {
std::cout << p.path() << std::endl;
}
}
But I need all file list on APPDATA, and I'm trying to get path with getenv() function and when using it "recursive_directory_iterator" function skipping files:
for (auto& p : std::filesystem::recursive_directory_iterator(getenv("APPDATA"))) {
if (std::filesystem::is_regular_file(p.path())) {
std::cout << p.path() << std::endl;
}
}
Is that because of using getenv() function? Some folders that skipping when using getenv;
Mozilla
TeamWiever
NVIDIA
and so on ..
Btw, I'm using C++ last 5 days and definitely don't have any clue what causes for that behavior. Please help me, right now I'm stuck.
EDIT :
for (auto& p : std::filesystem::directory_iterator(getenv("APPDATA"))) {
std::string targetFolder = p.path().string();
for (auto& targetFolderFiles : std::filesystem::recursive_directory_iterator(targetFolder)) {
if (std::filesystem::is_regular_file(targetFolderFiles.path())) {
std::cout << targetFolderFiles.path() << std::endl;
}
}
}
This is also not working, seems like i must put string into function like this:
recursive_directory_iterator("C:\\Users\\r00t\\AppData\\Roaming\\Mozilla")
otherwise definitely not working, LOL ??
EDIT - PROBLEM FIXED
Using experimental library is working with C++14 compiler like as expected.
#include <experimental/filesystem>
Now i can able to get all files without problem.Seems like this is problem about C++17 and filesystem library ..
Thanks for all support guys.

getenv() returns a char* or NULL. <filesystem> is probably operating with wchar_t* strings since you are on Windows. Use SHGetKnownFolderPath(...) to query for where special folders are.
What happens when you run your program is probably that you hit some character that can't be displayed with your current locale ("C" if not set explicitly) so it sets your outstream in fail mode. You can however set your locale to UTF-16LE to remedy this. It works with /std:c++17 and the standard <filesystem> header:
#include <Shlobj.h> // SHGetKnownFolderPath
#include <clocale> // std::setlocale
#include <io.h> // _setmode
#include <fcntl.h> // _O_U16TEXT
Code Page Identifiers
const char CP_UTF_16LE[] = ".1200";
setlocale(LC_ALL, CP_UTF_16LE);
_setmode
_setmode(_fileno(stdout), _O_U16TEXT);
With that in place, the path you get from SHGetKnownFolderPath should work:
PWSTR the_path;
if(SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DEFAULT, NULL, &the_path) == S_OK) {
for(auto& p : std::filesystem::recursive_directory_iterator(the_path)) {
std::wcout << p.path() << L"\n";
// you can also detect if the outstream is in fail mode:
if (std::wcout.fail()) {
std::wcout.clear(); // ... and clear the fail mode
std::wcout << L" (wcout was fail mode)\n";
}
}
CoTaskMemFree(the_path);
}
You may also find the list of Default Known Folders in Windows useful.

Related

Get culture info of the system using win32 application

I am working win32 console application. I want to get the current system locale or culture info in my win32 application.
Like en-US or zh-CN.
Is there anything provided by WINAPI.
Sample code for this will really help.
You should use GetLocaleInfo.
wchar_t szISOLang[5] = { 0 };
wchar_t szISOCountry[5] = { 0 };
::GetLocaleInfo(LOCALE_USER_DEFAULT,
LOCALE_SISO639LANGNAME,
szISOLang,
sizeof(szISOLang) / sizeof(wchar_t));
::GetLocaleInfo(LOCALE_USER_DEFAULT,
LOCALE_SISO3166CTRYNAME,
szISOCountry,
sizeof(szISOCountry) / sizeof(WCHAR));
std::wcout << szISOLang << "_" << szISOCountry << std::endl;
In C or C++, you can create a locale based on a name, so if you supply a name like en-US it will create a matching locale (assuming you use one of the strings it knows about--obviously most libraries aren't going to recognize every possible string).
This has one little-known feature though: if you supply an empty string, it will create a locale that's appropriate for the environment as configured by the user (determined by some means the language doesn't specify).
So, you can retrieve that, and use it. For example:
#include <locale>
#include <iostream>
int main() {
auto loc = std::locale("");
std::cout << loc.name() << "\n";
}
On the machine I'm using at the moment (Linux), this prints out: en_US.UTF-8.

wcout does not output as desired

I've been trying to write a C++ application for a project and I ran into this issue. Basically:
class OBSClass
{
public:
wstring ClassName;
uint8_t Credit;
uint8_t Level;
OBSClass() : ClassName(), Credit(), Level() {}
OBSClass(wstring name, uint8_t credit, uint8_t hyear)
: ClassName(name), Credit(credit), Level(hyear)
{}
};
In some other file:
vector<OBSClass> AllClasses;
...
AllClasses.push_back(OBSClass(L"Bilişim Sistemleri Mühendisliğine Giriş", 3, 1));
AllClasses.push_back(OBSClass(L"İş Sağlığı ve Güvenliği", 3, 1));
AllClasses.push_back(OBSClass(L"Türk Dili 1", 2, 1));
... (rest omitted, some of entries have non-ASCII characters like 'ş' and 'İ')
I have a function basically outputs everything in AllClasses, the problem is wcout does not output as desired.
void PrintClasses()
{
for (size_t i = 0; i < AllClasses.size(); i++)
{
wcout << "Class: " << AllClasses[i].ClassName << "\n";
}
}
Output is 'Class: Bili' and nothing else. Program does not even tries to output other entries and just hangs. I am on windows using G++ 6.3.0. And I am not using Windows' cmd, I am using bash from mingw, so encoding will not be problem (or isn't it?). Any advice?
Edit: Also source code encoding is not a problem, just checked it is UTF8, default of VSCode
Edit: Also just checked to find out if problem is with string literals.
wstring test;
wcin >> test;
wcout << test;
Entered some non-ASCII characters like 'ö' and 'ş', it works perfectly. What is the problem with wide string literals?
Edit: Here you go
#include <iostream>
#include <string>
#include <vector>
using namespace std;
vector<wstring> testvec;
int main()
{
testvec.push_back(L"Bilişim Sistemleri Mühendisliğine Giriş");
testvec.push_back(L"ıiÖöUuÜü");
testvec.push_back(L"☺☻♥♦♣♠•◘○");
for (size_t i = 0; i < testvec.size(); i++)
wcout << testvec[i] << "\n";
return 0;
}
Compile with G++:
g++ file.cc -O3
This code only outputs 'Bili'. It must be something with the g++ screwing up binary encoding (?), since entering values with wcin then outputting them with wcout does not generate any problem.
The following code works for me, using MinGW-w64 7.3.0 in both MSYS2 Bash, and Windows CMD; and with the source encoded as UTF-8:
#include <iostream>
#include <locale>
#include <string>
#include <codecvt>
int main()
{
std::ios_base::sync_with_stdio(false);
std::locale utf8( std::locale(), new std::codecvt_utf8_utf16<wchar_t> );
std::wcout.imbue(utf8);
std::wstring w(L"Bilişim Sistemleri Mühendisliğine Giriş");
std::wcout << w << '\n';
}
Explanation:
The Windows console doesn't support any sort of 16-bit output; it's only ANSI and a partial UTF-8 support. So you need to configure wcout to convert the output to UTF-8. This is the default for backwards compatibility purposes, though Windows 10 1803 does add an option to set that to UTF-8 (ref).
imbue with a codecvt_utf8_utf16 achieves this; however you also need to disable sync_with_stdio otherwise the stream doesn't even use the facet, it just defers to stdout which has a similar problem.
For writing to other files, I found the same technique works to write UTF-8. For writing a UTF-16 file you need to imbue the wofstream with a UTF-16 facet, see example here, and manually write a BOM.
Commentary: Many people just avoid trying to use wide iostreams completely, due to these issues.
You can write a UTF-8 file using a narrow stream; and have function calls in your code to convert wstring to UTF-8, if you are using wstring internally; you can of course use UTF-8 internally.
Of course you can also write a UTF-16 file using a narrow stream, just not with operator<< from a wstring.
If you have at least Windows 10 1903 (May 2019), and at least
Windows Terminal 0.3.2142 (Aug 2019). Then set Unicode:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage]
"OEMCP"="65001"
and restart. After that you can use this:
#include <iostream>
int main() {
std::string a[] = {
"Bilişim Sistemleri Mühendisliğine Giriş",
"Türk Dili 1",
"İş Sağlığı ve Güvenliği",
"ıiÖöUuÜü",
"☺☻♥♦♣♠•◘○"
};
for (auto s: a) {
std::cout << s << std::endl;
}
}

How to fix CopyFile() error 5 - access denied error

I am trying to write a copy file function that can be used on both Linux and Windows. It works on Linux, but on Windows, I get error code 5 when trying to use the WinApi function CopyFile().
In header File.h
This is the custom defined function in the File namespace that I should be able to use on both Linux and windows.
class File
{
public:
static bool copyFile(std::string source, std::string destination);
private:
}
In File.cpp
For Linux it is simple:
#ifdef __unix__
#include "File.h"
bool File::copyFile(std::string source, std::string destination)
{
std::string arg = source + " " + destination;
return launchProcess("cp", arg);
}
#endif
In the Windows specific block of code, I use the WinAPI (#include < windows.h >) function CopyFile(). This accepts LPCWSTR data types instead of strings. To overcome this I have created a function that converts strings to LPCWSTR types.
#ifdef _WIN32
#include "File.h"
#include <Windows.h>
std::wstring strtowstr(const std::string &str)
{
// Convert an ASCII string to a Unicode String
std::wstring wstrTo;
wchar_t *wszTo = new wchar_t[str.length() + 1];
wszTo[str.size()] = L'\0';
MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, wszTo,(int)str.length());
wstrTo = wszTo;
delete[] wszTo;
return wstrTo;
}
bool File::copyFile(std::string source, std::string destination)
{
std::wstring wsource = strtowstr(source);
std::wstring wdestination = strtowstr(destination);
int result = CopyFileW(wsource.c_str(), wdestination.c_str(), TRUE);
//for debugging...
std::wcout << "The error is " << GetLastError() <<std::endl;
std::wcout << wsource.c_str() << std::endl;
std::wcout << wdestination.c_str() << std::endl;
if (result == 0)
{
return false;
}
return true;
}
#endif
In my Test Programme
TEST(all,main_copy_file)
{
std::cout << "Testing copyFile() function..." << std::endl;
std::string srcDir = File::currentWorkingDirectory() + "srcDir";
File::makeDirectory(srcDir);
std::string destDir = File::currentWorkingDirectory() + "destDir/";
File::makeDirectory(destDir);
File::makeFile(srcDir, "testFile", ".txt");
ASSERT_TRUE(File::fileExists(srcDir + "/testFile.txt")) << "Error: Test file has not been generated" << std::endl;
ASSERT_TRUE(File::directoryExists(destDir)) << "Error: Destination directory does not exist" <<std::endl;
ASSERT_TRUE(File::copyFile(srcDir + "/testFile.txt", destDir)) << "Error: Coppy unsucsessfull" << std::endl;
ASSERT_TRUE(File::fileExists(destDir + "/testFile.txt")) << "Error: CoppyFile() flagged as sucsessfull but file does not exist" << std::endl;
}
In the application Output (on Windows)
/*
Testing copyFile() function...
The error is 5
C:\GIT\CorteX\Externals\OSAL\build\Debug/srcDir/testFile.txt
C:\GIT\CorteX\Externals\OSAL\build\Debug/destDir/
error: Value of: File::copyFile(srcDir + "/testFile.txt", destDir)
Actual: false
Expected: true
Error: Coppy unsucsessfull
*/
Error code 5 is an access denied error. I think it gives this error when either the directory does not exist, the directory is open somewhere else, or I do not have permissions.
Since I have tested that the directory does exist, I think it must be one of the latter two. I might only have restricted Admin rights (I don't know), but I can paste into the "destDir" without admin permission. So maybe it thinks the directory is open? Is there a command that exists to make sure the directory is closed?
The test is successful when running on Linux.
The CopyFile API expects file names for both source and destination files. Your code passes a directory name for the destination. This causes the API to fail. You need to append the file name for the destination as well.
Besides that, there are several other issues with your code:
The path separator on Windows is a backslash (\). Your are mixing forward slashes (/) and backslashes. Depending on the arguments passed, the system won't translate forward slashes to backslashes, before passing them on to lower-level file I/O API's.
You are calling GetLastError too late. You need to call it immediately, whenever it is documented to return a meaningful value. Do not intersperse it with any other code, however trivial it may appear to you. That code can modify and invalidate the calling thread's last error code.
Your code assumes ASCII-encoded strings. This will stop working, when dealing with files containing non-ASCII characters. This is quite common.
new wchar_t[...] buys you nothing over std::vector<wchar_t>, except the possibility to introduce bugs.
Your MultiByteToWideChar-based string conversion implementation makes (undue) assumptions about the code unit requirements of different character encodings. Those assumptions may not be true. Have the API calculate and tell you the destination buffer size, by passing 0 for cchWideChar.
Your string conversion routine ignores all return values, making bugs ever so likely, and unnecessarily hard to diagnose.
I know this is an old post, but for anyone who stumbles here needing more help:
CopyFile has the following constraints which if not met can give access denied error:
Insufficient permissions for the current user
File is in use
Filepath is a directory and not a file
File is read-only
In my case all the above were met, still I kept getting the same error. What helped me was a simple
SetFileAttributes(filePath,FILE_ATTRIBUTE_NORMAL)
Retrieving and Changing File Attributes
SetFileAttributes

boost filesystem copy_file "successful" but no files copied

im having trouble figuring out why my files wont copy. Here's a brief portion of the code:
(dir_itr is directory_iterator & root is a path)
if (!(is_directory(dir_itr->path())))
{
cout << "copying: " << dir_itr->path().filename() << endl;
try
{
copy(dir_itr->path(), root);
remove(dir_itr->path());
} catch (filesystem_error& ex) {
//more code
The results are as follows in the command window:
boost::filesystem::copy_file: The operation completed successfully:
"C:\Documents and Settings\R\Desktop\New Folder\New Folder (2)\New Bitmap Image 3.bmp",
"C:\Documents and Settings\R\Desktop\New Folder"
However no files are copied over.
I am basically just trying to move said file from folder c:\x\y\file.file to c:\x
I'm assuming why i cant move it is because i need a full file name and not just a directory or something? If this is the case, how do i convert path root to string so i can add a file name to it? (im gettin a thousand errors if i even try, they're so long i cant scroll all the way back up the window to see where it starts)
Perhaps boost::filesystem::system_complete can help:
(Sorry, I'm on my Mac and not windows but it shows a way to get the absolute path from a relative path). Good luck.
#include <iostream>
#include <boost/filesystem.hpp>
using namespace std;
int main(int argc, char *argv[]) {
boost::filesystem::path cwd(".");
boost::filesystem::path resolved = boost::filesystem::system_complete(cwd);
std::cout << cwd << std::endl;
std::cout << resolved << std::endl;
}
Outputs:
"."
"/private/var/folders/qw/x23nm9f11fxc45rgddb04n_w0000gn/T/CodeRunner/."
Got back to working on this and I added/changed the following:
try
{
string temp = root.string() + "\\" + dir_itr->path().filename().string();
path p(temp);
copy(dir_itr->path(), p);
remove(dir_itr->path());
//more code
And it seemed to work. I guess my assumption of needing to include the file name when copying was correct.

Capturing cout in Visual Studio 2005 output window?

I created a C++ console app and just want to capture the cout/cerr statements in the Output Window within the Visual Studio 2005 IDE. I'm sure this is just a setting that I'm missing. Can anyone point me in the right direction?
I've finally implemented this, so I want to share it with you:
#include <vector>
#include <iostream>
#include <windows.h>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/tee.hpp>
using namespace std;
namespace io = boost::iostreams;
struct DebugSink
{
typedef char char_type;
typedef io::sink_tag category;
std::vector<char> _vec;
std::streamsize write(const char *s, std::streamsize n)
{
_vec.assign(s, s+n);
_vec.push_back(0); // we must null-terminate for WINAPI
OutputDebugStringA(&_vec[0]);
return n;
}
};
int main()
{
typedef io::tee_device<DebugSink, std::streambuf> TeeDevice;
TeeDevice device(DebugSink(), *cout.rdbuf());
io::stream_buffer<TeeDevice> buf(device);
cout.rdbuf(&buf);
cout << "hello world!\n";
cout.flush(); // you may need to flush in some circumstances
}
BONUS TIP: If you write:
X:\full\file\name.txt(10) : message
to the output window and then double-click on it, then Visual Studio will jump to the given file, line 10, and display the 'message' in status bar. It's very useful.
You can capture the output of cout like this, for example:
std::streambuf* old_rdbuf = std::cout.rdbuf();
std::stringbuf new_rdbuf;
// replace default output buffer with string buffer
std::cout.rdbuf(&new_rdbuf);
// write to new buffer, make sure to flush at the end
std::cout << "hello, world" << std::endl;
std::string s(new_rdbuf.str());
// restore the default buffer before destroying the new one
std::cout.rdbuf(old_rdbuf);
// show that the data actually went somewhere
std::cout << s.size() << ": " << s;
Magicking it into the Visual Studio 2005 output window is left as an exercise to a Visual Studio 2005 plugin developer. But you could probably redirect it elsewhere, like a file or a custom window, perhaps by writing a custom streambuf class (see also boost.iostream).
You can't do this.
If you want to output to the debugger's output window, call OutputDebugString.
I found this implementation of a 'teestream' which allows one output to go to multiple streams. You could implement a stream that sends data to OutputDebugString.
A combination of ben's answer and Mike Dimmick's: you would be implementing a stream_buf_ that ends up calling OutputDebugString. Maybe someone has done this already? Take a look at the two proposed Boost logging libraries.
Is this a case of the output screen just flashing and then dissapearing? if so you can keep it open by using cin as your last statement before return.
Also, depending on your intentions, and what libraries you are using, you may want to use the TRACE macro (MFC) or ATLTRACE (ATL).
Write to a std::ostringsteam and then TRACE that.
std::ostringstream oss;
oss << "w:=" << w << " u=" << u << " vt=" << vt << endl;
TRACE(oss.str().data());