views:

109

answers:

5

I'm using the SHGetSpecialFolderLocation API function. My application is set to "Use Unicode Character Set".

Here's what I have so far:

int main ( int, char ** )
{
    LPITEMIDLIST pidl;
    HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl);


    /* Confused at this point */
    wstring wstrPath;

    wstrPath.resize ( _MAX_PATH );
    BOOL f = SHGetPathFromIDList(pidl, wstrPath.c_str () );
    /* End confusion */

The error I'm getting is:

error C2664: 'SHGetPathFromIDListW' : cannot convert parameter 2 from 'const wchar_t *' to 'LPWSTR'

Can someone help? What's the proper C++ way to do this?

Thanks!

+5  A: 

The second parameter is an out parameter, so you can't just pass c_str (which is const) directly. It would probably be simplest just to do:

wchar_t wstrPath[MAX_PATH];
BOOL f = SHGetPathFromIDList(pidl, wstrPath);

MAX_PATH is currently 260 characters.

Matthew Flaschen
Billy ONeal
Thanks a bunch. :) I'm still learning C++.
guitar-
@Billy true but if you can create it on the stack, I say might as well do it. Otherwise we give more fuel to C trolls claiming C++ is inherently slower than C. It's not like you lose RAII benefits unless it needs to be heap-allocated. I don't see vector as a replacement for stack-allocated arrays of fixed sizes or global fixed-size arrays to store in the data segment. Boost.Array is a legitimate alternative.
@Billy sorry I forgot what I originally wrote and misunderstood what you meant. wstring would also work if we're going with a heap-based solution, yes.
+1  A: 

std::basic_string::c_str() returns a constant buffer to it's memory. If you want to modify the string, you'd have to do something like this:

wstring wstrPath;
wstrPath.resize( _MAX_PATH );
BOOL f = SHGetPathFromIDList(pidl, &wstrPath[0]);
wstrPath.erase(
   std::find(wstrPath.begin(), wstrPath.end(), L'\0'), wstrPath.end()
); //Throw away unused buffer space

EDIT: This should also work if you're not afraid of C libraries (though I've not tested it like I've tested the implementation above):

wstring wstrPath;
wstrPath.resize( _MAX_PATH );
BOOL f = SHGetPathFromIDList(pidl, &wstrPath[0]);
wstrPath.resize(wcslen(wstrPath.c_str()));
Billy ONeal
+1  A: 

wstring::c_str() returns const wchar_t* and is read only. LPWSTR is not a const type, and that parameter is an out parameter. You will need to allocate the buffer yourself. You could do something like this:

wchar_t buf[MAX_PATH] = {0};
BOOL f = SHGetPathFromIDList( pidl, buf );
wstring wstrPath = buf;
Aaron Klotz
Why the zero in {}s?
Billy ONeal
It initializes all the elements of the buf array to zero. If the length of the initializer list is smaller than the size of the array, the remaining elements are initialized to zero, so initializing the first element to zero in effect zeros out the entire buf array.
Aaron Klotz
+1  A: 

wstring::c_str() does not let you modify its internal buffer in this way. Your easiest workaround is to create a wchar_t buffer yourself, and the pass that to the wstring constructor:

wchar_t buf[MAX_PATH];
BOOL f = SHGetPathFromIDList(pidl, buf );
wstring wstrPath(buf);
zdan
The only downside here is that you end up copying the buffer twice.
Billy ONeal
A: 

You can get address of 1st array item in basic_string as pointer to writable string data. Although C++ standard does not guarantee that this block of memory must be continuous this is safe in all known implementations (How bad is code using std::basic_string as a contiguous buffer).

std::wstring path(_MAX_PATH, L'\0');
BOOL f = SHGetPathFromIDList(pidl, &path[0]);
Sergey