3

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!

guitar-
  • 1,051
  • 5
  • 15
  • 21
  • 1
    Not really related to the question, but `SHGetSpecialFolderLocation`/`SHGetSpecialFolderPath` is obsolete. Microsoft suggests `SHGetKnownFolderPath` (http://msdn.microsoft.com/en-us/library/bb762188.aspx), or, if you want to stay compatible with Windows XP, `SHGetFolderPath` (http://msdn.microsoft.com/en-us/library/bb762181.aspx). – Philipp Jun 29 '10 at 06:56

5 Answers5

6

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
  • 278,309
  • 50
  • 514
  • 539
  • 1
    +1. one could also use std::vector for this and use &v[0], but when it's easy enough to allocate the char buffer on the stack like this with a MAX_PATH we can work with, it's both the simplest and fastest solution. – stinky472 Jun 29 '10 at 01:47
  • @Stinky: You can just do &wstrPath[0] as well. No reason you're forced to use a vector over a string here. – Billy ONeal Jun 29 '10 at 01:48
  • @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. – stinky472 Jun 29 '10 at 05:50
  • @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. – stinky472 Jun 29 '10 at 05:56
1

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
  • 104,103
  • 58
  • 317
  • 552
1

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
  • 11,287
  • 1
  • 28
  • 22
  • 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 Jun 29 '10 at 13:14
  • So does empty braces `{}` for the same reason. (Resurrecting a very old thread, I know, feel free to ignore :) ) – Billy ONeal Dec 15 '14 at 18:10
1

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]);
Community
  • 1
  • 1
Sergey
  • 627
  • 1
  • 9
  • 16
  • This is well documented in an MSDN Magazine article [C++ - Using STL Strings at Win32 API Boundaries](https://msdn.microsoft.com/en-us/magazine/mt238407.aspx) under topic **A Shortcut for the Output Case: Working in Place with std::wstring**. A couple other approaches that do not use `std::wstring` are presented as well. This article was peer reviewed by VC++ contributor Stephan T. Lavavej. As of C++11x, the memory within a std::basic_string which holds the data buffer is guaranteed to be contiguous. – MikeOnline Mar 15 '19 at 01:36
0

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
  • 28,667
  • 7
  • 60
  • 71