Neither approach is correct.
SHGetKnownFolderPath()
returns a pointer to a newly allocated string, which you must free with CoTaskMemFree()
when finished using it.
Both code examples are attempting to use operator&
to gain direct access to CString
's internal pointer to its character data, in hopes that the CString
will take ownership of the pointer returned by SHGetKnownFolderPath()
.
This is WRONG and VERY DANGEROUS! For several reasons...
You are assuming that the address of that internal pointer is the same address as the CString
object itself. CString
does not override operator&
to return the address of its internal buffer pointer, like you are assuming it does.
Even if it did, the first code example is still broken because it has a memory leak. You are pre-allocating the character buffer with GetBuffer()
(and not calling ReleaseBuffer()
afterwards). SHGetKnownFolderPath()
will not free that buffer before re-assigning the pointer to point at the returned memory block, so you end up leaking the earlier memory block.
And you are assuming that CString
s destructor uses CoTaskMemFree()
(or compatible function) to free its internal character buffer. Which is not safe to assume.
And you are assuming that the memory layout of the CString
s internal character buffer is compatible with the memory layout of the memory block returned by SHGetKnownFolderPath()
. That is actually NOT the case, as CString
implements a reference counter for its character data (amongst other things). Where do you thing that counter is stored? Right, inside the character buffer itself! Since SHGetKnownFolderPath()
does not return a memory buffer that is compatible with CString
's data format, you are going to corrupt your CString
data and cause problems when it later tries to access things that do not exist in memory. See CString In A Nutshell for more information about `CString's internal details.
In short, CString
is not designed to be used the way you are attempting. You are making some very dangerous assumptions, and thus causing undefined behavior in your code!
You cannot directly access the CString
's internal buffer pointer to reassign it to a different address. The correct solution would look more like this instead:
CString szProgramDataPath;
LPWSTR pProgramDataPath;
if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramData, 0, NULL, &pProgramDataPath)))
{
// DON'T use _T() here! std::cout expects char* strings only...
cout << "Failed, error is: " << GetLastError();
}
else
{
szProgramDataPath = pProgramDataPath;
CoTaskMemFree(pProgramDataPath);
}
If you want to automate the destruction of the returned memory block, especially if the CString
assignment fails with an exception, use a smart pointer like std::unique_ptr
(see std::unique_ptr, deleters and the Win32 API), eg:
CString szProgramDataPath;
LPWSTR pProgramDataPath;
if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramData, 0, NULL, &pProgramDataPath)))
{
// DON'T use _T() here! std::cout expects char* strings only...
cout << "Failed, error is: " << GetLastError();
}
else
{
std::unique_ptr<WCHAR, decltype(CoTaskMemFree)> deleter(pProgramDataPath, &CoTaskMemFree);
szProgramDataPath = pProgramDataPath;
}