folder.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 70k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. #include "shellprv.h"
  2. #pragma  hdrstop
  3. #include <userenv.h>
  4. #include "drives.h"
  5. #include "apithk.h"
  6. #include "folder.h"
  7. //---------------------------------------------------------------------------
  8. // Get the path for the CSIDL_ folders  and optionally create it if it
  9. // doesn't exist.
  10. //
  11. // Returns FALSE if the special folder given isn't one of those above or the
  12. // directory couldn't be created.
  13. // By default all the special folders are in the windows directory.
  14. // This can be overidden by a [.Shell Folders] section in win.ini with
  15. // entries like Desktop = c:stuffdesktop
  16. // This in turn can be overidden by a "per user" section in win.ini eg
  17. // [Shell Folder Ianel] - the user name for this section is the current
  18. // network user name, if this fails the default network user name is used
  19. // and if this fails the name given at setup time is used.
  20. //
  21. // "Shell Folders" is the key that records all the absolute paths to the
  22. // shell folders.  The values there are always supposed to be present.
  23. //
  24. // "User Shell Folders" is the key where the user's modifications from
  25. // the defaults are stored.  On Win9x, since we don't use env vars to encode
  26. // the path, If a folder is in the default location, the
  27. // corresponding value is not present under this key. this is to make roaming
  28. // work. on NT we use env vars so this is not an issue. it's only found
  29. // under "Shell Folders".
  30. //
  31. // When we need to find the location of a path, we look in "User Shell Folders"
  32. // first, and if that's not there, generate the default path.  In either
  33. // case we then write the absolute path under "Shell Folders" for other
  34. // apps to look at.  This is so that HKEY_CURRENT_USER can be propagated
  35. // to a machine with Windows installed in a different directory, and as
  36. // long as the user hasn't changed the setting, they won't have the other
  37. // Windows directory hard-coded in the registry.
  38. //   -- gregj, 11/10/94
  39. typedef enum {
  40.     SDIF_CREATE_IN_ROOT         = 0x00000001,   // create in root (not in profiles dir)
  41.     SDIF_CREATE_IN_WINDIR       = 0x00000002,   // create in the windows dir (not in profiles dir)
  42.     SDIF_CREATE_IN_ALLUSERS     = 0x00000003,   // create in "All Users" folder (not in profiles dir)
  43.     SDIF_CREATE_IN_MYDOCUMENTS  = 0x00000004,   // create in CSIDL_PERSONAL folder
  44.     SDIF_CREATE_IN_MASK         = 0x0000000F,   // mask for above values
  45.     SDIF_CAN_DELETE             = 0x00000010,
  46.     SDIF_SHORTCUT_RELATIVE      = 0x00000020,   // make shortcuts relative to this folder
  47.     SDIF_HIDE                   = 0x00000040,   // hide these when we create them
  48.     SDIF_EMPTY_IF_NOT_IN_REG    = 0x00000080,   // does not exist if nothing in the registry
  49.     SDIF_NOT_FILESYS            = 0x00000100,   // not a file system folder
  50.     SDIF_NOT_TRACKED            = 0x00000200,   // don't track this, it can't change
  51.     SDIF_CONST_IDLIST           = 0x00000400,   // don't alloc or free this
  52.     SDIF_REMOVABLE              = 0x00000800,   // Can exist on removable media
  53.     SDIF_CANT_MOVE_RENAME       = 0x00001000,   // can't move or rename this
  54.     SDIF_WX86                   = 0x00002000,   // do Wx86 thunking
  55.     SDIF_NETWORKABLE            = 0x00004000,   // Can be moved to the net
  56. } FOLDER_FLAGS;
  57. typedef struct {
  58.     int id;                     // CSIDL_ value
  59.     int idsDefault;             // string id of default folder name name
  60.     LPCTSTR pszValueName;       // reg key (not localized)
  61.     HKEY hKey;                  // HKCU or HKLM (Current User or Local Machine)
  62.     FOLDER_FLAGS dwFlags;
  63. } FOLDER_INFO;
  64. const FOLDER_INFO c_rgFolderInfo[] = 
  65. {
  66.     { CSIDL_DESKTOP,   IDS_CSIDL_DESKTOPDIRECTORY, TEXT("DesktopFolder"), NULL, SDIF_NOT_TRACKED | SDIF_CONST_IDLIST },
  67.     { CSIDL_NETWORK,   -1, TEXT("NetworkFolder"), NULL, SDIF_NOT_TRACKED | SDIF_NOT_FILESYS | SDIF_CONST_IDLIST },
  68.     { CSIDL_DRIVES,    -1, TEXT("DriveFolder"), NULL, SDIF_NOT_TRACKED | SDIF_NOT_FILESYS | SDIF_CONST_IDLIST },
  69.     { CSIDL_INTERNET,  -1, TEXT("InternetFolder"), NULL, SDIF_NOT_TRACKED | SDIF_NOT_FILESYS | SDIF_CONST_IDLIST },
  70.     { CSIDL_CONTROLS,  -1, TEXT("ControlPanelFolder"), NULL, SDIF_NOT_TRACKED | SDIF_NOT_FILESYS },
  71.     { CSIDL_PRINTERS,  -1, TEXT("PrintersFolder"), NULL, SDIF_NOT_TRACKED | SDIF_NOT_FILESYS } ,
  72.     { CSIDL_BITBUCKET, -1, TEXT("RecycleBinFolder"), NULL, SDIF_NOT_TRACKED | SDIF_NOT_FILESYS },
  73.     { CSIDL_CONNECTIONS, -1, TEXT("ConnectionsFolder"), NULL, SDIF_NOT_TRACKED | SDIF_NOT_FILESYS | SDIF_CONST_IDLIST },
  74.     { CSIDL_FONTS, IDS_CSIDL_FONTS, TEXT("Fonts"), HKEY_CURRENT_USER, SDIF_NOT_TRACKED | SDIF_CREATE_IN_WINDIR | SDIF_CANT_MOVE_RENAME },
  75.     { CSIDL_DESKTOPDIRECTORY, IDS_CSIDL_DESKTOPDIRECTORY, TEXT("Desktop"), HKEY_CURRENT_USER, SDIF_SHORTCUT_RELATIVE },
  76.     // _STARTUP is a subfolder of _PROGRAMS is a subfolder of _STARTMENU -- keep that order
  77.     { CSIDL_STARTUP,    IDS_CSIDL_STARTUP, TEXT("Startup"), HKEY_CURRENT_USER, 0 },
  78.     { CSIDL_PROGRAMS,   IDS_CSIDL_PROGRAMS, TEXT("Programs"), HKEY_CURRENT_USER, 0 },
  79.     { CSIDL_STARTMENU,  IDS_CSIDL_STARTMENU, TEXT("Start Menu"), HKEY_CURRENT_USER, SDIF_SHORTCUT_RELATIVE},
  80.     { CSIDL_RECENT,     IDS_CSIDL_RECENT, TEXT("Recent"), HKEY_CURRENT_USER, SDIF_HIDE | SDIF_CANT_MOVE_RENAME },
  81.     { CSIDL_SENDTO,     IDS_CSIDL_SENDTO, TEXT("SendTo"), HKEY_CURRENT_USER, SDIF_HIDE },
  82.     { CSIDL_PERSONAL,   IDS_CSIDL_PERSONAL, TEXT("Personal"), HKEY_CURRENT_USER, SDIF_SHORTCUT_RELATIVE | SDIF_NETWORKABLE | SDIF_REMOVABLE },
  83.     { CSIDL_FAVORITES,  IDS_CSIDL_FAVORITES, TEXT("Favorites"), HKEY_CURRENT_USER, 0 },
  84.     { CSIDL_NETHOOD,    IDS_CSIDL_NETHOOD, TEXT("NetHood"), HKEY_CURRENT_USER, SDIF_HIDE },
  85.     { CSIDL_PRINTHOOD,  IDS_CSIDL_PRINTHOOD, TEXT("PrintHood"), HKEY_CURRENT_USER, SDIF_HIDE },
  86.     { CSIDL_TEMPLATES,  IDS_CSIDL_TEMPLATES, TEXT("Templates"), HKEY_CURRENT_USER, SDIF_HIDE },
  87.     // Common special folders
  88.     // _STARTUP is a subfolder of _PROGRAMS is a subfolder of _STARTMENU -- keep that order
  89.     { CSIDL_COMMON_STARTUP,  IDS_CSIDL_STARTUP,    TEXT("Common Startup"), HKEY_LOCAL_MACHINE, SDIF_EMPTY_IF_NOT_IN_REG | SDIF_CREATE_IN_ALLUSERS | SDIF_CANT_MOVE_RENAME},
  90.     { CSIDL_COMMON_PROGRAMS,  IDS_CSIDL_PROGRAMS,  TEXT("Common Programs"), HKEY_LOCAL_MACHINE, SDIF_EMPTY_IF_NOT_IN_REG | SDIF_CREATE_IN_ALLUSERS },
  91.     { CSIDL_COMMON_STARTMENU, IDS_CSIDL_STARTMENU, TEXT("Common Start Menu"), HKEY_LOCAL_MACHINE, SDIF_EMPTY_IF_NOT_IN_REG | SDIF_SHORTCUT_RELATIVE | SDIF_CREATE_IN_ALLUSERS },
  92.     { CSIDL_COMMON_DESKTOPDIRECTORY, IDS_CSIDL_DESKTOPDIRECTORY, TEXT("Common Desktop"), HKEY_LOCAL_MACHINE, SDIF_EMPTY_IF_NOT_IN_REG | SDIF_SHORTCUT_RELATIVE | SDIF_CREATE_IN_ALLUSERS },
  93.     { CSIDL_COMMON_FAVORITES, IDS_CSIDL_FAVORITES, TEXT("Common Favorites"), HKEY_LOCAL_MACHINE, SDIF_EMPTY_IF_NOT_IN_REG | SDIF_CREATE_IN_ALLUSERS },
  94.     { CSIDL_COMMON_APPDATA,   IDS_CSIDL_APPDATA,   TEXT("Common AppData"),   HKEY_LOCAL_MACHINE, SDIF_SHORTCUT_RELATIVE | SDIF_CREATE_IN_ALLUSERS },
  95.     { CSIDL_COMMON_TEMPLATES, IDS_CSIDL_TEMPLATES, TEXT("Common Templates"), HKEY_LOCAL_MACHINE, SDIF_NOT_TRACKED | SDIF_CAN_DELETE | SDIF_CREATE_IN_ALLUSERS },
  96.     { CSIDL_COMMON_DOCUMENTS, IDS_CSIDL_ALLUSERS_DOCUMENTS, TEXT("Common Documents"), HKEY_LOCAL_MACHINE, SDIF_NOT_TRACKED | SDIF_CAN_DELETE | SDIF_CREATE_IN_ALLUSERS },
  97.     // Application Data special folder
  98.     { CSIDL_APPDATA, IDS_CSIDL_APPDATA, TEXT("AppData"), HKEY_CURRENT_USER, SDIF_SHORTCUT_RELATIVE },
  99.     { CSIDL_LOCAL_APPDATA, IDS_CSIDL_LOCAL_APPDATA, TEXT("Local AppData"), HKEY_CURRENT_USER, 0 },
  100.     // Non-localized startup folder (do not localize this folde name)
  101.     { CSIDL_ALTSTARTUP, IDS_CSIDL_ALTSTARTUP, TEXT("AltStartup"), HKEY_CURRENT_USER, SDIF_EMPTY_IF_NOT_IN_REG },
  102.     // Non-localized Common StartUp group (do not localize this folde name)
  103.     { CSIDL_COMMON_ALTSTARTUP, IDS_CSIDL_ALTSTARTUP, TEXT("Common AltStartup"), HKEY_LOCAL_MACHINE, SDIF_EMPTY_IF_NOT_IN_REG | SDIF_CREATE_IN_ALLUSERS },
  104.     // Per-user Internet-related folders
  105.     { CSIDL_INTERNET_CACHE, IDS_CSIDL_CACHE, TEXT("Cache"), HKEY_CURRENT_USER, 0 },
  106.     { CSIDL_COOKIES, IDS_CSIDL_COOKIES, TEXT("Cookies"), HKEY_CURRENT_USER, 0 },
  107.     { CSIDL_HISTORY, IDS_CSIDL_HISTORY, TEXT("History"), HKEY_CURRENT_USER, 0 },
  108.     { CSIDL_WINDOWS,                0, TEXT("Windows"), 0, SDIF_NOT_TRACKED | SDIF_SHORTCUT_RELATIVE | SDIF_CANT_MOVE_RENAME },
  109.     { CSIDL_SYSTEM,                 0, TEXT("System"), 0, SDIF_NOT_TRACKED | SDIF_CANT_MOVE_RENAME },
  110.     { CSIDL_PROGRAM_FILES,          0, TEXT("ProgramFiles"), 0, SDIF_NOT_TRACKED | SDIF_CAN_DELETE | SDIF_SHORTCUT_RELATIVE },
  111.     { CSIDL_PROGRAM_FILES_COMMON,   0, TEXT("CommonProgramFiles"), 0, SDIF_NOT_TRACKED | SDIF_CAN_DELETE },
  112.     { CSIDL_PROFILE,                0, TEXT("Profile"), 0, SDIF_NOT_TRACKED | SDIF_CANT_MOVE_RENAME },
  113.     { CSIDL_MYPICTURES, IDS_CSIDL_MYPICTURES, TEXT("My Pictures"), HKEY_CURRENT_USER, SDIF_CAN_DELETE | SDIF_NETWORKABLE | SDIF_REMOVABLE | SDIF_CREATE_IN_MYDOCUMENTS },
  114.     { CSIDL_SYSTEMX86, 0, TEXT("SystemX86"), 0, SDIF_NOT_TRACKED | SDIF_CANT_MOVE_RENAME | SDIF_WX86 },
  115. #ifdef WX86
  116.     { CSIDL_PROGRAM_FILESX86,          0, TEXT("ProgramFilesX86"), 0, SDIF_NOT_TRACKED | SDIF_CAN_DELETE | SDIF_SHORTCUT_RELATIVE|SDIF_WX86},
  117.     { CSIDL_PROGRAM_FILES_COMMONX86,   0, TEXT("CommonProgramFilesX86"), 0, SDIF_NOT_TRACKED | SDIF_CAN_DELETE | SDIF_WX86 },
  118. #else
  119.     { CSIDL_PROGRAM_FILESX86,          0, TEXT("ProgramFilesX86"), 0, SDIF_NOT_TRACKED | SDIF_CAN_DELETE | SDIF_SHORTCUT_RELATIVE},
  120.     { CSIDL_PROGRAM_FILES_COMMONX86,   0, TEXT("CommonProgramFilesX86"), 0, SDIF_NOT_TRACKED | SDIF_CAN_DELETE },
  121. #endif
  122.     { CSIDL_ADMINTOOLS,         IDS_CSIDL_ADMINTOOLS, TEXT("Administrative Tools"), HKEY_CURRENT_USER, 0 },
  123.     { CSIDL_COMMON_ADMINTOOLS,  IDS_CSIDL_ADMINTOOLS, TEXT("Common Administrative Tools"), HKEY_LOCAL_MACHINE, SDIF_CREATE_IN_ALLUSERS },
  124.     { -1, 0, NULL, 0, 0 },
  125. };
  126. // this array holds a cache of the valures of these folders. this cache can only
  127. // be used in the hToken == NULL case otherwise we would need a per user version
  128. // of this cache.
  129. #define SFENTRY(x)  { (LPTSTR)-1, (LPITEMIDLIST)x }
  130. EXTERN_C const IDREGITEM c_aidlConnections[];
  131. struct {
  132.     LPTSTR       psz;
  133.     LPITEMIDLIST pidl;
  134. } g_aFolderCache[] = {
  135.     SFENTRY(&c_idlDesktop),    // CSIDL_DESKTOP                   (0x0000)
  136.     SFENTRY(&c_idlInetRoot),   // CSIDL_INTERNET                  (0x0001)
  137.     SFENTRY(-1),               // CSIDL_PROGRAMS                  (0x0002)
  138.     SFENTRY(-1),               // CSIDL_CONTROLS                  (0x0003)
  139.     SFENTRY(-1),               // CSIDL_PRINTERS                  (0x0004)
  140.     SFENTRY(-1),               // CSIDL_PERSONAL                  (0x0005)
  141.     SFENTRY(-1),               // CSIDL_FAVORITES                 (0x0006)
  142.     SFENTRY(-1),               // CSIDL_STARTUP                   (0x0007)
  143.     SFENTRY(-1),               // CSIDL_RECENT                    (0x0008)
  144.     SFENTRY(-1),               // CSIDL_SENDTO                    (0x0009)
  145.     SFENTRY(-1),               // CSIDL_BITBUCKET                 (0x000a)
  146.     SFENTRY(-1),               // CSIDL_STARTMENU                 (0x000b)
  147.     SFENTRY(-1),               // <unused>                        (0x000c)
  148.     SFENTRY(-1),               // <unused>                        (0x000d)
  149.     SFENTRY(-1),               // <unused>                        (0x000e)
  150.     SFENTRY(-1),               // <unused>                        (0x000f)
  151.     SFENTRY(-1),               // CSIDL_DESKTOPDIRECTORY          (0x0010)
  152.     SFENTRY(&c_idlDrives),     // CSIDL_DRIVES                    (0x0011)
  153.     SFENTRY(&c_idlNet),        // CSIDL_NETWORK                   (0x0012)
  154.     SFENTRY(-1),               // CSIDL_NETHOOD                   (0x0013)
  155.     SFENTRY(-1),               // CSIDL_FONTS                     (0x0014)
  156.     SFENTRY(-1),               // CSIDL_TEMPLATES                 (0x0015)
  157.     SFENTRY(-1),               // CSIDL_COMMON_STARTMENU          (0x0016)
  158.     SFENTRY(-1),               // CSIDL_COMMON_PROGRAMS           (0X0017)
  159.     SFENTRY(-1),               // CSIDL_COMMON_STARTUP            (0x0018)
  160.     SFENTRY(-1),               // CSIDL_COMMON_DESKTOPDIRECTORY   (0x0019)
  161.     SFENTRY(-1),               // CSIDL_APPDATA                   (0x001a)
  162.     SFENTRY(-1),               // CSIDL_PRINTHOOD                 (0x001b)
  163.     SFENTRY(-1),               // CSIDL_LOCAL_APPDATA             (0x001c)
  164.     SFENTRY(-1),               // CSIDL_ALTSTARTUP                (0x001d)
  165.     SFENTRY(-1),               // CSIDL_COMMON_ALTSTARTUP         (0x001e)
  166.     SFENTRY(-1),               // CSIDL_COMMON_FAVORITES          (0x001f)
  167.     SFENTRY(-1),               // CSIDL_INTERNET_CACHE            (0x0020)
  168.     SFENTRY(-1),               // CSIDL_COOKIES                   (0x0021)
  169.     SFENTRY(-1),               // CSIDL_HISTORY                   (0x0022)
  170.     SFENTRY(-1),               // CSIDL_COMMON_APPDATA            (0x0023)
  171.     SFENTRY(-1),               // CSIDL_WINDOWS                   (0x0024)
  172.     SFENTRY(-1),               // CSIDL_SYSTEM                    (0x0025)
  173.     SFENTRY(-1),               // CSIDL_PROGRAM_FILES             (0x0026)
  174.     SFENTRY(-1),               // CSIDL_MYPICTURES                (0x0027)
  175.     SFENTRY(-1),               // CSIDL_PROFILE                   (0x0028)
  176.     SFENTRY(-1),               // CSIDL_SYSTEMX86                 (0x0029)
  177.     SFENTRY(-1),               // CSIDL_PROGRAM_FILESX86          (0x002a)
  178.     SFENTRY(-1),               // CSIDL_PROGRAM_FILES_COMMON      (0x002b)
  179.     SFENTRY(-1),               // CSIDL_PROGRAM_FILES_COMMONX86   (0x002c)
  180.     SFENTRY(-1),               // CSIDL_COMMON_TEMPLATES          (0x002d)
  181.     SFENTRY(-1),               // CSIDL_COMMON_DOCUMENTS          (0x002e)
  182.     SFENTRY(-1),               // CSIDL_COMMON_ADMINTOOLS         (0x002f)
  183.     SFENTRY(-1),               // CSIDL_ADMINTOOLS                (0x0030)
  184.     SFENTRY(c_aidlConnections), // CSIDL_CONNECTIONS              (0x0031)
  185. };
  186. HRESULT _OpenKeyForFolder(const FOLDER_INFO *pfi, HANDLE hToken, LPCTSTR pszSubKey, HKEY *phkey);
  187. void _UpdateShellFolderCache(void);
  188. BOOL GetUserProfileDir(HANDLE hToken, TCHAR *pszPath);
  189. HRESULT VerifyAndCreateFolder(HWND hwnd, const FOLDER_INFO *pfi, UINT uFlags, LPTSTR pszPath) ;
  190. #define _IsDefaultUserToken(hToken)     ((HANDLE)-1 == hToken)
  191. const FOLDER_INFO *_GetFolderInfo(int csidl)
  192. {
  193.     const FOLDER_INFO *pfi;
  194.     // make sure g_aFolderCache can be indexed by the CSIDL values
  195.     COMPILETIME_ASSERT((ARRAYSIZE(g_aFolderCache) - 1) == CSIDL_CONNECTIONS);
  196.     for (pfi = c_rgFolderInfo; pfi->id != -1; pfi++)
  197.     {
  198.         if (pfi->id == csidl)
  199.             return pfi;
  200.     }
  201.     return NULL;
  202. }
  203. // expand an individual enviornment variable
  204. // in:
  205. //      pszVar      "%USERPROFILE%
  206. //      pszValue    "c:winntprofilesuser"
  207. //
  208. // in/out:
  209. //      pszToExpand in: %USERPROFILE%My Docs", out: c:winntprofilesuserMy Docs"
  210. BOOL ExpandEnvVar(LPCTSTR pszVar, LPCTSTR pszValue, LPTSTR pszToExpand)
  211. {
  212.     TCHAR *pszStart = StrStrI(pszToExpand, pszVar);
  213.     if (pszStart)
  214.     {
  215.         TCHAR szAfter[MAX_PATH];
  216.         lstrcpy(szAfter, pszStart + lstrlen(pszVar));   // save the tail
  217.         lstrcpyn(pszStart, pszValue, (int) (MAX_PATH - (pszStart - pszToExpand)));
  218.         StrCatBuff(pszToExpand, szAfter, MAX_PATH);       // put the tail back on
  219.         return TRUE;
  220.     }
  221.     return FALSE;
  222. }
  223. HANDLE GetCurrentUserToken()
  224. {
  225.     HANDLE hToken;
  226.     if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hToken) ||
  227.         OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_IMPERSONATE, &hToken))
  228.         return hToken;
  229.     return NULL;
  230. }
  231. // like ExpandEnvironmentStrings but is robust to the enviornment variables
  232. // not being set. this works on...
  233. // %SYSTEMROOT%
  234. // %SYSTEMDRIVE%
  235. // %USERPROFILE%
  236. // %ALLUSERSPROFILE%
  237. //
  238. // in the rare case (Winstone!) that there is a NULL enviornment block
  239. DWORD ExpandEnvironmentStringsNoEnv(HANDLE hToken, LPCTSTR pszExpand, LPTSTR pszOut, UINT cchOut)
  240. {
  241.     TCHAR szPath[MAX_PATH];
  242. #ifdef WINNT
  243.     if (hToken && !_IsDefaultUserToken(hToken))
  244.     {
  245.         if (!ExpandEnvironmentStringsForUser(hToken, pszExpand, pszOut, cchOut))
  246.             lstrcpyn(pszOut, pszExpand, cchOut);
  247.     }
  248.     else if (hToken == NULL)
  249. #endif
  250.     {
  251.         // to debug env expansion failure...
  252.         // lstrcpyn(pszOut, pszExpand, cchOut);
  253.         SHExpandEnvironmentStrings(pszExpand, pszOut, cchOut);
  254.     }
  255.     // manually expand in this order since 
  256.     //  %USERPROFILE% -> %SYSTEMDRIVE%Docs & Settings
  257. #ifdef WINNT
  258.     if (StrChr(pszOut, TEXT('%')) && (hToken == NULL))
  259.     {
  260.         hToken = GetCurrentUserToken();
  261.         if (hToken)
  262.         {
  263.             // this does %USERPROFILE% and other per user stuff
  264.             ExpandEnvironmentStringsForUser(hToken, pszExpand, pszOut, cchOut);
  265.             CloseHandle(hToken);
  266.         }
  267.     }
  268.     else if (_IsDefaultUserToken(hToken) && StrChr(pszOut, TEXT('%')))
  269.     {
  270.         GetUserProfileDir(hToken, szPath);
  271.         ExpandEnvVar(TEXT("%USERPROFILE%"), szPath, pszOut);
  272.     }
  273. #endif
  274.     if (*pszOut == TEXT('%'))
  275.     {
  276.         GetAllUsersDirectory(szPath);
  277.         ExpandEnvVar(TEXT("%ALLUSERSPROFILE%"), szPath, pszOut);
  278.     }
  279.     if (*pszOut == TEXT('%'))
  280.     {
  281.         GetSystemWindowsDirectory(szPath, ARRAYSIZE(szPath));
  282.         ExpandEnvVar(TEXT("%SYSTEMROOT%"), szPath, pszOut);
  283.     }
  284.     if (*pszOut == TEXT('%'))
  285.     {
  286.         GetSystemWindowsDirectory(szPath, ARRAYSIZE(szPath));
  287.         ASSERT(szPath[1] == TEXT(':')); // this better not be a UNC!
  288.         szPath[2] = TEXT(''); // SYSTEMDRIVE = 'c:', not 'c:'
  289.         ExpandEnvVar(TEXT("%SYSTEMDRIVE%"), szPath, pszOut);
  290.     }
  291.     if (*pszOut == TEXT('%'))
  292.         *pszOut = 0;
  293.     return lstrlen(pszOut) + 1;    // +1 to cover the NULL
  294. }
  295. // get the user profile directory:
  296. // on Win9x: reads from the registry and FAILS if user profiles are not turned on
  297. // on NT: uses the hToken as needed to determine the proper user profile
  298. BOOL GetUserProfileDir(HANDLE hToken, TCHAR *pszPath)
  299. {
  300. #ifdef WINNT
  301.     DWORD dwcch = MAX_PATH;
  302.     HANDLE hClose = NULL;
  303.     BOOL fRet;
  304.     
  305.     *pszPath = 0;       // in case of error
  306.     if ( !hToken )
  307.     {
  308.         hClose = hToken = GetCurrentUserToken();
  309.     }
  310.     if (_IsDefaultUserToken(hToken) && g_bRunOnNT5)
  311.     {
  312.         fRet = GetDefaultUserProfileDirectory( pszPath, &dwcch );
  313.     }
  314.     else
  315.     {
  316.         fRet = GetUserProfileDirectory( hToken, pszPath, &dwcch );
  317.     }
  318.     if ( hClose )
  319.     {
  320.         CloseHandle( hClose );
  321.     }
  322.     return fRet;
  323. #else
  324.     DWORD cbData = MAX_PATH * SIZEOF(TCHAR);
  325.     *pszPath = 0;       // in case of error
  326.     SHGetValue(HKEY_CURRENT_USER, TEXT("Software\Microsoft\Windows\CurrentVersion\ProfileReconciliation"), 
  327.         TEXT("ProfileDirectory"), NULL, pszPath, &cbData);
  328. #endif
  329.     return (BOOL)*pszPath;
  330. }
  331. #ifdef WX86
  332. void SetUseKnownWx86Dll(const FOLDER_INFO *pfi, BOOL bValue)
  333. {
  334.     if (pfi->dwFlags & SDIF_WX86)
  335.     {
  336.         //  GetSystemDirectory() knows we're looking for the Wx86 system
  337.         //  directory when this flag is set.
  338.         NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = bValue ? TRUE : FALSE;
  339.     }
  340. }
  341. #else
  342. #define SetUseKnownWx86Dll(pfi, bValue)
  343. #endif
  344. // read from registry
  345. BOOL GetProgramFiles(LPCTSTR pszValue, LPTSTR pszPath)
  346. {
  347.     DWORD cbPath = MAX_PATH * sizeof(*pszPath);
  348.     *pszPath = 0;
  349.     SHGetValue(HKEY_LOCAL_MACHINE, TEXT("Software\Microsoft\Windows\CurrentVersion"), 
  350.         pszValue, NULL, pszPath, &cbPath);
  351.     return (BOOL)*pszPath;
  352. }
  353. void LoadDefaultString(int idString, LPTSTR lpBuffer, int cchBufferMax)
  354. {
  355. #ifdef WINNT
  356.     BOOL fSucceeded = FALSE;
  357.     HRSRC hResInfo;
  358.     HANDLE hStringSeg;
  359.     LPWSTR lpsz;
  360.     int    cch;
  361.     HMODULE hmod = GetModuleHandle(TEXT("SHELL32"));
  362.     
  363.     // Make sure the parms are valid.     
  364.     if (lpBuffer == NULL || cchBufferMax == 0) 
  365.     {
  366.         return;
  367.     }
  368.     cch = 0;
  369.     
  370.     // String Tables are broken up into 16 string segments.  Find the segment
  371.     // containing the string we are interested in.     
  372.     if (hResInfo = FindResourceExW(hmod, (LPCWSTR)RT_STRING,
  373.                                    (LPWSTR)((LONG)(((USHORT)idString >> 4) + 1)), GetSystemDefaultUILanguage())) 
  374.     {        
  375.         // Load that segment.        
  376.         hStringSeg = LoadResource(hmod, hResInfo);
  377.         
  378.         // Lock the resource.        
  379.         if (lpsz = (LPWSTR)LockResource(hStringSeg)) 
  380.         {            
  381.             // Move past the other strings in this segment.
  382.             // (16 strings in a segment -> & 0x0F)             
  383.             idString &= 0x0F;
  384.             while (TRUE) 
  385.             {
  386.                 cch = *((WORD *)lpsz++);   // PASCAL like string count
  387.                                             // first UTCHAR is count if TCHARs
  388.                 if (idString-- == 0) break;
  389.                 lpsz += cch;                // Step to start if next string
  390.              }
  391.             
  392.                             
  393.             // Account for the NULL                
  394.             cchBufferMax--;
  395.                 
  396.             // Don't copy more than the max allowed.                
  397.             if (cch > cchBufferMax)
  398.                 cch = cchBufferMax;
  399.                 
  400.             // Copy the string into the buffer.                
  401.             CopyMemory(lpBuffer, lpsz, cch*sizeof(WCHAR));
  402.             // Attach Null terminator.
  403.             lpBuffer[cch] = 0;
  404.             fSucceeded = TRUE;
  405.         }
  406.     }
  407.     if (!fSucceeded)
  408. #endif
  409.     {
  410.         LoadString(HINST_THISDLL, idString, lpBuffer, cchBufferMax);
  411.     }
  412. }
  413. // out:
  414. //      pszPath     fills in with the full path with no env gunk (MAX_PATH)
  415. HRESULT _GetFolderDefaultPath(const FOLDER_INFO *pfi, HANDLE hToken, LPTSTR pszPath)
  416. {
  417.     TCHAR szEntry[MAX_PATH];
  418.     *pszPath = 0;
  419.     switch (pfi->id)
  420.     {
  421.     case CSIDL_PROFILE:
  422.         GetUserProfileDir(hToken, pszPath);
  423.         break;
  424.     case CSIDL_PROGRAM_FILES:
  425.         GetProgramFiles(TEXT("ProgramFilesDir"), pszPath);
  426.         break;
  427.     case CSIDL_PROGRAM_FILES_COMMON:
  428.         GetProgramFiles(TEXT("CommonFilesDir"  ), pszPath);
  429.         break;
  430.     case CSIDL_PROGRAM_FILESX86:
  431.         GetProgramFiles(TEXT("ProgramFilesDir (x86)"), pszPath);
  432.         break;
  433.     case CSIDL_PROGRAM_FILES_COMMONX86:
  434.         GetProgramFiles(TEXT("CommonFilesDir (x86)"), pszPath);
  435.         break;
  436.     case CSIDL_SYSTEMX86:
  437.     case CSIDL_SYSTEM:
  438.         // tell GetSystemDirectory() we are know what we are doing
  439.         SetUseKnownWx86Dll(pfi, TRUE);
  440.         GetSystemDirectory(pszPath, MAX_PATH);
  441.         SetUseKnownWx86Dll(pfi, FALSE);
  442.         break;
  443.     case CSIDL_WINDOWS:
  444.         GetWindowsDirectory(pszPath, MAX_PATH);
  445.         break;
  446.     default:
  447.         switch (pfi->dwFlags & SDIF_CREATE_IN_MASK)
  448.         {
  449.         case SDIF_CREATE_IN_ROOT:
  450.             GetWindowsDirectory(pszPath, MAX_PATH);
  451.             PathStripToRoot(pszPath);
  452.             break;
  453.         case SDIF_CREATE_IN_ALLUSERS:
  454.             GetAllUsersDirectory(pszPath);
  455.             break;
  456.         case SDIF_CREATE_IN_WINDIR:
  457.             GetWindowsDirectory(pszPath, MAX_PATH);
  458.             break;
  459.         case SDIF_CREATE_IN_MYDOCUMENTS:
  460.         {
  461.             HRESULT hr = SHGetFolderPath(NULL, CSIDL_PERSONAL, hToken, SHGFP_TYPE_CURRENT, pszPath);
  462.             if (FAILED(hr))
  463.             {
  464.                 return hr;
  465.             }
  466.             break;
  467.         }
  468.         default:
  469.             if (!GetUserProfileDir(hToken, pszPath))
  470.             {
  471.                 // Win9x, failed to get user profile dir. Using windows directory...
  472.                 GetWindowsDirectory(pszPath, MAX_PATH);
  473.                 if (pfi->id == CSIDL_PERSONAL) 
  474.                 {
  475.                     PathStripToRoot(pszPath);
  476.                 }
  477.             }
  478.             break;
  479.         }
  480.         LoadDefaultString(pfi->idsDefault, szEntry, ARRAYSIZE(szEntry));
  481.         PathAppend(pszPath, szEntry);
  482.         break;
  483.     }
  484.     return *pszPath ? S_OK : E_FAIL;
  485. }
  486. void RegSetFolderPath(const FOLDER_INFO *pfi, LPCTSTR pszSubKey, LPCTSTR pszPath)
  487. {
  488.     HKEY hk;
  489.     if (SUCCEEDED(_OpenKeyForFolder(pfi, NULL, pszSubKey, &hk)))
  490.     {
  491.         if (pszPath)
  492.             RegSetValueEx(hk, pfi->pszValueName, 0, REG_SZ, (LPBYTE)pszPath, (1 + lstrlen(pszPath)) * SIZEOF(TCHAR));
  493.         else
  494.             RegDeleteValue(hk, pfi->pszValueName);
  495.         RegCloseKey(hk);
  496.     }
  497. }
  498. BOOL RegQueryPath(HKEY hk, LPCTSTR pszValue, LPTSTR pszPath)
  499. {
  500.     DWORD cbPath = MAX_PATH * SIZEOF(TCHAR);
  501.     *pszPath = 0;
  502.     SHQueryValueEx(hk, pszValue, 0, NULL, pszPath, &cbPath);
  503.     return (BOOL)*pszPath;
  504. }
  505. // More than 50 is silly
  506. #define MAX_TEMP_FILE_TRIES         50
  507. // returns:
  508. //      S_OK        the path exists and it is a folder
  509. //      FAILED()    result
  510. HRESULT _IsFolderNotFile(LPCTSTR pszFolder)
  511. {
  512.     HRESULT hr;
  513.     DWORD dwAttribs = GetFileAttributes(pszFolder);
  514.     if (dwAttribs == -1)
  515.     {
  516.         DWORD err = GetLastError();
  517.         hr = HRESULT_FROM_WIN32(err);
  518.     }
  519.     else
  520.     {
  521.         // see if it is a file, if so we need to rename that file
  522.         if (dwAttribs & FILE_ATTRIBUTE_DIRECTORY)
  523.         {
  524.             hr = S_OK;
  525.         }
  526.         else
  527.         {
  528.             int iExt = 0;
  529.             do
  530.             {
  531.                 TCHAR szExt[32], szDst[MAX_PATH];
  532.                 wsprintf(szExt, TEXT(".%03d"), iExt);
  533.                 lstrcpy(szDst, pszFolder);
  534.                 lstrcat(szDst, szExt);
  535.                 if (MoveFile(pszFolder, szDst))
  536.                     iExt = 0;
  537.                 else
  538.                 {
  539.                     // Normally we fail because .00x already exists but that may not be true.
  540.                     DWORD dwError = GetLastError();
  541.                     if (ERROR_ALREADY_EXISTS == dwError)
  542.                         iExt++;     // Try the next one...
  543.                     else
  544.                         iExt = 0;   // We have problems and need to give up. (No write access?)
  545.                 }
  546.             } while (iExt && (iExt < MAX_TEMP_FILE_TRIES));
  547.             hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
  548.         }
  549.     }
  550.     return hr;
  551. }
  552. HRESULT _OpenKeyForFolder(const FOLDER_INFO *pfi, HANDLE hToken, LPCTSTR pszSubKey, HKEY *phkey)
  553. {
  554.     TCHAR szRegPath[255];
  555.     LONG err;
  556.     HKEY hkRoot, hkeyToFree = NULL;
  557.     *phkey = NULL;
  558.     lstrcpy(szRegPath, TEXT("Software\Microsoft\Windows\CurrentVersion\Explorer\"));
  559.     lstrcat(szRegPath, pszSubKey);
  560. #ifdef  WINNT
  561.     if (_IsDefaultUserToken(hToken) && (pfi->hKey == HKEY_CURRENT_USER))
  562.     {
  563.         if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_USERS, TEXT(".Default"), 0, KEY_READ, &hkRoot))
  564.             hkeyToFree = hkRoot;
  565.         else
  566.             return E_FAIL;
  567.     }
  568.     else
  569. #endif
  570.     if (hToken && (pfi->hKey == HKEY_CURRENT_USER))
  571.     {
  572.         if (GetUserProfileKey(hToken, &hkRoot))
  573.             hkeyToFree = hkRoot;
  574.         else
  575.             return E_FAIL;
  576.     }
  577.     else
  578.         hkRoot = pfi->hKey;
  579.     err = RegCreateKeyEx(hkRoot, szRegPath, 0, NULL, REG_OPTION_NON_VOLATILE,
  580.                 MAXIMUM_ALLOWED, NULL, phkey, NULL);
  581.     
  582.     if (hkeyToFree)
  583.         RegCloseKey(hkeyToFree);
  584.     return HRESULT_FROM_WIN32(err);
  585. }
  586. // returns:
  587. //      S_OK        found in registry, path well formed
  588. //      S_FALSE     empty registry
  589. //      FAILED()    failure result
  590. HRESULT _GetFolderFromReg(const FOLDER_INFO *pfi, HANDLE hToken, LPTSTR pszPath)
  591. {
  592.     HKEY hkUSF;
  593.     HRESULT hr;
  594.     *pszPath = 0;
  595.     hr = _OpenKeyForFolder(pfi, hToken, TEXT("User Shell Folders"), &hkUSF);
  596.     if (SUCCEEDED(hr))
  597.     {
  598.         TCHAR szExpand[MAX_PATH];
  599.         DWORD dwType, cbPath = SIZEOF(szExpand);
  600.         if (RegQueryValueEx(hkUSF, pfi->pszValueName, 0, &dwType, (BYTE *)szExpand, &cbPath) == ERROR_SUCCESS)
  601.         {
  602.             if (REG_SZ == dwType)
  603.             {
  604.                 lstrcpyn(pszPath, szExpand, MAX_PATH);
  605.             }
  606.             else if (REG_EXPAND_SZ == dwType)
  607.             {
  608.                 ExpandEnvironmentStringsNoEnv(hToken, szExpand, pszPath, MAX_PATH);
  609.             }
  610.             TraceMsg(TF_PATH, "_CreateFolderPath 'User Shell Folders' %s = %s", pfi->pszValueName, pszPath);
  611.         }
  612.         if (*pszPath == 0)
  613.         {
  614.             hr = S_FALSE;     // empty registry, success but empty
  615.         }
  616.         else if ((PathGetDriveNumber(pszPath) != -1) || PathIsUNC(pszPath))
  617.         {
  618.             hr = S_OK;        // good reg path, fully qualified
  619.         }
  620.         else
  621.         {
  622.             *pszPath = 0;       // bad reg data
  623.             hr = E_INVALIDARG;
  624.         }
  625.         RegCloseKey(hkUSF);
  626.     }
  627.     return hr;
  628. }
  629. HRESULT _GetFolderPath(HWND hwnd, const FOLDER_INFO *pfi, HANDLE hToken, UINT uFlags, LPTSTR pszPath)
  630. {
  631.     HRESULT hr;
  632.     *pszPath = 0;       // assume failure
  633.     if (pfi->hKey)
  634.     {
  635.         hr = _GetFolderFromReg(pfi, hToken, pszPath);
  636.         if (SUCCEEDED(hr))
  637.         {
  638.             if (hr == S_FALSE)
  639.             {
  640.                 // empty registry, SDIF_EMPTY_IF_NOT_IN_REG means they don't exist
  641.                 // if the registry is not populated with a value. this lets us disable
  642.                 // the common items on platforms that don't want them
  643.                 if (pfi->dwFlags & SDIF_EMPTY_IF_NOT_IN_REG)
  644.                     return S_FALSE;     // success, but empty
  645.                 hr = _GetFolderDefaultPath(pfi, hToken, pszPath);
  646.             }
  647.             if (!(uFlags & CSIDL_FLAG_DONT_VERIFY))
  648.             {
  649.                hr = VerifyAndCreateFolder(hwnd, pfi, uFlags, pszPath) ;
  650.             }
  651.             if (hr != S_OK)
  652.                 *pszPath = 0;
  653.             if (!(uFlags & CSIDL_FLAG_DONT_VERIFY))
  654.             {
  655.                 HKEY hkey;
  656.                 // record value in "Shell Folders", even in the failure case
  657.                 // NOTE: we only do this for historical reasons. there may be some
  658.                 // apps that depend on these values being in the registry, but in general
  659.                 // the contetens here are unreliable as they are only written after someone
  660.                 // asks for the folder through this API.
  661.                 if (SUCCEEDED(_OpenKeyForFolder(pfi, hToken, TEXT("Shell Folders"), &hkey)))
  662.                 {
  663.                     RegSetValueEx(hkey, pfi->pszValueName, 0, REG_SZ, (LPBYTE)pszPath, (1 + lstrlen(pszPath)) * SIZEOF(TCHAR));
  664.                     RegCloseKey(hkey);
  665.                 }
  666.             }
  667.         }
  668.     }
  669.     else
  670.     {
  671.         hr = _GetFolderDefaultPath(pfi, hToken, pszPath);
  672.         if (S_OK == hr)
  673.         {
  674.             if (!(uFlags & CSIDL_FLAG_DONT_VERIFY))
  675.             {
  676.                 hr = VerifyAndCreateFolder(hwnd, pfi, uFlags, pszPath);
  677.             }
  678.         }
  679.         else
  680.         {
  681.             *pszPath = 0;
  682.         }
  683.     }
  684.     ASSERT(hr == S_OK ? *pszPath != 0 : *pszPath == 0);
  685.     return hr;
  686. }
  687. HRESULT VerifyAndCreateFolder(HWND hwnd, const FOLDER_INFO *pfi, UINT uFlags, LPTSTR pszPath)
  688. {
  689.     HRESULT hr ;
  690.     hr = _IsFolderNotFile(pszPath);
  691.     // 99/06/16 vtan: In the case of ERROR_ACCESS_DENIED don't
  692.     // even bother trying to do anything more with the directory.
  693.     if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED))
  694.     {
  695.         return(hr);
  696.     }
  697.     if ((hr != S_OK) && hwnd)
  698.     {
  699.         // we might be able to reconnect if this is a net path
  700.         if (PathIsUNC(pszPath))
  701.         {
  702.             if (SHValidateUNC(hwnd, pszPath, 0))
  703.                 hr = _IsFolderNotFile(pszPath);
  704.         }
  705.         else if (IsDisconnectedNetDrive(DRIVEID(pszPath)))
  706.         {
  707.             TCHAR szDrive[4];
  708.             PathBuildSimpleRoot(DRIVEID(pszPath), szDrive);
  709.             if (WNetRestoreConnection(hwnd, szDrive) == WN_SUCCESS)
  710.                 hr = _IsFolderNotFile(pszPath);
  711.          }
  712.     }
  713.     if ((hr != S_OK) && (uFlags & CSIDL_FLAG_CREATE))
  714.     {
  715.         DWORD err = SHCreateDirectory(NULL, pszPath);
  716.         hr = HRESULT_FROM_WIN32(err);
  717.         if (hr == S_OK)
  718.         {
  719. #ifdef DBG 
  720.             //  we want to fire this assert in checked builds, even when FULL_DEBUG is not on
  721.             if (StrChr(pszPath, TEXT('%')))
  722.                 DebugBreak();
  723. #endif
  724.             if (pfi->dwFlags & SDIF_HIDE)
  725.                 SetFileAttributes(pszPath, GetFileAttributes(pszPath) | FILE_ATTRIBUTE_HIDDEN);
  726.         }
  727.     }
  728.     return hr ;
  729. }
  730. void _SetPathCache(const FOLDER_INFO *pfi, LPCTSTR psz)
  731. {
  732.     LPTSTR pszOld;
  733.     AssertMsg(((LPCTSTR)-1 == psz) || (NULL == psz) || PathFileExistsAndAttributes(psz, NULL), TEXT("Caching non existant folder!"));
  734.     pszOld = (LPTSTR)InterlockedExchangePointer((void **)&g_aFolderCache[pfi->id].psz, (void *)psz);
  735.     if (pszOld && pszOld != (LPTSTR)-1)
  736.     {
  737.         // check for the concurent use... very rare case
  738.         LocalFree(pszOld);
  739.     }
  740. }
  741. HRESULT _GetFolderPathCached(HWND hwnd, const FOLDER_INFO *pfi, HANDLE hToken, UINT uFlags, LPTSTR pszPath)
  742. {
  743.     HRESULT hr;
  744.     *pszPath = 0;
  745.     // can only cache for the current user, hToken == NULL or per machine folders
  746.     if (!hToken || (pfi->hKey != HKEY_CURRENT_USER))
  747.     {
  748.         LPTSTR pszCache;
  749.         _UpdateShellFolderCache();
  750.         pszCache = (LPTSTR)InterlockedExchangePointer((void **)&g_aFolderCache[pfi->id].psz, (void *)-1);
  751.         if ((pszCache == (LPTSTR)-1) || (pszCache == NULL))
  752.         {
  753.             // either not cached or cached failed state
  754.             if ((pszCache == (LPTSTR)-1) || (uFlags & CSIDL_FLAG_CREATE))
  755.             {
  756.                 hr = _GetFolderPath(hwnd, pfi, hToken, uFlags, pszPath);
  757.                 // only set the cache value if CSIDL_FLAG_DONT_VERIFY was NOT passed
  758.                 if (!(uFlags & CSIDL_FLAG_DONT_VERIFY))
  759.                 {
  760.                     if (hr == S_OK)
  761.                     {
  762.                         // dupe the string so we can add it to the cache
  763.                         pszCache = StrDup(pszPath);
  764.                     }
  765.                     else
  766.                     {
  767.                         // we failed to get the folder path, null out the cache
  768.                         ASSERT(*pszPath == 0);
  769.                         pszCache = NULL;
  770.                     }
  771.                     _SetPathCache(pfi, pszCache);
  772.                 }
  773.                 
  774.             }
  775.             else
  776.             {
  777.                 // cache was null and user didnt pass create flag so we just fail
  778.                 ASSERT(pszCache == NULL);
  779.                 ASSERT(*pszPath == 0);
  780.                 hr = E_FAIL;
  781.             }
  782.         }
  783.         else
  784.         {
  785.             // cache hit case: copy the cached string and then restore the cached value back
  786.             lstrcpyn(pszPath, pszCache, MAX_PATH);
  787.             _SetPathCache(pfi, pszCache);
  788.             hr = S_OK;
  789.         }
  790.     }
  791.     else
  792.     {
  793.         hr = _GetFolderPath(hwnd, pfi, hToken, uFlags, pszPath);
  794.     }
  795.     return hr;
  796. }
  797. HRESULT _CreateFolderIDList(HWND hwnd, const FOLDER_INFO *pfi, HANDLE hToken, UINT uFlags, LPITEMIDLIST *ppidl)
  798. {
  799.     HRESULT hr = S_OK;
  800.     switch (pfi->id)
  801.     {
  802.     case CSIDL_PRINTERS:
  803.         if (SHGetAppCompatFlags(ACF_STAROFFICE5PRINTER))
  804.         {
  805.             // Star Office 5.0 relies on the fact that the printer pidl used to be like below.  They skip the 
  806.             // first simple pidl (My Computer) and do not check if there is anything else, they assume that the
  807.             // second simple pidl is the Printer folder one. (stephstm, 07/30/99)
  808.             // CLSID_MyComputer, CLSID_Printers
  809.             hr = SHILCreateFromPath(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\::{2227A280-3AEA-1069-A2DE-08002B30309D}"), ppidl, NULL);
  810.         }
  811.         else
  812.         {
  813.             // CLSID_MyComputer, CLSID_ControlPanel, CLSID_Printers
  814.             hr = SHILCreateFromPath(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\::{2227A280-3AEA-1069-A2DE-08002B30309D}"), ppidl, NULL);
  815.         }
  816.         break;
  817.     case CSIDL_CONTROLS:
  818.         // CLSID_MyComputer, CLSID_ControlPanel
  819.         hr = SHILCreateFromPath(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), ppidl, NULL);
  820.         break;
  821.     case CSIDL_BITBUCKET:
  822.         // CLSID_RecycleBin
  823.         hr = SHILCreateFromPath(TEXT("::{645FF040-5081-101B-9F08-00AA002F954E}"), ppidl, NULL);
  824.         break;
  825.     default:
  826.         {
  827.             TCHAR szPath[MAX_PATH];
  828.             hr = _GetFolderPathCached(hwnd, pfi, hToken, uFlags, szPath);
  829.             if (hr == S_OK)
  830.             {
  831.                 HRESULT hrInit = SHCoInitialize();
  832.                 hr = ILCreateFromPathEx(szPath, NULL, ILCFP_FLAG_SKIPJUNCTIONS, ppidl, NULL);
  833.                 SHCoUninitialize(hrInit);
  834.             }
  835.             else
  836.             {
  837.                 *ppidl = NULL;      // assume failure or empty
  838.             }
  839.         }
  840.         break;
  841.     }
  842.     return hr;
  843. }
  844. void _SetIDListCache(const FOLDER_INFO *pfi, LPCITEMIDLIST pidl)
  845. {
  846.     if (!(pfi->dwFlags & SDIF_CONST_IDLIST))
  847.     {
  848.         LPITEMIDLIST pidlOld = (LPITEMIDLIST)InterlockedExchangePointer((void **)&g_aFolderCache[pfi->id].pidl, (void *)pidl);
  849.         if (pidlOld && pidlOld != (LPITEMIDLIST)-1)
  850.         {
  851.             // check for the concurent use... very rare case
  852.             // ASSERT(pidl == (LPCITEMIDLIST)-1);   // should not really be ASSERT
  853.             ILFree(pidlOld);
  854.         }
  855.     }
  856. }
  857. // hold this lock for the minimal amout of time possible to avoid other users
  858. // of this resource requring them to re-create the pidl
  859. HRESULT _GetFolderIDListCached(HWND hwnd, const FOLDER_INFO *pfi, UINT uFlags, LPITEMIDLIST *ppidl)
  860. {
  861.     HRESULT hr;
  862.     ASSERT(pfi->id < ARRAYSIZE(g_aFolderCache));
  863.     if (pfi->dwFlags & SDIF_CONST_IDLIST)
  864.     {
  865.         // these are CONST, never change
  866.         hr = SHILClone(g_aFolderCache[pfi->id].pidl, ppidl);     
  867.     }
  868.     else
  869.     {
  870.         LPITEMIDLIST pidlCache;
  871.         _UpdateShellFolderCache();
  872.         pidlCache = (LPITEMIDLIST)InterlockedExchangePointer((void **)&g_aFolderCache[pfi->id].pidl, (void *)-1);
  873.         if ((pidlCache == (LPCITEMIDLIST)-1) || (pidlCache == NULL))
  874.         {
  875.             // either uninitalized cache state OR cached failure (NULL)
  876.             if ((pidlCache == (LPCITEMIDLIST)-1) || (uFlags & CSIDL_FLAG_CREATE))
  877.             {
  878.                 // not initialized (or concurent use) try creating it for this use
  879.                 hr = _CreateFolderIDList(hwnd, pfi, NULL, uFlags, ppidl);
  880.                 if (S_OK == hr)
  881.                     hr = SHILClone(*ppidl, &pidlCache); // create cache copy
  882.                 else
  883.                     pidlCache = NULL;
  884.             }
  885.             else
  886.                 hr = E_FAIL;            // return cached failure
  887.         }
  888.         else
  889.         {
  890.             hr = SHILClone(pidlCache, ppidl);   // cache hit
  891.         }
  892.         // store back the PIDL if it is non NULL or they specified CREATE
  893.         // and we failed to create it (cache the not existant state). this is needed
  894.         // so we don't cache a NULL if the first callers don't ask for create and
  895.         // subsequent callers do
  896.         if (pidlCache || (uFlags & CSIDL_FLAG_CREATE))
  897.             _SetIDListCache(pfi, pidlCache);
  898.     }
  899.     return hr;
  900. }
  901. void _ClearCacheEntry(const FOLDER_INFO *pfi)
  902. {
  903.     if (!(pfi->dwFlags & SDIF_CONST_IDLIST))
  904.         _SetIDListCache(pfi, (LPCITEMIDLIST)-1);
  905.     _SetPathCache(pfi, (LPCTSTR)-1);
  906. }
  907. void _ClearAllCacheEntrys()
  908. {
  909.     const FOLDER_INFO *pfi;
  910.     for (pfi = c_rgFolderInfo; pfi->id != -1; pfi++)
  911.     {
  912.         _ClearCacheEntry(pfi);
  913.     }
  914. }
  915. // Per instance count of mods to Special Folder cache.
  916. HANDLE g_hCounter = NULL;   // Global count of mods to Special Folder cache.
  917. int g_lPerProcessCount = 0;
  918. //----------------------------------------------------------------------------
  919. // Make sure the special folder cache is up to date.
  920. void _UpdateShellFolderCache(void)
  921. {
  922.     long lGlobalCount;
  923.     HANDLE hCounter = SHGetCachedGlobalCounter(&g_hCounter, &GUID_SystemPidlChange);
  924.     // Is the cache up to date?
  925.     lGlobalCount = SHGlobalCounterGetValue(hCounter);
  926.     if (lGlobalCount != g_lPerProcessCount)
  927.     {
  928.         _ClearAllCacheEntrys();
  929.         g_lPerProcessCount = lGlobalCount;
  930.     }
  931. }
  932. STDAPI_(void) SHFlushSFCache(void)
  933. {
  934.     // Increment the shared variable;  the per-process versions will no
  935.     // longer match, causing this and/or other processes to refresh their
  936.     // pidl caches when they next need to access a folder.
  937.     if (g_hCounter)
  938.         SHGlobalCounterIncrement(g_hCounter);
  939. }
  940. // use SHGetFolderLocation() instead using CSIDL_FLAG_CREATE
  941. STDAPI_(LPITEMIDLIST) SHCloneSpecialIDList(HWND hwnd, int csidl, BOOL fCreate)
  942. {
  943.     LPITEMIDLIST pidlReturn;
  944.     if (fCreate)
  945.         csidl |= CSIDL_FLAG_CREATE;
  946.     SHGetSpecialFolderLocation(hwnd, csidl, &pidlReturn);
  947.     return pidlReturn;
  948. }
  949. STDAPI SHGetSpecialFolderLocation(HWND hwnd, int csidl, LPITEMIDLIST *ppidl)
  950. {
  951.     HRESULT hr = SHGetFolderLocation(hwnd, csidl, NULL, 0, ppidl);
  952.     if (hr == S_FALSE)
  953.         hr = E_FAIL;        // mail empty case into failure for compat with this API
  954.     return hr;
  955. }
  956. // return IDLIST for special folder
  957. //      fCreate encoded in csidl with CSIDL_FLAG_CREATE (new for NT5)
  958. //
  959. //  in:
  960. //      hwnd    should be NULL
  961. //      csidl   CSIDL_ value with CSIDL_FLAG_ values ORed in as well
  962. //      dwType  must be SHGFP_TYPE_CURRENT
  963. //
  964. //  out:
  965. //      *ppild  NULL on failure or empty, PIDL to be freed by caller on success
  966. //
  967. //  returns:
  968. //      S_OK        *ppidl is non NULL
  969. //      S_FALISE    *ppidl is NULL, but valid csidl was passed (folder does not exist)
  970. //      FAILED(hr)
  971. STDAPI SHGetFolderLocation(HWND hwnd, int csidl, HANDLE hToken, DWORD dwType, LPITEMIDLIST *ppidl)
  972. {
  973.     const FOLDER_INFO *pfi;
  974.     HRESULT hr;
  975.     *ppidl = NULL;  // in case of error or empty
  976.     // -1 is an invalid csidl
  977.     if ((dwType != SHGFP_TYPE_CURRENT) || (-1 == csidl))
  978.         return E_INVALIDARG;    // no flags used yet, validate this param
  979.     pfi = _GetFolderInfo(csidl & ~CSIDL_FLAG_MASK);
  980.     if (pfi)
  981.     {
  982.         HANDLE hTokenToFree = NULL;
  983. #ifdef WINNT
  984.         if (hToken == NULL && (pfi->hKey == HKEY_CURRENT_USER))
  985.         {
  986.             if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hToken))
  987.                 hTokenToFree = hToken;
  988.         }
  989. #endif
  990.         if (hToken && (pfi->hKey == HKEY_CURRENT_USER))
  991.         {
  992.             // we don't cache PIDLs for other users, do all of the work
  993.             hr = _CreateFolderIDList(hwnd, pfi, hToken, csidl & CSIDL_FLAG_MASK, (LPITEMIDLIST *)ppidl);
  994.         }
  995.         else
  996.         {
  997.             hr = _GetFolderIDListCached(hwnd, pfi, csidl & CSIDL_FLAG_MASK, ppidl);
  998.         }
  999.         if (hTokenToFree)
  1000.             CloseHandle(hTokenToFree);
  1001.     }
  1002.     else
  1003.         hr = E_INVALIDARG;    // bad CSIDL (apps can check to veryify our support)
  1004.     return hr;
  1005. }
  1006. STDAPI_(BOOL) SHGetSpecialFolderPath(HWND hwnd, LPTSTR pszPath, int csidl, BOOL fCreate)
  1007. {
  1008.     if (fCreate)
  1009.         csidl |= CSIDL_FLAG_CREATE;
  1010.     return SHGetFolderPath(hwnd, csidl, NULL, 0, pszPath) == S_OK;
  1011. }
  1012. //  in:
  1013. //      hwnd    should be NULL
  1014. //      csidl   CSIDL_ value with CSIDL_FLAG_ values ORed in as well
  1015. //      dwType  must be SHGFP_TYPE_CURRENT
  1016. //
  1017. //  out:
  1018. //      *pszPath    MAX_PATH buffer to get path name, zeroed on failure or empty case
  1019. //
  1020. //  returns:
  1021. //      S_OK        filled in pszPath with path value
  1022. //      S_FALSE     pszPath is NULL, valid CSIDL value, but this folder does not exist
  1023. //      E_FAIL
  1024. STDAPI SHGetFolderPath(HWND hwnd, int csidl, HANDLE hToken, DWORD dwType, LPTSTR pszPath)
  1025. {
  1026.     HRESULT hr = E_INVALIDARG;
  1027.     const FOLDER_INFO *pfi;
  1028.     ASSERT(IS_VALID_WRITE_BUFFER(pszPath, TCHAR, MAX_PATH));
  1029.     *pszPath = 0;
  1030.     pfi = _GetFolderInfo(csidl & ~CSIDL_FLAG_MASK);
  1031.     if (pfi && !(pfi->dwFlags & SDIF_NOT_FILESYS))
  1032.     {
  1033.         switch (dwType)
  1034.         {
  1035.         case SHGFP_TYPE_DEFAULT:
  1036.             ASSERT((csidl & CSIDL_FLAG_MASK) == 0); // meaningless for default
  1037.             hr = _GetFolderDefaultPath(pfi, hToken, pszPath);
  1038.             break;
  1039.     
  1040.         case SHGFP_TYPE_CURRENT:
  1041.             {
  1042.                 HANDLE hTokenToFree = NULL;
  1043. #ifdef WINNT
  1044.                 if (hToken == NULL && (pfi->hKey == HKEY_CURRENT_USER))
  1045.                 {
  1046.                     if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hToken))
  1047.                         hTokenToFree = hToken;
  1048.                 }
  1049. #endif
  1050.                 hr = _GetFolderPathCached(hwnd, pfi, hToken, csidl & CSIDL_FLAG_MASK, pszPath);
  1051.                 if (hTokenToFree)
  1052.                     CloseHandle(hTokenToFree);
  1053.             }
  1054.             break;
  1055.         }
  1056.     }
  1057.     return hr;
  1058. }
  1059. #ifdef UNICODE
  1060. STDAPI SHGetFolderPathA(HWND hwnd, int csidl, HANDLE hToken, DWORD dwType, LPSTR pszPath)
  1061. {
  1062.     WCHAR wsz[MAX_PATH];
  1063.     HRESULT hr = SHGetFolderPath(hwnd, csidl, hToken, dwType, wsz);
  1064.     ASSERT(IS_VALID_WRITE_BUFFER(pszPath, CHAR, MAX_PATH));
  1065.     SHUnicodeToAnsi(wsz, pszPath, MAX_PATH);
  1066.     return hr;
  1067. }
  1068. STDAPI_(BOOL) SHGetSpecialFolderPathA(HWND hwnd, LPSTR pszPath, int csidl, BOOL fCreate)
  1069. {
  1070.     if (fCreate)
  1071.         csidl |= CSIDL_FLAG_CREATE;
  1072.     return SHGetFolderPathA(hwnd, csidl, NULL, 0, pszPath) == S_OK;
  1073. }
  1074. #else
  1075. STDAPI SHGetFolderPathW(HWND hwnd, int csidl, HANDLE hToken, DWORD dwType, LPWSTR pszPath)
  1076. {
  1077.     CHAR sz[MAX_PATH];
  1078.     HRESULT hr = SHGetFolderPath(hwnd, csidl, hToken, dwType, sz);
  1079.     ASSERT(IS_VALID_WRITE_BUFFER(pszPath, WCHAR, MAX_PATH));
  1080.     SHAnsiToUnicode(sz, pszPath, MAX_PATH);
  1081.     return hr;
  1082. }
  1083. STDAPI_(BOOL) SHGetSpecialFolderPathW(HWND hwnd, LPWSTR pszPath, int csidl, BOOL fCreate)
  1084. {
  1085.     if (fCreate)
  1086.         csidl |= CSIDL_FLAG_CREATE;
  1087.     return SHGetFolderPathW(hwnd, csidl, NULL, 0, pszPath) == S_OK;
  1088. }
  1089. #endif
  1090. //  HRESULT SHSetFolderPath (int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)
  1091. //
  1092. //  in:
  1093. //      csidl       CSIDL_ value with CSIDL_FLAG_ values ORed in as well
  1094. //      dwFlags     reserved: should be 0x00000000
  1095. //      pszPath     path to change shell folder to (will optionally be unexpanded)
  1096. //
  1097. //  returns:
  1098. //      S_OK        function succeeded and flushed cache
  1099. STDAPI  SHSetFolderPath (int csidl, HANDLE hToken, DWORD dwFlags, LPCTSTR pszPath)
  1100. {
  1101.     HRESULT             hr;
  1102.     const FOLDER_INFO   *pfi;
  1103.     hr = E_INVALIDARG;
  1104.     // Validate csidl and dwFlags. Add extra valid flags as needed.
  1105.     RIPMSG(((csidl & CSIDL_FLAG_MASK) & ~(CSIDL_FLAG_DONT_UNEXPAND | 0x00000000)) == 0, "SHSetFolderPath: CSIDL flag(s) invalid");
  1106.     RIPMSG(dwFlags == 0, "SHSetFolderPath: dwFlags parameter must be 0x00000000");
  1107.     // Exit with E_INVALIDARG if bad parameters.
  1108.     if ((((csidl & CSIDL_FLAG_MASK) & ~(CSIDL_FLAG_DONT_UNEXPAND | 0x00000000)) != 0) ||
  1109.         (dwFlags != 0) ||
  1110.         (pszPath == NULL) ||
  1111.         (pszPath[0] == TEXT('')))
  1112.     {
  1113.         return(hr);
  1114.     }
  1115.     pfi = _GetFolderInfo(csidl & ~CSIDL_FLAG_MASK);
  1116.     // Only allow setting for SDIF_NOT_FILESYS is clear
  1117.     //                        SDIF_NOT_TRACKED is clear
  1118.     //                        SDIF_CANT_MOVE_RENAME is clear
  1119.     // and for non-NULL value
  1120.     // If HKLM is used then rely on security or registry restrictions
  1121.     // to enforce whether the change can be made.
  1122.     if ((pfi != NULL) &&
  1123.         ((pfi->dwFlags & (SDIF_NOT_FILESYS | SDIF_NOT_TRACKED | SDIF_CANT_MOVE_RENAME)) == 0))
  1124.     {
  1125.         BOOL    fSuccessfulUnexpand, fSuccessfulExpand, fEmptyOrNullPath;
  1126.         LONG    lError;
  1127.         HANDLE  hTokenToFree;
  1128.         TCHAR   szPath[MAX_PATH];
  1129.         TCHAR   szExpandedPath[MAX_PATH];   // holds expanded path for "Shell Folder" compat key
  1130.         LPCTSTR pszWritePath;
  1131.         hTokenToFree = NULL;
  1132. #ifdef WINNT
  1133.         if ((hToken == NULL) && (pfi->hKey == HKEY_CURRENT_USER))
  1134.         {
  1135.             if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hToken))
  1136.             {
  1137.                 hTokenToFree = hToken;
  1138.             }
  1139.         }
  1140. #endif
  1141.         fEmptyOrNullPath = ((pszPath == NULL) || (pszPath[0] == TEXT('')));
  1142.         if (fEmptyOrNullPath)
  1143.         {
  1144. #ifdef  WINNT
  1145.             HKEY    hKeyDefaultUser;
  1146.             pszWritePath = NULL;
  1147.             if (SUCCEEDED(_OpenKeyForFolder(pfi, (HANDLE)-1, TEXT("User Shell Folders"), &hKeyDefaultUser)))
  1148.             {
  1149.                 DWORD   dwPathSize;
  1150.                 dwPathSize = sizeof(szPath);
  1151.                 if (ERROR_SUCCESS == RegQueryValueEx(hKeyDefaultUser,
  1152.                                                      pfi->pszValueName,
  1153.                                                      NULL,
  1154.                                                      NULL,
  1155.                                                      (LPBYTE)szPath,
  1156.                                                      &dwPathSize))
  1157.                 {
  1158.                     pszWritePath = szPath;
  1159.                 }
  1160.                 RegCloseKey(hKeyDefaultUser);
  1161.             }
  1162. #else
  1163.             pszWritePath = NULL;
  1164. #endif
  1165.             fSuccessfulUnexpand = TRUE;
  1166.         }
  1167.         else if ((csidl & CSIDL_FLAG_DONT_UNEXPAND) != 0)
  1168.         {
  1169.             // Does the caller want to write the string as is? Leave
  1170.             // it alone if so.
  1171.             pszWritePath = pszPath;
  1172.             fSuccessfulUnexpand = TRUE;
  1173.         }
  1174.         else
  1175.         {
  1176.             if (pfi->hKey == HKEY_CURRENT_USER)
  1177.             {
  1178.                 fSuccessfulUnexpand = (PathUnExpandEnvStringsForUser(hToken, pszPath, szPath, ARRAYSIZE(szPath)) != FALSE);
  1179.             }
  1180.             pszWritePath = szPath;
  1181.         }
  1182.         if (fSuccessfulUnexpand)
  1183.         {
  1184.             HKEY    hKeyUser, hKeyUSF, hKeyToFree;
  1185.             // we also get the fully expanded path so that we can write it out to the "Shell Folders" key for lame apps that depend on
  1186.             // the old registry values
  1187.             fSuccessfulExpand = (SHExpandEnvironmentStringsForUser(hToken, pszPath, szExpandedPath, ARRAYSIZE(szExpandedPath)) != 0);
  1188.             // Get either the current users HKCU or HKUSID if a token
  1189.             // was specified and running in NT.
  1190.             if ((hToken != NULL) && (GetUserProfileKey(hToken, &hKeyUser) != FALSE))
  1191.             {
  1192.                 hKeyToFree = hKeyUser;
  1193.             }
  1194.             else
  1195.             {
  1196.                 hKeyUser = pfi->hKey;
  1197.                 hKeyToFree = NULL;
  1198.             }
  1199.             // Open the key to the User Shell Folders and write the string
  1200.             // there. Clear the shell folder cache.
  1201.             // NOTE: This functionality is duplicated in SetFolderPath but
  1202.             // that function deals with the USF key only. This function
  1203.             // requires HKUSID so while there is identical functionality
  1204.             // from the point of view of settings the USF value that is
  1205.             // where it ends. To make this function simple it just writes
  1206.             // the value to registry itself.
  1207.             // Additional note: there is a threading issue here with
  1208.             // clearing the cache entry incrementing the counter. This
  1209.             // should be locked access.
  1210.             lError = RegOpenKeyEx(hKeyUser,
  1211.                                   TEXT("Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders"),
  1212.                                   0,
  1213.                                   KEY_READ | KEY_WRITE,
  1214.                                   &hKeyUSF);
  1215.             if (lError == ERROR_SUCCESS)
  1216.             {
  1217.                 if (pszWritePath != NULL)
  1218.                 {
  1219.                     // vtan: Win98 SE supports writing whatever type of data
  1220.                     // you give it. It blindly gives back the same type. If
  1221.                     // this function writes REG_EXPAND_SZ any corresponding
  1222.                     // reading function (SHGetFolderPath) will automatically
  1223.                     // expand the variables so this doesn't appear to present
  1224.                     // a compatibility issue. If it does (I have foreseen
  1225.                     // incorrectly) it can be solved using an "#ifdef WINNT".
  1226.                     lError = RegSetValueEx(hKeyUSF,
  1227.                                            pfi->pszValueName,
  1228.                                            0,
  1229.                                            REG_EXPAND_SZ,
  1230.                                            (LPBYTE)pszWritePath,
  1231.                                            (lstrlen(pszWritePath) + sizeof('')) * sizeof(TCHAR));
  1232.                 }
  1233.                 else
  1234.                 {
  1235.                     lError = RegDeleteValue(hKeyUSF, pfi->pszValueName);
  1236.                 }
  1237.                 RegCloseKey(hKeyUSF);
  1238.                 _ClearCacheEntry(pfi);
  1239.                 g_lPerProcessCount = SHGlobalCounterIncrement(g_hCounter);
  1240.             }
  1241.             // update the old "Shell Folders" value for compat
  1242.             if ((lError == ERROR_SUCCESS) && fSuccessfulExpand)
  1243.             {
  1244.                 HKEY hkeySF;
  1245.                 if (RegOpenKeyEx(hKeyUser,
  1246.                                  TEXT("Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"),
  1247.                                  0,
  1248.                                  KEY_READ | KEY_WRITE,
  1249.                                  &hkeySF) == ERROR_SUCCESS)
  1250.                 {
  1251.                     if (pszWritePath != NULL)
  1252.                     {
  1253.                         RegSetValueEx(hkeySF,
  1254.                                       pfi->pszValueName,
  1255.                                       0,
  1256.                                       REG_SZ,
  1257.                                       (LPBYTE)szExpandedPath,
  1258.                                       (lstrlen(szExpandedPath) + sizeof('')) * sizeof(TCHAR));
  1259.                     }
  1260.                     else
  1261.                     {
  1262.                         RegDeleteValue(hkeySF, pfi->pszValueName);
  1263.                     }
  1264.                     RegCloseKey(hkeySF);
  1265.                 }
  1266.             }
  1267.             if ((lError == ERROR_SUCCESS) && (pfi->hKey == HKEY_CURRENT_USER))
  1268.             {
  1269.                 switch (csidl & ~CSIDL_FLAG_MASK)
  1270.                 {
  1271.                     case CSIDL_APPDATA:
  1272.                     {
  1273.                         HKEY    hKeyVolatileEnvironment;
  1274.                         // In the case of AppData there is a matching environment variable
  1275.                         // for this shell folder. Make sure the place in the registry where
  1276.                         // userenv.dll places this value is updated and correct so that when
  1277.                         // the user context is created by winlogon it will have the updated
  1278.                         // value.
  1279.                         // It's probably also a good thing to check for a %APPDATA% variable
  1280.                         // in the calling process' context but this would only be good for
  1281.                         // the life of the process. What is really required is a mechanism
  1282.                         // to change the environment variable for the entire logon session.
  1283.                         lError = RegOpenKeyEx(hKeyUser,
  1284.                                               TEXT("Volatile Environment"),
  1285.                                               0,
  1286.                                               KEY_READ | KEY_WRITE,
  1287.                                               &hKeyVolatileEnvironment);
  1288.                         if (lError == ERROR_SUCCESS)
  1289.                         {
  1290.                             if (SUCCEEDED(SHGetFolderPath(NULL,
  1291.                                                           csidl | CSIDL_FLAG_DONT_VERIFY,
  1292.                                                           hToken,
  1293.                                                           SHGFP_TYPE_CURRENT,
  1294.                                                           szPath)))
  1295.                             {
  1296.                                 lError = RegSetValueEx(hKeyVolatileEnvironment,
  1297.                                                        TEXT("APPDATA"),
  1298.                                                        0,
  1299.                                                        REG_SZ,
  1300.                                                        (LPBYTE)szPath,
  1301.                                                        (lstrlen(szPath) + sizeof('')) * sizeof(TCHAR));
  1302.                             }
  1303.                             RegCloseKey(hKeyVolatileEnvironment);
  1304.                         }
  1305.                         break;
  1306.                     }
  1307.                     default:
  1308.                     {
  1309.                         break;
  1310.                     }
  1311.                 }
  1312.             }
  1313.             if (hKeyToFree != NULL)
  1314.             {
  1315.                 RegCloseKey(hKeyToFree);
  1316.             }
  1317.             if (lError == ERROR_SUCCESS)
  1318.             {
  1319.                 hr = S_OK;
  1320.             }
  1321.             else
  1322.             {
  1323.                 hr = HRESULT_FROM_WIN32(lError);
  1324.             }
  1325.         }
  1326.         if (hTokenToFree != NULL)
  1327.         {
  1328.             CloseHandle(hTokenToFree);
  1329.         }
  1330.     }
  1331.     return(hr);
  1332. }
  1333. #ifdef UNICODE
  1334. STDAPI SHSetFolderPathA(int csidl, HANDLE hToken, DWORD dwType, LPCSTR pszPath)
  1335. {
  1336.     WCHAR       wsz[MAX_PATH];
  1337.     SHAnsiToUnicode(pszPath, wsz, MAX_PATH);
  1338.     return(SHSetFolderPath(csidl, hToken, dwType, wsz));
  1339. }
  1340. #else
  1341. STDAPI SHSetFolderPathW(int csidl, HANDLE hToken, DWORD dwType, LPCWSTR pszPath)
  1342. {
  1343.     CHAR        sz[MAX_PATH];
  1344.     SHUnicodeToAnsi(pszPath, sz, MAX_PATH);
  1345.     return(SHSetFolderPath(csidl, hToken, dwType, sz));
  1346. }
  1347. #endif
  1348. // NOTE: called from DllEntry
  1349. void SpecialFolderIDTerminate()
  1350. {
  1351.     ASSERTDLLENTRY      // does not require a critical section
  1352.     _ClearAllCacheEntrys();
  1353.     if (g_hCounter)
  1354.     {
  1355.         CloseHandle(g_hCounter);
  1356.         g_hCounter = NULL;
  1357.     }
  1358. }
  1359. // update our cache and the registry for pfi with pszPath. this also invalidates the
  1360. // cache in other processes so they stay in sync
  1361. void SetFolderPath(const FOLDER_INFO *pfi, LPCTSTR pszPath)
  1362. {
  1363.     _ClearCacheEntry(pfi);
  1364.     
  1365.     if (pszPath)
  1366.     {
  1367.         HKEY hk;
  1368.         if (SUCCEEDED(_OpenKeyForFolder(pfi, NULL, TEXT("User Shell Folders"), &hk)))
  1369.         {
  1370.             LONG err;
  1371.             TCHAR szDefaultPath[MAX_PATH];
  1372.             
  1373. #ifndef WINNT
  1374.             // If the path being set is the default, delete the custom
  1375.             // setting.  Otherwise, set the new path as the custom setting.
  1376.             
  1377.             _GetFolderDefaultPath(pfi, NULL, szDefaultPath);
  1378.             
  1379.             // There is no reason to delete the value on NT, since we are
  1380.             // able to write REG_EXPAND_SZ strings to the registry, which
  1381.             // handles the roaming case that this RegDeleteValue() attempts
  1382.             // to solve for Win9x
  1383.             
  1384.             // don't delete SDIF_EMPTY_IF_NOT_IN_REG entries, as that will make our
  1385.             // code think they are not to be used
  1386.             if (!(pfi->dwFlags & SDIF_EMPTY_IF_NOT_IN_REG) &&
  1387.                 lstrcmpi(szDefaultPath, pszPath) == 0)
  1388.             {
  1389.                 TraceMsg(TF_PATH, "deleting 'User Shell Folders' %s", pfi->pszValueName);
  1390.                 err = RegDeleteValue(hk, pfi->pszValueName);
  1391.             }
  1392.             else
  1393. #endif // ! WINNT
  1394.             {
  1395.                 // Check for an existing path, and if the unexpanded version
  1396.                 // of the existing path does not match the new path, then
  1397.                 // write the new path to the registry.
  1398.                 //
  1399.                 // RegQueryPath expands the environment variables for us
  1400.                 // so we can't just blindly set the new value to the registry.
  1401.                 //
  1402.                 
  1403.                 RegQueryPath(hk, pfi->pszValueName, szDefaultPath);
  1404.                 
  1405.                 if (lstrcmpi(szDefaultPath, pszPath) != 0)
  1406.                 {
  1407.                     // The paths are different. Write to the registry as file
  1408.                     // system path.
  1409.                     err = SHRegSetPath(hk, NULL, pfi->pszValueName, pszPath, 0);
  1410.                 } 
  1411.                 else
  1412.                     err = ERROR_SUCCESS;
  1413.             }
  1414.             
  1415.             // clear out any temp paths
  1416.             RegSetFolderPath(pfi, TEXT("User Shell Folders\New"), NULL);
  1417.             
  1418.             if (err == ERROR_SUCCESS)
  1419.             {
  1420.                 // this will force a new creation (see TRUE as fCreate).
  1421.                 // This will also copy the path from "User Shell Folders"
  1422.                 // to "Shell Folders".
  1423.                 LPITEMIDLIST pidl;
  1424.                 if (S_OK == _GetFolderIDListCached(NULL, pfi, CSIDL_FLAG_CREATE, &pidl))
  1425.                 {
  1426.                     ILFree(pidl);
  1427.                 }
  1428.                 else
  1429.                 {
  1430.                     // failed!  null out the entry.  this will go back to our default
  1431.                     RegDeleteValue(hk, pfi->pszValueName);
  1432.                     _ClearCacheEntry(pfi);
  1433.                 }
  1434.             }
  1435.             RegCloseKey(hk);
  1436.         }
  1437.     }
  1438.     else
  1439.     {
  1440.         RegSetFolderPath(pfi, TEXT("User Shell Folders"), NULL);
  1441.         // clear out any temp paths
  1442.         RegSetFolderPath(pfi, TEXT("User Shell Folders\New"), NULL);
  1443.     }
  1444.     
  1445.     // set the global different from the per process variable
  1446.     // to signal an update needs to happen other processes
  1447.     g_lPerProcessCount = SHGlobalCounterIncrement(g_hCounter);
  1448. }
  1449. // file system change notifies come in here AFTER the folders have been moved/deleted
  1450. // we fix up the registry to match what occured in the file system
  1451. void SFP_FSEvent(LONG lEvent, LPITEMIDLIST pidl, LPITEMIDLIST pidlExtra)
  1452. {
  1453.     const FOLDER_INFO *pfi;
  1454.     TCHAR szSrc[MAX_PATH];
  1455.     if (!(lEvent & (SHCNE_RENAMEFOLDER | SHCNE_RMDIR | SHCNE_MKDIR)) ||
  1456.         !SHGetPathFromIDList(pidl, szSrc)                            ||
  1457.         (pidlExtra && ILIsEqual(pidl, pidlExtra)))  // when volume label changes, pidl==pidlExtra so we detect this case and skip it for perf
  1458.     {
  1459.         return;
  1460.     }
  1461.     for (pfi = c_rgFolderInfo; pfi->id != -1; pfi++)
  1462.     {
  1463.         if (0 == (pfi->dwFlags & (SDIF_NOT_TRACKED | SDIF_NOT_FILESYS)))
  1464.         {
  1465.             TCHAR szCurrent[MAX_PATH];
  1466.             if (S_OK == _GetFolderPathCached(NULL, pfi, NULL, CSIDL_FLAG_DONT_VERIFY, szCurrent) &&
  1467.                 PathIsEqualOrSubFolder(szSrc, szCurrent))
  1468.             {
  1469.                 TCHAR szDest[MAX_PATH];
  1470.                 szDest[0] = 0;
  1471.                 if (lEvent & SHCNE_RMDIR)
  1472.                 {
  1473.                     // complete the "move accross volume" case
  1474.                     HKEY hk;
  1475.                     if (SUCCEEDED(_OpenKeyForFolder(pfi, NULL, TEXT("User Shell Folders\New"), &hk)))
  1476.                     {
  1477.                         RegQueryPath(hk, pfi->pszValueName, szDest);
  1478.                         RegCloseKey(hk);
  1479.                     }
  1480.                 }
  1481.                 else if (pidlExtra)
  1482.                 {
  1483.                     SHGetPathFromIDList(pidlExtra, szDest);
  1484.                 }
  1485.                 if (szDest[0])
  1486.                 {
  1487.                     // rename the specal folder
  1488.                     UINT cch = PathCommonPrefix(szCurrent, szSrc, NULL);
  1489.                     ASSERT(cch != 0);
  1490.                     
  1491.                     if (szCurrent[cch])
  1492.                     {
  1493.                         PathAppend(szDest, szCurrent + cch);
  1494.                     }
  1495.                     SetFolderPath(pfi, szDest);
  1496.                 }
  1497.             }
  1498.         }
  1499.     }
  1500. }
  1501. // returns the first special folder CSIDL_ id that is a parent
  1502. // of the passed in pidl or 0 if not found. only CSIDL_ entries marked as
  1503. // SDIF_SHORTCUT_RELATIVE are considered for this.
  1504. //
  1505. // returns:
  1506. //      CSIDL_ values
  1507. //      *pcbOffset  offset into pidl
  1508. int GetSpecialFolderParentIDAndOffset(LPCITEMIDLIST pidl, ULONG *pcbOffset)
  1509. {
  1510.     BOOL bFound = FALSE;
  1511.     const FOLDER_INFO *pfi;
  1512.     for (pfi = c_rgFolderInfo; pfi->id != -1; pfi++)
  1513.     {
  1514.         if (pfi->dwFlags & SDIF_SHORTCUT_RELATIVE)
  1515.         {
  1516.             LPITEMIDLIST pidlFolder;
  1517.             if (S_OK == _GetFolderIDListCached(NULL, pfi, 0, &pidlFolder))
  1518.             {
  1519.                 BOOL bParent = ILIsParent(pidlFolder, pidl, FALSE);
  1520.                 if (bParent)
  1521.                 {
  1522.                     LPCITEMIDLIST pidlT = pidl, pidlFolderT = pidlFolder;
  1523.                     while (!ILIsEmpty(pidlFolderT))
  1524.                     {
  1525.                         pidlFolderT = _ILNext(pidlFolderT);
  1526.                         pidlT = _ILNext(pidlT);
  1527.                     }
  1528.                     *pcbOffset = (ULONG)((LPBYTE)pidlT - (LPBYTE)pidl);
  1529.                     bFound = TRUE;
  1530.                 }
  1531.                 ILFree(pidlFolder);
  1532.             }
  1533.             if (bFound)
  1534.                 return pfi->id;
  1535.         }
  1536.     }
  1537.     return 0;
  1538. }
  1539. // this is copied from mydocs.dll so that we can completely enforce its DisablePersonalDirChange
  1540. // policy by blocking rename/move even when mydocs.dll isn't loaded
  1541. BOOL CanChangePersonalPath( void )
  1542. {
  1543.     return (ERROR_SUCCESS != SHGetValue(HKEY_CURRENT_USER,
  1544.                                         TEXT("Software\Microsoft\Windows\CurrentVersion\Policies\Explorer"),
  1545.                                         TEXT("DisablePersonalDirChange"),
  1546.                                         NULL,
  1547.                                         NULL,
  1548.                                         NULL));
  1549. }
  1550. // this is called from the copy engine (like all other copy hooks)
  1551. // this is where we put up UI blocking the delete/move of some special folders
  1552. int PathCopyHookCallback(HWND hwnd, UINT wFunc, LPCTSTR pszSrc, LPCTSTR pszDest)
  1553. {
  1554.     int ret = IDYES;
  1555.     if ((wFunc == FO_DELETE) || (wFunc == FO_MOVE) || (wFunc == FO_RENAME))
  1556.     {
  1557.         const FOLDER_INFO *pfi;
  1558.         // is one of our system directories being affected?
  1559.         for (pfi = c_rgFolderInfo; ret == IDYES && pfi->id != -1; pfi++)
  1560.         {
  1561.             // even non tracked folders (windows, system) come through here
  1562.             if (0 == (pfi->dwFlags & SDIF_NOT_FILESYS))
  1563.             {
  1564.                 TCHAR szCurrent[MAX_PATH];
  1565.                 if (S_OK == _GetFolderPathCached(NULL, pfi, NULL, CSIDL_FLAG_DONT_VERIFY, szCurrent) &&
  1566.                     PathIsEqualOrSubFolder(pszSrc, szCurrent))
  1567.                 {
  1568.                     // Yes
  1569.                     if (wFunc == FO_DELETE)
  1570.                     {
  1571.                         if (pfi->dwFlags & SDIF_CAN_DELETE)
  1572.                         {
  1573.                             SetFolderPath(pfi, NULL);  // Let them delete some folders
  1574.                         }
  1575.                         else
  1576.                         {
  1577.                             ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_CANTDELETESPECIALDIR),
  1578.                                             MAKEINTRESOURCE(IDS_DELETE), MB_OK | MB_ICONINFORMATION, PathFindFileName(pszSrc));
  1579.                             ret = IDNO;
  1580.                         }
  1581.                     }
  1582.                     else
  1583.                     {
  1584.                         int idSrc = PathGetDriveNumber(pszSrc);
  1585.                         int idDest = PathGetDriveNumber(pszDest);
  1586.                         ASSERT((wFunc == FO_MOVE) || (wFunc == FO_RENAME));
  1587.                         if ((pfi->dwFlags & SDIF_CANT_MOVE_RENAME) || 
  1588.                             (((idSrc != -1) && (idDest == -1) && !(pfi->dwFlags & SDIF_NETWORKABLE)) ||
  1589.                              ((idSrc != idDest) && PathIsRemovable(pszDest) && !(pfi->dwFlags & SDIF_REMOVABLE))) ||
  1590.                               ((CSIDL_PERSONAL == pfi->id) && !CanChangePersonalPath()))
  1591.                         {
  1592.                             ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_CANTMOVESPECIALDIRHERE),
  1593.                                 wFunc == FO_MOVE ? MAKEINTRESOURCE(IDS_MOVE) : MAKEINTRESOURCE(IDS_RENAME), 
  1594.                                 MB_ICONERROR, PathFindFileName(pszSrc));
  1595.                             ret = IDNO;
  1596.                         }
  1597.                         else
  1598.                         {
  1599.                             // if this is a logical move that is really conveted into
  1600.                             // a copy we detect that here. 2 cases, cross volume
  1601.                             if ((idSrc != idDest) || PathIsDirectory(pszDest))
  1602.                             {
  1603.                                 // this is going to be a move across volumes
  1604.                                 // which means a delete then a create notifications,
  1605.                                 // store this info here
  1606.                                 RegSetFolderPath(pfi, TEXT("User Shell Folders\New"), pszDest);
  1607.                             }
  1608.                         }
  1609.                     }
  1610.                 }
  1611.             }
  1612.         }
  1613.     }
  1614.     return ret;
  1615. }
  1616. STDAPI_(int) SHGetSpecialFolderID(LPCWSTR pszName)
  1617. {
  1618.     const FOLDER_INFO *pfi;
  1619.     USES_CONVERSION;
  1620.     LPCTSTR szPath = W2CT(pszName);
  1621.     // make sure g_aFolderCache can be indexed by the CSIDL values
  1622.     COMPILETIME_ASSERT((ARRAYSIZE(g_aFolderCache) - 1) == CSIDL_CONNECTIONS);
  1623.     for (pfi = c_rgFolderInfo; pfi->id != -1; pfi++)
  1624.     {
  1625.         if (0 == StrCmpI(szPath, pfi->pszValueName))
  1626.             return pfi->id;
  1627.     }
  1628.     return -1;
  1629. }
  1630. // Return the special folder ID, if this folder is one of them.
  1631. // At this point, we handle PROGRAMS folder only.
  1632. //
  1633. //  GetSpecialFolderID() 
  1634. //  this allows a list of CSIDLs to be passed in.
  1635. //  they will be searched in order for the specified csidl
  1636. //  and the path will be checked against it.
  1637. //  if -1 is specified as the csidl, then all of array entries should
  1638. //  be checked for a match with the folder.
  1639. //
  1640. int GetSpecialFolderID(LPCTSTR pszFolder, const int *rgcsidl, UINT count)
  1641. {
  1642.     UINT i;
  1643.     for (i = 0; i < count; i++)
  1644.     {
  1645.         int csidlSpecial = rgcsidl[i] & ~TEST_SUBFOLDER;
  1646.         TCHAR szPath[MAX_PATH];
  1647.         if (S_OK == SHGetFolderPath(NULL, csidlSpecial | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szPath))
  1648.         {
  1649.             if (((rgcsidl[i] & TEST_SUBFOLDER) && PathIsEqualOrSubFolder(szPath, pszFolder)) ||
  1650.                 (lstrcmpi(szPath, pszFolder) == 0))
  1651.             {
  1652.                 return csidlSpecial;
  1653.             }
  1654.         }
  1655.     }
  1656.     return -1;
  1657. }