Using std::filesystem output as LPCWSTR - c++

I'm making a program which recursively lists all files in a certain directory and uploads each file separately to an FTP server using WinINet.
The problem I'm encountering is using filesystem::path::filename in the FtpPutFile() function because a LPCWSTR is needed.
Whats the best and easiest way to convert it (or somehow use it as is)?
std::string path = "C:\\Programs";
for (const auto & entry : std::experimental::filesystem::recursive_directory_iterator(path))
FtpPutFile(hIConnect, entry.path().filename(), entry.path().filename(), FTP_TRANSFER_TYPE_BINARY, 0);
The error I get is:
no suitable conversion function from "const std::experimental::filesystem::v1::path" to "LPCWSTR" exists
EDIT: Here is a solution that worked for me, by following Lightness solution:
std::string path = "C:\\Programs";
for (const auto & entry : std::experimental::filesystem::recursive_directory_iterator(path))
FtpPutFile(hIConnect, entry.path().wstring().c_str(), entry.path().filename().wstring().c_str(), FTP_TRANSFER_TYPE_BINARY, 0);

LPCWSTR is Microsoft's obfuscation of the const wchar_t* type, and filesystem paths conveniently have a wstring() member function. As you may recall, C++ strings give you access to their character buffer, too, via c_str().
So, entry.path().filename().wstring().c_str() is a LPCWSTR you can use (ugh!). Be careful to use that immediately, or store the result of wstring() somewhere for as long as you need the LPCWSTR to survive, because wstring() returns by value and you don't want a dangling pointer.
// Untested, but a logical adaptation of your code
const std::string path = "C:\\Programs";
std::experimental::filesystem::recursive_directory_iterator it(path);
for (const auto& entry : it)
{
const std::wstring filename = entry.path().filename().wstring();
FtpPutFile(
hIConnect,
filename.c_str(),
filename.c_str(),
FTP_TRANSFER_TYPE_BINARY,
0
);
}

Related

C++ CreateDirectory() not working with APPDATA

I want to create a directory inside the %APPDATA% folder. I am using CreateDirectory() for this and it doesn't work. I debugged the code and it seems like the path is correct, but I can't see a new directory in my the APPDATA.
My code for creating dit in appdata:
void setAppDataDir(std::string name)
{
char* path;
size_t len;
_dupenv_s(&path, &len, "APPDATA");
AppDataPath = path;
AppDataPath += "\\"+name;
createDir(this->AppDataPath.c_str());
}
void createDir(const char* path)
{
assert(CreateDirectory((PCWSTR)path, NULL) || ERROR_ALREADY_EXISTS == GetLastError()); // no exception here
}
This is how I call the function:
setAppDataDir("thisistest");
I use Visual Studio 2019 and the debugger tells me, that path is
C:\\Users\\Micha\AppData\Roaming\\thisistest
What am I doing wrong?
CreateDirectory() is a macro that expands to CreateDirectoryW() in your case, which requires strings in UTF-16LE encoding (wchar_t*). You are casting the const char* path param to PCWSTR (const wchar_t*):
CreateDirectory((PCWSTR)path, NULL) ...
But you are not converting that string into a UTF-16LE string.
So, you need to convert your path into a wchar_t* string. There are some methods to do it, see Convert char * to LPWSTR.
The problem was the way I was giving path to CreateDirectory(). As #RemyLebeau pointed out, I should have used CreateDirectoryA(). This change solved the issue.

Native Dll Call with CStringArray

Consider the following code (Which works!):
int SetInputFile( const CString& fileName );
int SetInputFile(System::String^ fileName)
{
const char* str = (char*)(void*)Marshal::StringToHGlobalAnsi(fileName);
return m_Native->SetInputFile(str);
}
How do I handle/convert a case with this input?
int SetInputFiles( const CStringArray& fileNames );
Thank you Alex Farber and Nostromoo
After searching the web - I was unable to find and such conversion. There for I had the interface changed to receive a list of strings in a const CString& with a seperator character. Naturally your suggestions are as well good and valid solutions.

How can I pass the contents of a tstringstream to a method that takes in an LPTSTR?

I have tried the following:
tstringstream s;
s << _T("test") << std::endl;
LPTSTR message = s.str().c_str();
Log(5, _T("Example", message);
Where Log is defined as such:
void Log(DWORD dwSeverity, LPTSTR szAppID, LPTSTR szMsgString, ...)
{
}
But I get the following error:
Error: A value of type "const char *" cannot be used to initialize an entity of type "LPTSTR"
But I'm not sure exactly how to handle this conversion. In my case, I am compiling a multi-byte character-set application using the MSVC++ compiler. Under these conditions, LPTSTR is defined as an LPSTR, which is defined as a CHAR *.
You're hitting const-incompatibility. For some reason, the function Log takes a pointer to mutable data, which is incompatible with the pointer to const data returned by c_str().
If you have the option, change Log to take its parameter as const (I assume it does not actually modify the string passed in):
void Log(DWORD dwSeverity, LPCTSTR szAppID, LPCTSTR szMsgString, ...)
The same way, declare message as LPCTSTR.
Also note that you cannot actually initialise message the way you're doing it now: the string returned by str() is temporary, so you have to store it:
tstring message = s.str();
Log(5, _T("Example", message.c_str());
If Log is outside your control, but you know that it does not modify its arguments, you can use a const_cast:
LPTSTR message = const_cast<LPTSTR>(message.c_str()); // `message` changed as above
If you cannot vouch for what Log does internally, you'll have to create a mutable buffer:
std::vector<TCHAR> buffer(message.begin(), message.end());
buffer.push_back(0);
Log(5, _T("Example", &buffer[0]);
I can see a couple of problems.
First of all here:
LPTSTR message = s.str().c_str();
The call to s.str() returns a temporary which will be destroyed unless you keep it alive. Therefore the address returned by calling c_str() becomes invalid. You need a temporary local:
string str = s.str();
LPCTSTR message = str.c_str();
And since c_str() returns a const C string, you need to declare message to be a const C string too.
The other problem is that your Log function receives a non-const C string, but c_str() returns a const C string. Presumably the Log function does not need to modify the message, so why are you asking for a modifiable buffer. Change Log to receive a const C string:
void Log(..., LPCTSTR szMsgString, ...)
Finally, since this is C++, why are you using C strings at all? It is better to use C++ strings.

.wav Player : mmioOpen API

I am trying to make an audio player that plays .wav files. I wrote a function ReadWaveFile(CString szFilename) for reading the wave data of the file into the WAVEHDR structure. In this function
BOOL CWavePlay::ReadWaveFile(CString szFilename)
{
hmmio = mmioOpen((LPTSTR)&szFilename,NULL,MMIO_READ);
ASSERT(hmmio); //error here: hmmio=0x00000000
if(hmmio==0)
return FALSE;
....
}
mmioOpen is always returning 0 whenever I pass a filepath to this function for opening the specified file. And what baffles me is when i pass the filepath explicitly in mmioOpen API the code works; i.e., a valid handle is returned.
can some body explain why is this happening??
What will happen when you say
MessageBox(NULL,(LPTSTR)&szFilename,"Foo",MB_ICONINFORMATION);
When passing strings to system functions you will need to pick up the pointer to the raw string. For example, if you want to use an std::string object to build your path you will need to say
mmioOpen(filename.c_str(),NULL,MMIO_READ);
Your cast assumes from CString* to LPTSTR assumes that a CString is binary compatible with a LPTSRT which is not the case. When you write LPCTSTR on szFilename you will invoke a cast operator defined on CStrings that converts it to apropriate format. Did you tried just
hmmio = mmioOpen((LPCTSTR)szFilename,NULL,MMIO_READ);
The last cast does not do anything real here so it should be enough.

Why does the original CString get overwritten when passing a copy to the DrawText function with the DT_MODIFYSTRING option?

I've already found a workaround to this problem, but was just wondering if anyone knew what was actually happening to cause the problem I was seeing. My guess is that it has something to do with mutability of strings, but I thought the CString object accounted for that in the copy constructor.
The following code causes mFileName to be overwritten:
class File {
public:
...
CString GetFilename() {return mFileName;}
private:
CString mFileName;
};
class FileContainer {
private: File* mFile;
public:
FileContainer() {
mFile = new File("C:\temp.txt");
}
GetFilename(CString& fileName) {
fileName = mFile->GetFileName();
}
}
void UpdateText() {
FileContainer fileCnt;
CString filePath(L"");
this->fileCnt.GetFilename(filePath);
...
::DrawText(hDC, filePath, -1, &destRect, DT_PATH_ELLIPSIS | DT_MODIFYSTRING | DT_CALCRECT);
}
What happens is that the first time UpdateText is called, GetFilename returns C:\temp.txt. Assuming that the bounding rect caused the text to be truncated to "...\temp.txt" on the first call, "...\temp.txt" is what is returned from GetFilename on the second call to UpdateText.
Even more perplexing is that this didn't cause mFileName to be changed:
void UpdateText() {
FileContainer fileCnt;
CString filePath(L"");
this->fileCnt->GetFilename(filePath);
filePath = L"TEST";
}
GetFilename always returned C:\temp.txt. So it would seem that the DrawText function is somehow finding the original CString and modifying it. But how?
UPDATE: I figured I'd throw another odd chunk of code that also causes mFileName to be overwritten:
class File {
public:
...
CString GetFilename() {return CString(mFileName);}
private:
CString mFileName;
};
That seems like it should create a new object and return that new object. Yet, somehow, DrawText still overwrites mFileName.
If I change the code to the following, I don't have any issues:
class File {
public:
...
CString GetFilename() {return CString(mFileName.GetBuffer());}
private:
CString mFileName;
};
The only thing that seems to solve the problem is to construct a new CString the way I showed in the workaround. What is DrawText doing when I pass the DT_MODIFYSTRING option?
First, note that CString can be used as a raw string pointer in two ways:
operator LPCSTR - gives a pointer which should never be modified.
GetBuffer - gives a pointer to memory specifically for the purpose of modifying the string.
Now, DrawText is declared to accept a LPCSTR. So when you pass a CString object directly as in your code, it implicitly uses operator LPCSTR to give the function what it says it wants, a constant string pointer.
However, DT_MODIFYSTRING says that DrawText can modify the string it was given. So internally, DrawText must be throwing away the constness of the pointer and modifying the string anyway.
This combination is a bad thing. But the fault is mainly in the implmentation of DrawText which is violating its own declaration.
As for why this modifies other CString objects: Apparently when a CString object is copied, it delays copying the internal string memory until something tries to modify the string through a CString member function. But until that happens, the operator LPCSTR of each CString object would still point to the same shared internal memory. This is normally fine, as long as any code using it is obeying the rules of const-correctness. However, as we've already seen, DrawText with DT_MODIFYSTRING is not playing by the rules. Thus, it is overwriting memory shared by multiple CString objects.
So to fix this problem, you either need to stop using DT_MODIFYSTRING if you don't actually need the modified text. Or else you need to pass the string to DrawText using filePath.GetBuffer() and then call filePath.ReleaseBuffer() afterwards.
Well there are some discrepancies in the code that you posted:
In 'class File':
GetFileName() {return mFileName;}
There is no return type here? Also in the FileContainer class you define the stored 'File' object as a pointer, but in the GetFileName function you access it as if it was not a pointer?
File* mFile;
...
mFile.GetFileName();
As far as why this is happening well right now I can't really tell. Another work around however would be to change the GetFileName function to return a const ref, this should ensure that the returned value can never be changed.
const CString& GetFileName() { return mFileName; }