📄 shellpath.c
字号:
if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
return E_INVALIDARG;
if (!pszPath)
return E_INVALIDARG;
/* Try special cases first */
hr = E_FAIL;
switch (folder)
{
case CSIDL_PERSONAL:
case CSIDL_MYMUSIC:
case CSIDL_MYPICTURES:
case CSIDL_MYVIDEO:
{
const char *home = getenv("HOME");
/* special case for "My Documents", map to $HOME */
if (home)
{
WCHAR homeW[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, home, -1, homeW, MAX_PATH);
if (GetFullPathNameW(homeW, MAX_PATH, pszPath, NULL) != 0 &&
PathIsDirectoryW(pszPath))
hr = S_OK;
}
break;
}
case CSIDL_DESKTOP:
case CSIDL_DESKTOPDIRECTORY:
{
const char *home = getenv("HOME");
/* special case for Desktop, map to $HOME/Desktop if it exists */
if (home)
{
WCHAR desktopW[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, home, -1, desktopW, MAX_PATH);
PathAppendW(desktopW, DesktopW);
if (GetFullPathNameW(desktopW, MAX_PATH, pszPath, NULL) != 0 &&
PathIsDirectoryW(pszPath))
hr = S_OK;
}
break;
}
}
if (SUCCEEDED(hr))
return hr;
/* Either the folder was unhandled, or a suitable default wasn't found,
* so use one of the resource-based defaults
*/
if (CSIDL_Data[folder].szDefaultPath &&
IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath))
{
if (LoadStringW(shell32_hInstance,
LOWORD(CSIDL_Data[folder].szDefaultPath), resourcePath, MAX_PATH))
{
hr = S_OK;
pDefaultPath = resourcePath;
}
else
{
FIXME("(%d,%s), LoadString failed, missing translation?\n", folder,
debugstr_w(pszPath));
hr = E_FAIL;
}
}
else
{
hr = S_OK;
pDefaultPath = CSIDL_Data[folder].szDefaultPath;
}
if (SUCCEEDED(hr))
{
switch (CSIDL_Data[folder].type)
{
case CSIDL_Type_User:
strcpyW(pszPath, UserProfileW);
break;
case CSIDL_Type_AllUsers:
strcpyW(pszPath, AllUsersProfileW);
break;
case CSIDL_Type_CurrVer:
strcpyW(pszPath, SystemDriveW);
break;
default:
; /* no corresponding env. var, do nothing */
}
if (pDefaultPath)
{
PathAddBackslashW(pszPath);
strcatW(pszPath, pDefaultPath);
}
}
TRACE("returning 0x%08lx\n", hr);
return hr;
}
/* Gets the (unexpanded) value of the folder with index folder into pszPath.
* The folder's type is assumed to be CSIDL_Type_CurrVer. Its default value
* can be overridden in the HKLM\\szCurrentVersion key.
* If dwFlags has SHGFP_TYPE_DEFAULT set or if the value isn't overridden in
* the registry, uses _SHGetDefaultValue to get the value.
*/
static HRESULT _SHGetCurrentVersionPath(DWORD dwFlags, BYTE folder,
LPWSTR pszPath)
{
HRESULT hr;
TRACE("0x%08lx,0x%02x,%p\n", dwFlags, folder, pszPath);
if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
return E_INVALIDARG;
if (CSIDL_Data[folder].type != CSIDL_Type_CurrVer)
return E_INVALIDARG;
if (!pszPath)
return E_INVALIDARG;
if (dwFlags & SHGFP_TYPE_DEFAULT)
hr = _SHGetDefaultValue(folder, pszPath);
else
{
HKEY hKey;
DWORD dwDisp;
if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, szCurrentVersion, 0,
NULL, 0, KEY_ALL_ACCESS, NULL, &hKey, &dwDisp))
hr = E_FAIL;
else
{
DWORD dwType, dwPathLen = MAX_PATH * sizeof(WCHAR);
if (RegQueryValueExW(hKey, CSIDL_Data[folder].szValueName, NULL,
&dwType, (LPBYTE)pszPath, &dwPathLen) ||
(dwType != REG_SZ && dwType != REG_EXPAND_SZ))
{
hr = _SHGetDefaultValue(folder, pszPath);
dwType = REG_EXPAND_SZ;
RegSetValueExW(hKey, CSIDL_Data[folder].szValueName, 0, dwType,
(LPBYTE)pszPath, (strlenW(pszPath)+1)*sizeof(WCHAR));
}
else
{
pszPath[dwPathLen / sizeof(WCHAR)] = '\0';
hr = S_OK;
}
RegCloseKey(hKey);
}
}
TRACE("returning 0x%08lx (output path is %s)\n", hr, debugstr_w(pszPath));
return hr;
}
/* Gets the user's path (unexpanded) for the CSIDL with index folder:
* If SHGFP_TYPE_DEFAULT is set, calls _SHGetDefaultValue for it. Otherwise
* calls _SHGetUserShellFolderPath for it. Where it looks depends on hToken:
* - if hToken is -1, looks in HKEY_USERS\.Default
* - otherwise looks first in HKEY_CURRENT_USER, followed by HKEY_LOCAL_MACHINE
* if HKEY_CURRENT_USER doesn't contain any entries. If both fail, finally
* calls _SHGetDefaultValue for it.
*/
static HRESULT _SHGetUserProfilePath(HANDLE hToken, DWORD dwFlags, BYTE folder,
LPWSTR pszPath)
{
HRESULT hr;
TRACE("%p,0x%08lx,0x%02x,%p\n", hToken, dwFlags, folder, pszPath);
if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
return E_INVALIDARG;
if (CSIDL_Data[folder].type != CSIDL_Type_User)
return E_INVALIDARG;
if (!pszPath)
return E_INVALIDARG;
/* Only the current user and the default user are supported right now
* I'm afraid.
* FIXME: should be able to call GetTokenInformation on the token,
* then call ConvertSidToStringSidW on it to get the user prefix.
* But Wine's registry doesn't store user info by sid, it stores it
* by user name (and I don't know how to convert from a token to a
* user name).
*/
if (hToken != NULL && hToken != (HANDLE)-1)
{
FIXME("unsupported for user other than current or default\n");
return E_FAIL;
}
if (dwFlags & SHGFP_TYPE_DEFAULT)
hr = _SHGetDefaultValue(folder, pszPath);
else
{
LPCWSTR userPrefix = NULL;
HKEY hRootKey;
if (hToken == (HANDLE)-1)
{
hRootKey = HKEY_USERS;
userPrefix = DefaultW;
}
else /* hToken == NULL, other values disallowed above */
hRootKey = HKEY_CURRENT_USER;
hr = _SHGetUserShellFolderPath(hRootKey, userPrefix,
CSIDL_Data[folder].szValueName, pszPath);
if (FAILED(hr) && hRootKey != HKEY_LOCAL_MACHINE)
hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL,
CSIDL_Data[folder].szValueName, pszPath);
if (FAILED(hr))
hr = _SHGetDefaultValue(folder, pszPath);
}
TRACE("returning 0x%08lx (output path is %s)\n", hr, debugstr_w(pszPath));
return hr;
}
/* Gets the (unexpanded) path for the CSIDL with index folder. If dwFlags has
* SHGFP_TYPE_DEFAULT set, calls _SHGetDefaultValue. Otherwise calls
* _SHGetUserShellFolderPath for it, looking only in HKEY_LOCAL_MACHINE.
* If this fails, falls back to _SHGetDefaultValue.
*/
static HRESULT _SHGetAllUsersProfilePath(DWORD dwFlags, BYTE folder,
LPWSTR pszPath)
{
HRESULT hr;
TRACE("0x%08lx,0x%02x,%p\n", dwFlags, folder, pszPath);
if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
return E_INVALIDARG;
if (CSIDL_Data[folder].type != CSIDL_Type_AllUsers)
return E_INVALIDARG;
if (!pszPath)
return E_INVALIDARG;
if (dwFlags & SHGFP_TYPE_DEFAULT)
hr = _SHGetDefaultValue(folder, pszPath);
else
{
hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL,
CSIDL_Data[folder].szValueName, pszPath);
if (FAILED(hr))
hr = _SHGetDefaultValue(folder, pszPath);
}
TRACE("returning 0x%08lx (output path is %s)\n", hr, debugstr_w(pszPath));
return hr;
}
/* From the original Wine source:
*
* Attempts to expand environment variables from szSrc into szDest, which is
* assumed to be MAX_PATH characters in length. Before referring to the
* environment, handles a few variables directly, because the environment
* variables may not be set when this is called (as during Wine's installation
* when default values are being written to the registry).
* The directly handled environment variables, and their source, are:
* - ALLUSERSPROFILE, USERPROFILE: reads from the registry
* - SystemDrive: uses GetSystemDirectoryW and uses the drive portion of its
* path
* If one of the directly handled environment variables is expanded, only
* expands a single variable, and only in the beginning of szSrc.
*
* That's fine for Wine, but it breaks in ReactOS where we have profile paths
* like "c:\documents and settings\Administrator.REACTOS". Anyway, we have the
* environment variables handy so we'll just use them instead of hacking around
*/
static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest)
{
HRESULT hr = S_OK;
WCHAR szTemp[MAX_PATH];
TRACE("%s, %p\n", debugstr_w(szSrc), szDest);
if (!szSrc || !szDest) return E_INVALIDARG;
/* short-circuit if there's nothing to expand */
if (szSrc[0] != '%')
{
strcpyW(szDest, szSrc);
hr = S_OK;
goto end;
}
*szDest = 0;
strcpyW(szTemp, szSrc);
while (SUCCEEDED(hr) && szTemp[0] == '%')
{
DWORD ret = ExpandEnvironmentStringsW(szSrc, szDest, MAX_PATH);
if (ret > MAX_PATH)
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
else if (ret == 0)
hr = HRESULT_FROM_WIN32(GetLastError());
else
hr = S_OK;
/* By default, terminate loop */
szTemp[0] = '\0';
if (SUCCEEDED(hr) && strcmpW(szSrc, szDest) != 0)
{
/* Continue the loop */
strcpyW(szTemp, szDest);
}
}
end:
TRACE("returning 0x%08lx (input was %s, output is %s)\n", hr,
debugstr_w(szSrc), debugstr_w(szDest));
return hr;
}
/*************************************************************************
* SHGetFolderPathW [SHELL32.@]
*
* Convert nFolder to path.
*
* RETURNS
* Success: S_OK
* Failure: standard HRESULT error codes.
*
* NOTES
* Most values can be overridden in either
* HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
* or in the same location in HKLM.
* The registry usage is explained by the following tech note:
* http://www.microsoft.com/windows2000/techinfo/reskit/en-us/default.asp?url=/windows2000/techinfo/reskit/en-us/regentry/36173.asp
* The "Shell Folders" registry key was used in NT4 and earlier systems.
* Beginning with Windows 2000, the "User Shell Folders" key is used, so
* changes made to it are made to the former key too. This synchronization is
* done on-demand: not until someone requests the value of one of these paths
* (by calling one of the SHGet functions) is the value synchronized.
* Furthermore, as explained here:
* http://www.microsoft.com/windows2000/techinfo/reskit/en-us/default.asp?url=/windows2000/techinfo/reskit/en-us/regentry/36276.asp
* the HKCU paths take precedence over the HKLM paths.
*
*/
HRESULT WINAPI SHGetFolderPathW(
HWND hwndOwner, /* [I] owner window */
int nFolder, /* [I] CSIDL identifying the folder */
HANDLE hToken, /* [I] access token */
DWORD dwFlags, /* [I] which path to return */
LPWSTR pszPath) /* [O] converted path */
{
HRESULT hr;
WCHAR szBuildPath[MAX_PATH], szTemp[MAX_PATH];
DWORD folder = nFolder & CSIDL_FOLDER_MASK;
CSIDL_Type type;
int ret;
TRACE("%p,%p,nFolder=0x%04x\n", hwndOwner,pszPath,nFolder);
/* Windows always NULL-terminates the resulting path regardless of success
* or failure, so do so first
*/
if (pszPath)
*pszPath = '\0';
if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
return E_INVALIDARG;
szTemp[0] = 0;
type = CSIDL_Data[folder].type;
switch (type)
{
case CSIDL_Type_Disallowed:
hr = E_INVALIDARG;
break;
case CSIDL_Type_NonExistent:
hr = S_FALSE;
break;
case CSIDL_Type_WindowsPath:
GetWindowsDirectoryW(szTemp, MAX_PATH);
if (CSIDL_Data[folder].szDefaultPath &&
!IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
*CSIDL_Data[folder].szDefaultPath)
{
PathAddBackslashW(szTemp);
strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
}
hr = S_OK;
break;
case CSIDL_Type_SystemPath:
GetSystemDirectoryW(szTemp, MAX_PATH);
if (CSIDL_Data[folder].szDefaultPath &&
!IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
*CSIDL_Data[folder].szDefaultPath)
{
PathAddBackslashW(szTemp);
strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
}
hr = S_OK;
break;
case CSIDL_Type_CurrVer:
hr = _SHGetCurrentVersionPath(dwFlags, folder, szTemp);
break;
case CSIDL_Type_User:
hr = _SHGetUserProfilePath(hToken, dwFlags, folder, szTemp);
break;
case CSIDL_Type_AllUsers:
hr = _SHGetAllUsersProfilePath(dwFlags, folder, szTemp);
break;
default:
FIXME("bogus type %d, please fix\n", type);
hr = E_INVALIDARG;
break;
}
/* Expand environment strings if necessary */
if (*szTemp == '%')
hr = _SHExpandEnvironmentStrings(szTemp, szBuildPath);
else
strcpyW(szBuildPath, szTemp);
/* Copy the path if it's available before we might return */
if (SUCCEEDED(hr) && pszPath)
strcpyW(pszPath, szBuildPath);
if (FAILED(hr)) goto end;
/* if we don't care about existing directories we are ready */
if(nFolder & CSIDL_FLAG_DONT_VERIFY) goto end;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -