Error using SHFileOperation() to copy a folder - c++

I am trying to use SHFileOperation() to copy a folder, but get this error:
a value of type "const char *" cannot be assigned to an entity of type "PCZZWSTR"
for both s.pTo and s.pFrom.
The code I'm using is:
SHFILEOPSTRUCT s = { 0 };
s.hwnd = hWnd;
s.wFunc = FO_COPY;
s.fFlags = FOF_SILENT;
s.pTo = "C:\\Users\\styler\\Desktop\\Folder1\0";
s.pFrom = "C:\\Users\\styler\\Desktop\\Software\\Folder2\\Folder3\\*\0";
SHFileOperation(&s);
What am I doing wrong in s.pTo and s.pFrom? I am setting those equal to the target folder and the source folder, but why is this not working?

The compiler is telling you that you are trying to assign char string literals to wchar_t string pointers (PCZZWSTR = CONST WCHAR *). That means you must be compiling with UNICODE defined, where SHFileOperation() maps to SHFileOperationW() which expects wchar_t* string pointers instead of char* string pointers.
So, you need to prefix your string literals with the L prefix, eg:
SHFILEOPSTRUCT s = { 0 };
s.hwnd = hWnd;
s.wFunc = FO_COPY;
s.fFlags = FOF_SILENT;
s.pTo = L"C:\\Users\\styler\\Desktop\\Folder1\0";
s.pFrom = L"C:\\Users\\styler\\Desktop\\Software\\Folder2\\Folder3\\*\0";
SHFileOperation(&s);
Or, since you are actually using the TCHAR version of SHFileOperation(), use the TEXT() macro to match your string literals to the actual character type used by TCHAR:
SHFILEOPSTRUCT s = { 0 };
s.hwnd = hWnd;
s.wFunc = FO_COPY;
s.fFlags = FOF_SILENT;
s.pTo = TEXT("C:\\Users\\styler\\Desktop\\Folder1\0");
s.pFrom = TEXT("C:\\Users\\styler\\Desktop\\Software\\Folder2\\Folder3\\*\0");
SHFileOperation(&s);

Related

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

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.

C++ Builder 10.3 can not assign to const wchar_t* from const char[18]

I have a simple code for directories handling, and here is a part of it. The problem is, that in older version of builder(I guess it is 6) it was working perfectly, now it throws
[bcc32c Error] Unit1.cpp(32): assigning to 'PCZZWSTR' (aka 'const wchar_t *') from incompatible type 'const char [18]'.
void __fastcall TForm1::Button2Click(TObject *Sender)
{
SHFILEOPSTRUCT fos;
String dirDest;
fos.hwnd = Handle;
//operacja kopiowania
fos.wFunc = FO_COPY;
//plik źródłowy
fos.pFrom = "C:\\Melon\\AGA\\Bazy";
}
The problem is with line fos.pFrom = "C:\\Melon\\AGA\\Bazy";. I tried assigning "C:\\Melon\\AGA\\Bazy" to const wchar_t* using linkig, but it thrown me that it can not be linked. Does somebody have a clue how to fix it?
You are using the TCHAR-based version of SHFILEOPSTRUCT, so its string fields will be based on either wchar_t or char depending on whether UNICODE is defined or not, respectively.
In C++Builder 6 (where String was an alias for AnsiString), UNICODE was not defined by default. In C++Builder 2009 onward (where String is an alias for UnicodeString), UNICODE is defined by default, but can be turned off if needed for legacy projects.
Since you are using a TCHAR-based struct, you should use the TCHAR-based TEXT() macro when defining string literals for it, eg:
void __fastcall TForm1::Button2Click(TObject *Sender)
{
SHFILEOPSTRUCT fos;
fos.hwnd = Handle;
//operacja kopiowania
fos.wFunc = FO_COPY;
//plik źródłowy
fos.pFrom = TEXT("C:\\Melon\\AGA\\Bazy\0"); // don't forget the extra null terminator!
fos.pTo = TEXT("...\0");
...
SHFileOperation(&fos);
}
That will work in all C++Builder versions.
On the other hand, if you are trying to use a String variable to define strings for the struct, that will work only if UNICODE is undefined in pre-2009 versions, and defined in post-2009 versions, eg:
void __fastcall TForm1::Button2Click(TObject *Sender)
{
String dirSrc("C:\\Melon\\AGA\\Bazy\0", 18); // don't forget the extra null terminator!
String disDest(...);
SHFILEOPSTRUCT fos;
fos.hwnd = Handle;
//operacja kopiowania
fos.wFunc = FO_COPY;
//plik źródłowy
fos.pFrom = dirSrc.c_str();
fos.pTo = dirDest.c_str();
...
SHFileOperation(&fos);
}
If you don't want to rely on the UNICODE define, then you should use the ANSI or Unicode version of SHFILEOPSTRUCT explicitly, depending on whether you are working with ANSI (char) or Unicode (wchar_t) strings, eg:
void __fastcall TForm1::Button2Click(TObject *Sender)
{
SHFILEOPSTRUCTA fos;
fos.hwnd = Handle;
//operacja kopiowania
fos.wFunc = FO_COPY;
//plik źródłowy
fos.pFrom = "C:\\Melon\\AGA\\Bazy\0"; // don't forget the extra null terminator!
fos.pTo = "...\0";
...
SHFileOperationA(&fos);
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
SHFILEOPSTRUCTW fos;
fos.hwnd = Handle;
//operacja kopiowania
fos.wFunc = FO_COPY;
//plik źródłowy
fos.pFrom = L"C:\\Melon\\AGA\\Bazy\0"; // don't forget the extra null terminator!
fos.pTo = L"...\0";
...
SHFileOperationW(&fos);
}

How to initialize LPWSTR from a const string?

I am trying to initialize MENUITEMINFO for a call to InsertMenuItem. When trying to assign a const string to dwTypeData, I get an error. The code below is from MSDN samples.
I get an error for both types of assignment
mii.dwTypeData = "&Sample text";
mii.dwTypeData = L"&Sample text";
I am using Visual Studio 2019.
MENUITEMINFO mii = { sizeof(mii) };
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_STATE;
mii.wID = idCmdFirst + IDM_DISPLAY;
mii.fType = MFT_STRING;
mii.dwTypeData = L"&Sample Text";
mii.fState = MFS_ENABLED;
if (!InsertMenuItem(hMenu, indexMenu, TRUE, &mii))
{
return HRESULT_FROM_WIN32(GetLastError());
}
The error is
Error (active) E0144 a value of type "const wchar_t *" cannot be used to initialize an entity of type "wchar_t *"
According Microsoft documentation, the second one should work.
https://learn.microsoft.com/en-us/windows/win32/learnwin32/working-with-strings
Edit: This is not solved by I cannot initializate WCHAR because I cannot change the type as suggested in that answer.
Some Windows structs are used to both "Get and Set" and string members in these structs point to mutable strings. This is in direct conflict with the compiler/linker settings that store strings literals in read-only memory.
It is theoretically unsafe to use a string literal with the setter function because it might write to the string (and then restore it back to its original content).
The only known place where this happens is the command line parameter in CreateProcessW.
In all other places you can probably just cast away const:
MENUITEMINFO mii = { sizeof(mii) };
mii.dwTypeData = const_cast<LPTSTR>(TEXT("&Sample Text"));
Be careful! As Lightness Races in Orbit points out, modifying the data you pass could be a problem down the road. Try this, instead:
MENUITEMINFO mii = { sizeof(mii) };
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_STATE;
mii.wID = idCmdFirst + IDM_DISPLAY;
mii.fType = MFT_STRING;
wchar_t text[] = L"&Sample Text";
mii.dwTypeData = text;
mii.fState = MFS_ENABLED;
This way, you should be a wee bit safer - but not completely!! As RbMm points out, it is far better as a general rule to have the dwTypeData member pointing to a static character array. Somewhere (outside) the function …
static thread_local wchar_t menuText[MAXTEXTLEN];
Then, set up mii with …
wcscpy(menuText, L"&Sample Text");
mii.dwTypeData = menuText;
mii.cch = MAXTEXTLEN; // But not used in this case!

Invalid parameter error using GetDefaultCommConfig

I came up with the same issue,in which I got a LPTSTR portname param as input from a function.I have to convert this into wstring,so that I can fetch the Port paramaters.
below is the code snippet in which am trying to copy lptstr to wstring.
void C_PORT_MONITOR::SetPrinterComPortParam(LPTSTR PortName)
{
#ifdef _UNICODE
std::wstring l_ComPortName;
#else
std::string l_ComPortName;
#endif
DWORD dwSize,le = 0;
dwSize = sizeof(COMMCONFIG);
LPCOMMCONFIG lpCC = (LPCOMMCONFIG) new BYTE[dwSize];
l_ComPortName = PortName;//mPortName;
if(l_ComPortName.length() <= 0 )
return;
bool SetFlag = false;
//Get COMM port params called to get size of config. block
int length = l_ComPortName.length();
int iPos = l_ComPortName.find_first_of(':');
int iChc = length- iPos; //remove the charactrers after :
l_ComPortName = l_ComPortName.substr(0, (length- iChc)); //remove the characters from colon //COM1
//Get COMM port params with defined size
BOOL ret = GetDefaultCommConfig(l_ComPortName.c_str(), lpCC, &dwSize);
_RPT1(_CRT_WARN, "C_PORT_MONITOR::SetPrinterComPortParam length=%x,iPos=%x,iChc=%x,l_ComPortName=%s",length, iPos, iChc, l_ComPortName);
if(!ret)
{
le = GetLastError();
_RPT1(_CRT_WARN ,"C_PORT_MONITOR::SetPrinterComPortParam LastError=%x",le);
}
I need to assign this portname to l_comportname. and I need to create a substring from this l_comportname as COM1 and I have to use this substring in getdafaultcommconfig()
Your error is the second parameter not the first. Your debugging statement is bugged because it doesn't account for wide strings %s is for narrow strings only, you should use %S for a wide string.
Here's the real error
dwSize = sizeof(COMMCONFIG);
LPCOMMCONFIG lpCC = (LPCOMMCONFIG) new BYTE[dwSize];
lpCC->dwSize = sizeof(COMMCONFIG); // this line is needed
You might need this as well (the documentation isn't very clear)
lpCC->wVersion = 1;
It's very common in Windows programming that you have to initialize a struct with the size of the struct.
Ref: https://technet.microsoft.com/en-us/aa363188(v=vs.90)

How to get the current Tab Item name from CTabCtrl in MFC?

I am trying to get the text of the currently chosen tab in CTabCtrl.
int tabCurSel = currentTabCtrl->GetCurSel();
TCITEM tcItem;
tcItem.mask = TCIF_TEXT;
tcItem.cchTextMax = 256; //Do I need this?
CString tabCurrentCString;
currentTabCtrl->GetItem(tabCurSel, &tcItem);
tabCurrentCString = tcItem.pszText;
CT2A tabCurrentChar(tabCurrentCString);
std::string tabCurrentStr(tabCurrentChar);
return tabCurrentStr;
I clearly have some unnecessary string conversions and currently this returns a "Error reading characters of the string" in
tcItem.pszText;
How can I get the string from the CTabCtrl? I ultimately am trying to get an std::string but the main question is how to get the text from the tab.
tcItem.pszText is pointing to 0. To fill it with text, it has to point to a buffer before a call is made to GetItem:
Documentation for: CTabCtrl::GetItem
pszText
Pointer to a null-terminated string containing the tab text if the
structure contains information about a tab. If the structure is
receiving information, this member specifies the address of the buffer
that receives the tab text.
Example:
TCITEM tcItem { 0 };
tcItem.mask = TCIF_TEXT;
const int len = 256;
tcItem.cchTextMax = len;
TCHAR buf[len] = { 0 };
tcItem.pszText = buf;
currentTabCtrl->GetItem(tabCurSel, &tcItem);
Both tcItem.pszText and buf will point to the same text. Or use CString with CString::GetBuffer()/CString::ReleaseBuffer()
CString tabCurrentCString;
TCITEM tcItem;
tcItem.mask = TCIF_TEXT;
tcItem.cchTextMax = 256;
tcItem.pszText = tabCurrentCString.GetBuffer(tcItem.cchTextMax);
BOOL result = currentTabCtrl->GetItem(tabCurSel, &tcItem);
tabCurrentCString.ReleaseBuffer();
if (result)
MessageBox(tabCurrentCString); //success
It looks like you are using the recommended Unicode settings. Avoid converting UNICODE to ANSI (std::string). This conversion will work for Latin languages, most of the time, but it's not needed. You can use std::wstring if you need to use that in STL, or convert to UTF-8 if you want to send data to internet etc.
std::string str = CW2A(tabCurrentCString, CP_UTF8);