The SHGetFolderPath()
function is deprecated beginning with Windows Vista: http://msdn.microsoft.com/en-us/library/bb762181%28v=VS.85%29.aspx
What is the alternative way to retrieve the path to the Application Folder in Windows?
SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, 0, szPath)
Aside from that, why do I get those errors when using this function:
Error 1 error C2065: 'CSIDL_COMMON_APPDATA' : undeclared identifier
Error 2 error C3861: 'SHGetFolderPath': identifier not found
The alternative is described in the documentation to which you link. Namely it is SHGetKnownFolderPath
.
However, SHGetKnownFolderPath
is only available on Vista or later. So if you use load time linking, and run a program that calls SHGetKnownFolderPath
on XP, then that program will fail to start. This is clearly a problem if you wish to support XP.
Now, you could switch to run time linking of SHGetKnownFolderPath
. Carry out a version check before you call it, and if the function is not available, then fall back to SHGetFolderPath
.
Personally, I would not let this deprecation worry you excessively. Microsoft are renowned for maintaining backwards compatibility. Don't expect SHGetFolderPath
to disappear any time soon. You will find that SHGetFolderPath
exists in Windows 8 and I would expect it still to be present in whatever Windows is current 10 years from now. My advice is to stick to load time linking, and only switch to SHGetKnownFolderPath
when you give up supporting XP.
Your other question, that you ask in an edit, is how to call SHGetFolderPath
. You need to respect the requirements which are laid out at the bottom of the MSDN documentation topic which you linked to in your question. Specifically, include Shlobj.h
and pass Shlobj.lib
to the linker.
It is linked right at the top, SHGetKnownFolderPath.
CSIDL_COMMON_APPDATA is replaced by FOLDERID_ProgramData in the new API.
I faced the same set of errors when I added few new header files to my already working solution.
I was already calling SHGetFolderPath
and had also included #include <ShlObj.h>
but it was in a different header file. The solution was compiling without any errors before I added new library header files to it.
I tried replacing SHGetFolderPath()
with SHGetKnownFolderPath()
but this just redirected the identifier not found error to SHGetKnownFolderPath
.
On adding #include <ShlObj.h>
to the header file of the class calling SHGetFolderPath
, the
errors ceased and the solution compiled successfully again.
As mentioned in this page, calling SHGetFolderPath
on Windows Vista or a higher OS, will internally call SHGetKnownFolderPath
.
I have tested using the SHGetFolderPath()
with Visual Studio 2015 Enterprise on a Windows 10 PC and it compiled and worked just fine to find the current user's home folder. In the Windows Dev Center page on SHGetFolderPath()
SHGetFolderPath function there is the following note:
Note As of Windows Vista, this function is merely a wrapper for
SHGetKnownFolderPath
. The CSIDL
value is translated to its associated
KNOWNFOLDERID
and then SHGetKnownFolderPath
is called. New
applications should use the known folder system rather than the older
CSIDL
system, which is supported only for backward compatibility.
As David Heffman pointed out in his answer, Microsoft has a history of keeping backwards compatibility for years especially when they can take the older function and just redirect it to the new function with the appropriate arguments. The CSIDL
values seem to have a corresponding KNOWNFOLDERID
value. See this table of the CSIDL
constants with brief annotations and the corresponding KNOWNFOLDERID
value.
An example of the use of the function follows. This use retrieves the current user's user folder (e.g. "C:\Users\myuser\Documents" under Windows 7) and then adds a folder name to the end of the path using the PathAppend()
function.
TCHAR achDevice[MAX_PATH];
HRESULT hr;
// include file ShlObj.h contains list of CSIDL defines however only a subset
// are supported with Windows 7 and later.
// for the 3rd argument, hToken, can be a specified Access Token or SSID for
// a user other than the current user. Using NULL gives us the current user.
if (SUCCEEDED(hr = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, achDevice))) {
// append a folder name to the user's Documents directory.
// the Path Handling functions are pretty handy.
PathAppend(achDevice, L"xxx");
}
One possible failure is one or more invalid arguments (hr == E_INVALIDARG
). A returned value of S_OK
indicates the call succeeded.
There are a few CSIDL
constants that can be used to modify the results of the function such as CSIDL_FLAG_CREATE
by using the bitwise OR operator. I am not sure how well those operators will work with Windows 7 and later.
There are limits on the supported CSIDL
constants with Windows 7 and later. It also looks like there may be possible issues to overcome in complex, remote mounted, redirected, and/or shared folders in an Active Directory or similar environment.
See also KNOWNFOLDERID which includes a table that indicates some of the limitations of CSIDL
and SHGetFolderPath()
. Some examples from the table of CSIDL
constants that may be useful.
CSIDL_LOCAL_APPDATA - %USERPROFILE%\AppData\Local
CSIDL_MYDOCUMENTS - %USERPROFILE%\Document
CSIDL_PERSONAL - %USERPROFILE%\Documents
CSIDL_FONTS - %windir%\Fonts
CSIDL_MYMUSIC - %USERPROFILE%\Music
CSIDL_MYPICTURES - %USERPROFILE%\Pictures
CSIDL_COMMON_APPDATA - %ALLUSERSPROFILE% (%ProgramData%, %SystemDrive%\ProgramData)
CSIDL_COMMON_DOCUMENTS - %PUBLIC%\Documents
By the way, the Shell Path Handling Functions are a nice library of methods for manipulating file paths.
See also Where to put common writable application files?