Move a file or folder to the RecycleBin/Trash (C++17) - c++

I am trying to write function to move files to trash.
For example when I use a file path with unicode and whitespace I cannot send it to the Recycle Bin.
...\Yönü Değiştir\Yönü Değiştir Sil.txt
I found many examples on the forum.
But I couldn't run it correctly.
Where did I go wrong,
Can you help me write the function correctly?
My function and code is like this:
. includes...
.
.
bool recycle_file_folder(std::string path) {
std::wstring widestr = std::wstring(path.begin(), path.end());
const wchar_t* widecstr = widestr.c_str();
SHFILEOPSTRUCT fileOp; //#include <Windows.h>;
fileOp.hwnd = NULL;
fileOp.wFunc = FO_DELETE;
fileOp.pFrom = widecstr; /// L"C:\\Users\\USER000\\Documents\\Yönü Değiştir\\Yönü Değiştir Sil.txt";
fileOp.pTo = NULL;
fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOERRORUI | FOF_NOCONFIRMATION | FOF_SILENT;
int result = SHFileOperation(&fileOp);
if (result != 0) {
return false;
}
else {
return true;
}
}
int main()
{
std::filesystem::path p("C:\\Users\\USER000\\Documents\\Yönü Değiştir\\Yönü Değiştir Sil.txt");
recycle_file_folder(p.string());
return 0;
}
Now it works successfully when you specify the file like this:
fileOp.pFrom = L"C:\\Users\\USER000\\Documents\\Yönü Değiştir\\Yönü Değiştir Sil.txt";
How do I adapt this to function for all files?

I think your conversion between wstring and string has problem. Note that std::filesystem supports converting to both string and wstring so let's re-write your code a bit
bool recycle_file_folder(std::wstring path) {
std::wstring widestr = path + std::wstring(1, L'\0');
SHFILEOPSTRUCT fileOp;
fileOp.hwnd = NULL;
fileOp.wFunc = FO_DELETE;
fileOp.pFrom = widestr.c_str();
fileOp.pTo = NULL;
fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOERRORUI | FOF_NOCONFIRMATION | FOF_SILENT;
int result = SHFileOperation(&fileOp);
if (result != 0) {
return false;
}
else {
return true;
}
}
int main()
{
std::filesystem::path p("C:\\Users\\USER000\\Documents\\Yönü Değiştir\\Yönü Değiştir Sil.txt");
recycle_file_folder(p.wstring());
return 0;
}

a file path with unicode and whitespace
The problem is not in whitespace, it is with non-ASCII characters.
std::wstring widestr = std::wstring(path.begin(), path.end());
This is not a correct way to convert characters of some code page to UTF-16.
You'll have to use a method suggested in this Q&A: C++ Convert string (or char*) to wstring (or wchar_t*) (Ignore the answer by Pietro M, look into other answers)
Alternately, use SHFileOperationA, and SHFILEOPSTRUCTA, but it is a worse solution.

Related

How to set UTF-8 encoding without BOM for CStdioFileEx?

I try to read a file via MFC:
CString string;
CStdioFileEx gameFile;
bool have_file = false;
if (PathFileExists(filePathsAndNames[i].first + L"\\main.lua"))
{
gameFile.Open(filePathsAndNames[i].first + L"\\main.lua", CFile::modeRead);
have_file = true;
}
else if (PathFileExists(filePathsAndNames[i].first + L"\\main3.lua"))
{
gameFile.Open(filePathsAndNames[i].first + L"\\main3.lua", CFile::modeRead);
have_file = true;
}
if (have_file)
{
gameFile.SetCodePage(CP_UTF8);
CString game_name;
CString game_name_en;
CString game_author;
CString game_version;
const int MAX_STR_CNT = 5; //не больше этого количества строк от начала
int curr_str = 0;
while (gameFile.ReadString(string)) {
...
}
}
When a file has UTF-8 encoding without a BOM, the readString() method skips near two, three characters in the first line. When the file has UTF-8 encoding with a BOM, all is ok.
How can I fix it?
Is it an issue which I should report to Microsoft? If yes, how can I do it?

Win32 API Copy pictures files from a directory to new/existing directory

I want to copy all pictures file (like .jpeg, .bmp .gif) from "C:\" to a new/existing directory.
How can i write this? Something like (*.jpg|.bmp|.gif ) ???
Here is my code:
HANDLE hFile;
WIN32_FIND_DATA FindFileData;
char *strTheNameOfTheFile=new char[MAX_PATH];
char folder[100]="D:\\myPictures\\";
char tempFolder[100]="D:\\myPictures\\";
SetCurrentDirectory("D:\\");
hFile=FindFirstFile("*.jpg",&FindFileData);// Here is my problem | *.jpeg | *.bmp | *.gif
strTheNameOfTheFile = FindFileData.cFileName;
strcat(folder,strTheNameOfTheFile);
CopyFileEx(strTheNameOfTheFile,folder,NULL,NULL,NULL,NULL);
MessageBox(hDlg,strTheNameOfTheFile,"Oh",MB_OK);
strTheNameOfTheFile=NULL;
folder[0]='\0';
strcpy(folder,tempFolder);
while(FindNextFile(hFile,&FindFileData))
{
strTheNameOfTheFile = FindFileData.cFileName;
strcat(folder,strTheNameOfTheFile);
CopyFileEx(strTheNameOfTheFile,folder,NULL,NULL,NULL,NULL);
strTheNameOfTheFile=NULL;
folder[0]='\0';
strcpy(folder,tempFolder);
}
MessageBox(hDlg,"Done!","Finished",MB_OK);
FindClose(hFile);
Option 1: Put your code into a function (e.g., CopyMyFiles) that takes the extension as a parameter, and then you call that function once for each extension.
(Your code is a bit buggy, so you'll have to fix that up first.)
void CopyMyFiles(const char *pattern, const char *from, const char *to) {
const auto wild = std::string(from) + pattern;
WIN32_FIND_DATAA find_data;
HANDLE hfind = ::FindFirstFileA(wild.c_str(), &find_data);
if (hfind != INVALID_HANDLE_VALUE) {
do {
const auto source = std::string(from) + find_data.cFileName;
const auto target = std::string(to) + find_data.cFileName;
::CopyFileExA(source.c_str(), target.c_str(),
nullptr, nullptr, nullptr, 0);
} while (::FindNextFileA(hfind, &find_data));
::FindClose(hfind);
}
}
Then you can call this once for each pattern (extension).
const char *patterns[] = {"*.jpg", "*.gif", "*.bmp" };
const char *from = "D:\\";
const char *to = "D:\\pictures\\";
for (const auto &pattern : patterns)
CopyMyFiles(pattern, from, to);
Option 2: Enumerate all the files (using that pattern "*") and copy just the ones that match one of your extensions.

SHFileOperation/SHFILEOPSTRUCT

Im trying to copy a directory to a new location. So I am using SHFileOperation/SHFILEOPSTRUCT as follows:
SHFILEOPSTRUCT sf;
memset(&sf,0,sizeof(sf));
sf.hwnd = 0;
sf.wFunc = FO_COPY;
dirName += "\\*.*";
sf.pFrom = dirName.c_str();
string copyDir = homeDir + "\\CopyDir";
sf.pTo = copyDir.c_str();
sf.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI;
int n = SHFileOperation(&sf);
if(n != 0)
{
int x = 0;
}
So I set the values as above. There is a file I created in the folder (I have closed the Handle so it should be fine to move). The SHFileOperation call is returning 2, but I cant find anywhere where these error codes are explained. Does anyone know where I can find out what 2 means, or does anyone have any ideas why it might not be working? Cheers
Error code 2 means The system cannot find the file specified.
See Windows System Error Codes for full listing of error descriptions, or write a function that will obtain the description for the error code:
std::string error_to_string(const DWORD a_error_code)
{
// Get the last windows error message.
char msg_buf[1025] = { 0 };
// Get the error message for our os code.
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
0,
a_error_code,
0,
msg_buf,
sizeof(msg_buf) - 1,
0))
{
// Remove trailing newline character.
char* nl_ptr = 0;
if (0 != (nl_ptr = strchr(msg_buf, '\n')))
{
*nl_ptr = '\0';
}
if (0 != (nl_ptr = strchr(msg_buf, '\r')))
{
*nl_ptr = '\0';
}
return std::string(msg_buf);
}
return std::string("Failed to get error message");
}
From reading the documentation for SHFileOperation the strings specified for pTo and pFrom must be double null terminated: yours are only singly null terminated. Try the following:
dirName.append(1, '\0');
sf.pFrom = dirName.c_str();
string copyDir = homeDir + "\\CopyDir";
copyDir.append(1, '\0');
sf.pTo = copyDir.c_str();

How to get the stem of a filename from a path?

I want to extract a const char* filename from a const char* filepath. I tried with regex but failed:
const char* currentLoadedFile = "D:\files\file.lua";
char fileName[256];
if (sscanf(currentLoadedFile, "%*[^\\]\\%[^.].lua", fileName)) {
return (const char*)fileName; // WILL RETURN "D:\files\file!!
}
The issue is that "D:\files\file" will be returned and not the wanted "file"(note: without ".lua")
What about using std::string?
e.g.
std::string path("d:\\dir\\subdir\\file.ext");
std::string filename;
size_t pos = path.find_last_of("\\");
if(pos != std::string::npos)
filename.assign(path.begin() + pos + 1, path.end());
else
filename = path;
Just use boost::filesystem.
#include <boost/filesystem.hpp>
std::string filename_noext;
filename_noext = boost::filesystem::path("D:\\files\\file.lua").stem().string().
const char* result_as_const_char = filename_noext.c_str();
or alternatively, if you want to introduce bugs yourself :
// have fun defining that to the separator of the target OS.
#define PLATFORM_DIRECTORY_SEPARATOR '\\'
// the following code is guaranteed to have bugs.
std::string input = "D:\\files\\file.lua";
std::string::size_type filename_begin = input.find_last_of(PLATFORM_DIRECTORY_SEPERATOR);
if (filename_begin == std::string::npos)
filename_begin = 0;
else
filename_begin++;
std::string::size_type filename_length = input.find_last_of('.');
if (filename_length != std::string::npos)
filename_length = filename_length - filename_begin;
std::string result = input.substr(filename_begin, filename_length);
const char* bugy_result_as_const_char = result.c_str();
You can do this portably and easily using the new filesystem library in C++17.
#include <cstdint>
#include <cstdio>
#include <filesystem>
int main()
{
std::filesystem::path my_path("D:/files/file.lua");
std::printf("filename: %s\n", my_path.filename().u8string().c_str());
std::printf("stem: %s\n", my_path.stem().u8string().c_str());
std::printf("extension: %s\n", my_path.extension().u8string().c_str());
}
Output:
filename: file.lua
stem: file
extension: .lua
Do note that for the time being you may need to use #include <experimental/fileystem> along with std::experimental::filesystem instead until standard libraries are fully conforming.
For more documentation on std::filesystem check out the filesystem library reference.
You can easily extract the file:
int main()
{
char pscL_Dir[]="/home/srfuser/kush/folder/kushvendra.txt";
char pscL_FileName[50];
char pscL_FilePath[100];
char *pscL;
pscL=strrchr(pscL_Dir,'/');
if(pscL==NULL)
printf("\n ERROR :INvalid DIr");
else
{
strncpy(pscL_FilePath,pscL_Dir,(pscL-pscL_Dir));
strcpy(pscL_FileName,pscL+1);
printf("LENTH [%d}\n pscL_FilePath[%s]\n pscL_FileName[%s]",(pscL-pscL_Dir),pscL_FilePath,pscL_FileName);
}
return 0;
}
output:
LENTH [25}
pscL_FilePath[/home/srfuser/kush/folder]
pscL_FileName[kushvendra.txt
Here you can find an example. I'm not saying it's the best and I'm sure you could improve on that but it uses only standard C++ (anyway at least what's now considered standard).
Of course you won't have the features of the boost::filesystem (those functions in the example play along with plain strings and do not guarantee/check you'll actually working with a real filesystem path).
// Set short name:
char *Filename;
Filename = strrchr(svFilename, '\\');
if ( Filename == NULL )
Filename = svFilename;
if ( Filename[0] == '\\')
++Filename;
if ( !lstrlen(Filename) )
{
Filename = svFilename;
}
fprintf( m_FileOutput, ";\n; %s\n;\n", Filename );
You could use the _splitpath_s function to break a path name into its components. I don't know if this is standard C or is Windows specific. Anyway this is the function:
#include <stdlib.h>
#include <string>
using std::string;
bool splitPath(string const &path, string &drive, string &directory, string &filename, string &extension) {
// validate path
drive.resize(_MAX_DRIVE);
directory.resize(_MAX_DIR);
filename.resize(_MAX_FNAME);
extension.resize(_MAX_EXT);
errno_t result;
result = _splitpath_s(path.c_str(), &drive[0], drive.size(), &directory[0], directory.size(), &filename[0], filename.size(), &extension[0], extension.size());
//_splitpath(path.c_str(), &drive[0], &directory[0], &filename[0], &extension[0]); //WindowsXp compatibility
_get_errno(&result);
if (result != 0) {
return false;
} else {
//delete the blank spaces at the end
drive = drive.c_str();
directory = directory.c_str();
filename = filename.c_str();
extension = extension.c_str();
return true;
}
}
It is a lot easier and safe to use std::string but you could modify this to use TCHAR* (wchar, char)...
For your specific case:
int main(int argc, char *argv[]) {
string path = argv[0];
string drive, directory, filename, extension;
splitPath(path, drive, directory, filename, extension);
printf("FILE = %s%s", filename.c_str(), extension.c_str());
return 0;
}
If you are going to display a filename to the user on Windows you should respect their shell settings (show/hide extension etc).
You can get a filename in the correct format by calling SHGetFileInfo with the SHGFI_DISPLAYNAME flag.

C++ equivalent of MATLAB's "fileparts" function

In MATLAB there's a nice function called fileparts that takes a full file path and parses it into path, filename (without extension), and extension as in the following example from the documentation:
file = 'H:\user4\matlab\classpath.txt';
[pathstr, name, ext] = fileparts(file)
>> pathstr = H:\user4\matlab
>> name = classpath
>> ext = .txt
So I was wondering if there's an equivalent function in any standard C++ or C libraries that I could use? Or would I have to implement this myself? I realize it's fairly simple, but I figured if there's already something pre-made that would be preferable.
Thanks.
The boost library has a file system component "basic_path" that allows you use iterators to discover each component in the filename. Such a component would be OS specific, and I believe you need to compile boost separately for Windows, Linux etc.
I just wrote this simple function. It behaves similar as Matlab's fileparts and works independent of platform.
struct FileParts
{
string path;
string name;
string ext;
};
FileParts fileparts(string filename)
{
int idx0 = filename.rfind("/");
int idx1 = filename.rfind(".");
FileParts fp;
fp.path = filename.substr(0,idx0+1);
fp.name = filename.substr(idx0+1,idx1-idx0-1);
fp.ext = filename.substr(idx1);
return fp;
}
A platform-independent way with C++11/14.
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
void fileparts(string full, string& fpath, string& fname, string& fext)
{
auto source = fs::path(full);
fpath = source.parent_path().string();
fname = source.stem().string();
fext = source.extension().string();
}
...
string fpath, fname, fext;
fileparts(full_file_path,fpath,fname,fext);
Some possible solutions, depending on your OS:
Visual C++ _splitpath function
Win32 Shell Path Handling Functions such as PathFindExtension, PathFindFileName, PathStripPath, PathRemoveExtension, PathRemoveFileSpec
Ekalic's text-only approach is useful, but it didn't check for errors. Here's one that does, and also works with both / and \
struct FileParts
{
std::string path; //!< containing folder, if provided, including trailing slash
std::string name; //!< base file name, without extension
std::string ext; //!< extension, including '.'
};
//! Using only text manipulation, splits a full path into component file parts
FileParts fileparts(const std::string &fullpath)
{
using namespace std;
size_t idxSlash = fullpath.rfind("/");
if (idxSlash == string::npos) {
idxSlash = fullpath.rfind("\\");
}
size_t idxDot = fullpath.rfind(".");
FileParts fp;
if (idxSlash != string::npos && idxDot != string::npos) {
fp.path = fullpath.substr(0, idxSlash + 1);
fp.name = fullpath.substr(idxSlash + 1, idxDot - idxSlash - 1);
fp.ext = fullpath.substr(idxDot);
} else if (idxSlash == string::npos && idxDot == string::npos) {
fp.name = fullpath;
} else if (/* only */ idxSlash == string::npos) {
fp.name = fullpath.substr(0, idxDot);
fp.ext = fullpath.substr(idxDot);
} else { // only idxDot == string::npos
fp.path = fullpath.substr(0, idxSlash + 1);
fp.name = fullpath.substr(idxSlash + 1);
}
return fp;
}