Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2763 lines
99 KiB

  1. #include "shellprv.h"
  2. #include "apithk.h"
  3. #include "folder.h"
  4. #include "ids.h"
  5. #include "deskfldr.h"
  6. #include <winnls.h>
  7. #include "shitemid.h"
  8. #include "sddl.h"
  9. #ifdef _WIN64
  10. #include <wow64t.h>
  11. #endif
  12. #include "filefldr.h"
  13. #include "lmcons.h"
  14. #include "netview.h"
  15. //---------------------------------------------------------------------------
  16. // Get the path for the CSIDL_ folders and optionally create it if it
  17. // doesn't exist.
  18. //
  19. // Returns FALSE if the special folder given isn't one of those above or the
  20. // directory couldn't be created.
  21. // By default all the special folders are in the windows directory.
  22. // This can be overidden by a [.Shell Folders] section in win.ini with
  23. // entries like Desktop = c:\stuff\desktop
  24. // This in turn can be overidden by a "per user" section in win.ini eg
  25. // [Shell Folder Ianel] - the user name for this section is the current
  26. // network user name, if this fails the default network user name is used
  27. // and if this fails the name given at setup time is used.
  28. //
  29. // "Shell Folders" is the key that records all the absolute paths to the
  30. // shell folders. The values there are always supposed to be present.
  31. //
  32. // "User Shell Folders" is the key where the user's modifications from
  33. // the defaults are stored.
  34. //
  35. // When we need to find the location of a path, we look in "User Shell Folders"
  36. // first, and if that's not there, generate the default path. In either
  37. // case we then write the absolute path under "Shell Folders" for other
  38. // apps to look at. This is so that HKEY_CURRENT_USER can be propagated
  39. // to a machine with Windows installed in a different directory, and as
  40. // long as the user hasn't changed the setting, they won't have the other
  41. // Windows directory hard-coded in the registry.
  42. // -- gregj, 11/10/94
  43. typedef enum {
  44. SDIF_NONE = 0,
  45. SDIF_CREATE_IN_ROOT = 0x00000001, // create in root (not in profiles dir)
  46. SDIF_CREATE_IN_WINDIR = 0x00000002, // create in the windows dir (not in profiles dir)
  47. SDIF_CREATE_IN_ALLUSERS = 0x00000003, // create in "All Users" folder (not in profiles dir)
  48. SDIF_CREATE_IN_MYDOCUMENTS = 0x00000004, // create in CSIDL_PERSONAL folder
  49. SDIF_CREATE_IN_LOCALSET = 0x00000005, // create in <user>\Local Settings folder
  50. SDIF_CREATE_IN_MASK = 0x0000000F, // mask for above values
  51. SDIF_CAN_DELETE = 0x00000010,
  52. SDIF_SHORTCUT_RELATIVE = 0x00000020, // make shortcuts relative to this folder
  53. SDIF_HIDE = 0x00000040, // hide these when we create them
  54. SDIF_EMPTY_IF_NOT_IN_REG = 0x00000080, // does not exist if nothing in the registry
  55. SDIF_NOT_FILESYS = 0x00000100, // not a file system folder
  56. SDIF_NOT_TRACKED = 0x00000200, // don't track this, it can't change
  57. SDIF_CONST_IDLIST = 0x00000400, // don't alloc or free this
  58. SDIF_REMOVABLE = 0x00000800, // Can exist on removable media
  59. SDIF_CANT_MOVE_RENAME = 0x00001000, // can't move or rename this
  60. SDIF_WX86 = 0x00002000, // do Wx86 thunking
  61. SDIF_NETWORKABLE = 0x00004000, // Can be moved to the net
  62. SDIF_MAYBE_ALIASED = 0x00008000, // could have an alias representation
  63. SDIF_PERSONALIZED = 0x00010000, // resource name is to be personalized
  64. SDIF_POLICY_NO_MOVE = 0x00020000, // policy blocks move
  65. } ;
  66. typedef DWORD FOLDER_FLAGS;
  67. typedef void (*FOLDER_CREATE_PROC)(int id, LPCTSTR pszPath);
  68. void _InitMyPictures(int id, LPCTSTR pszPath);
  69. void _InitMyMusic(int id, LPCTSTR pszPath);
  70. void _InitMyVideos(int id, LPCTSTR pszPath);
  71. void _InitPerUserMyMusic(int id, LPCTSTR pszPath);
  72. void _InitPerUserMyPictures(int id, LPCTSTR pszPath);
  73. void _InitRecentDocs(int id, LPCTSTR pszPath);
  74. void _InitFavorites(int id, LPCTSTR pszPath);
  75. typedef struct {
  76. int id; // CSIDL_ value
  77. int idsDefault; // string id of default folder name name
  78. LPCTSTR pszValueName; // reg key (not localized)
  79. HKEY hKey; // HKCU or HKLM (Current User or Local Machine)
  80. FOLDER_FLAGS dwFlags;
  81. FOLDER_CREATE_PROC pfnInit;
  82. INT idsLocalizedName;
  83. } FOLDER_INFO;
  84. // typical entry
  85. #define FOLDER(csidl, ids, value, key, ff) \
  86. { csidl, ids, value, key, ff, NULL, 0}
  87. // FIXEDFOLDER entries must have be marked SDIF_CONST_IDLIST
  88. // or have code in _GetFolderDefaultPath() to create their path
  89. // if they have a filesys path
  90. #define FIXEDFOLDER(csidl, value, ff) \
  91. { csidl, 0, value, NULL, ff, NULL, 0}
  92. // PROCFOLDER's have a FOLDER_CREATE_PROC pfn that gets
  93. // run in _PostCreateStuff()
  94. #define PROCFOLDER(csidl, ids, value, key, ff, proc, idsLocal) \
  95. {csidl, ids, value, key, ff, proc, idsLocal}
  96. // folder that needs SHSetLocalizedName() in _PostCreateStuff()
  97. #define LOCALFOLDER(csidl, ids, value, key, ff, idsLocal) \
  98. {csidl, ids, value, key, ff, NULL, idsLocal}
  99. const FOLDER_INFO c_rgFolderInfo[] =
  100. {
  101. FOLDER( CSIDL_DESKTOP,
  102. IDS_CSIDL_DESKTOPDIRECTORY,
  103. TEXT("DesktopFolder"),
  104. NULL,
  105. SDIF_NOT_TRACKED | SDIF_CONST_IDLIST),
  106. FIXEDFOLDER( CSIDL_NETWORK,
  107. TEXT("NetworkFolder"),
  108. SDIF_NOT_TRACKED | SDIF_NOT_FILESYS | SDIF_CONST_IDLIST),
  109. FIXEDFOLDER( CSIDL_DRIVES,
  110. TEXT("DriveFolder"),
  111. SDIF_NOT_TRACKED | SDIF_NOT_FILESYS | SDIF_CONST_IDLIST),
  112. FIXEDFOLDER( CSIDL_INTERNET,
  113. TEXT("InternetFolder"),
  114. SDIF_NOT_TRACKED | SDIF_NOT_FILESYS | SDIF_CONST_IDLIST),
  115. FIXEDFOLDER( CSIDL_CONTROLS,
  116. TEXT("ControlPanelFolder"),
  117. SDIF_NOT_TRACKED | SDIF_NOT_FILESYS | SDIF_CONST_IDLIST),
  118. FIXEDFOLDER( CSIDL_PRINTERS,
  119. TEXT("PrintersFolder"),
  120. SDIF_NOT_TRACKED | SDIF_NOT_FILESYS | SDIF_CONST_IDLIST),
  121. FIXEDFOLDER( CSIDL_BITBUCKET,
  122. TEXT("RecycleBinFolder"),
  123. SDIF_NOT_TRACKED | SDIF_NOT_FILESYS | SDIF_CONST_IDLIST),
  124. FIXEDFOLDER( CSIDL_CONNECTIONS,
  125. TEXT("ConnectionsFolder"),
  126. SDIF_NOT_TRACKED | SDIF_NOT_FILESYS | SDIF_CONST_IDLIST),
  127. FOLDER( CSIDL_FONTS,
  128. 0,
  129. TEXT("Fonts"),
  130. HKEY_CURRENT_USER,
  131. SDIF_NOT_TRACKED | SDIF_CREATE_IN_WINDIR | SDIF_CANT_MOVE_RENAME),
  132. FOLDER( CSIDL_DESKTOPDIRECTORY,
  133. IDS_CSIDL_DESKTOPDIRECTORY,
  134. TEXT("Desktop"),
  135. HKEY_CURRENT_USER, SDIF_SHORTCUT_RELATIVE),
  136. // _STARTUP is a subfolder of _PROGRAMS is a subfolder of _STARTMENU -- keep that order
  137. FOLDER( CSIDL_STARTUP,
  138. IDS_CSIDL_STARTUP,
  139. TEXT("Startup"),
  140. HKEY_CURRENT_USER, SDIF_NONE),
  141. FOLDER( CSIDL_PROGRAMS,
  142. IDS_CSIDL_PROGRAMS,
  143. TEXT("Programs"),
  144. HKEY_CURRENT_USER,
  145. SDIF_NONE),
  146. FOLDER( CSIDL_STARTMENU,
  147. IDS_CSIDL_STARTMENU,
  148. TEXT("Start Menu"),
  149. HKEY_CURRENT_USER,
  150. SDIF_SHORTCUT_RELATIVE),
  151. PROCFOLDER( CSIDL_RECENT,
  152. IDS_CSIDL_RECENT,
  153. TEXT("Recent"),
  154. HKEY_CURRENT_USER,
  155. SDIF_HIDE | SDIF_CANT_MOVE_RENAME | SDIF_CAN_DELETE,
  156. _InitRecentDocs,
  157. IDS_FOLDER_RECENTDOCS),
  158. FOLDER( CSIDL_SENDTO,
  159. IDS_CSIDL_SENDTO,
  160. TEXT("SendTo"),
  161. HKEY_CURRENT_USER,
  162. SDIF_HIDE),
  163. FOLDER( CSIDL_PERSONAL,
  164. IDS_CSIDL_PERSONAL,
  165. TEXT("Personal"),
  166. HKEY_CURRENT_USER,
  167. SDIF_SHORTCUT_RELATIVE | SDIF_NETWORKABLE | SDIF_REMOVABLE | SDIF_CONST_IDLIST | SDIF_MAYBE_ALIASED | SDIF_PERSONALIZED | SDIF_POLICY_NO_MOVE),
  168. PROCFOLDER( CSIDL_FAVORITES,
  169. IDS_CSIDL_FAVORITES,
  170. TEXT("Favorites"),
  171. HKEY_CURRENT_USER,
  172. SDIF_POLICY_NO_MOVE,
  173. _InitFavorites,
  174. IDS_FOLDER_FAVORITES),
  175. FOLDER( CSIDL_NETHOOD,
  176. IDS_CSIDL_NETHOOD,
  177. TEXT("NetHood"),
  178. HKEY_CURRENT_USER,
  179. SDIF_HIDE),
  180. FOLDER( CSIDL_PRINTHOOD,
  181. IDS_CSIDL_PRINTHOOD,
  182. TEXT("PrintHood"),
  183. HKEY_CURRENT_USER,
  184. SDIF_HIDE),
  185. FOLDER( CSIDL_TEMPLATES,
  186. IDS_CSIDL_TEMPLATES,
  187. TEXT("Templates"),
  188. HKEY_CURRENT_USER,
  189. SDIF_HIDE),
  190. // Common special folders
  191. // _STARTUP is a subfolder of _PROGRAMS is a subfolder of _STARTMENU -- keep that order
  192. FOLDER( CSIDL_COMMON_STARTUP,
  193. IDS_CSIDL_STARTUP,
  194. TEXT("Common Startup"),
  195. HKEY_LOCAL_MACHINE,
  196. SDIF_CREATE_IN_ALLUSERS | SDIF_CANT_MOVE_RENAME | SDIF_EMPTY_IF_NOT_IN_REG),
  197. FOLDER( CSIDL_COMMON_PROGRAMS,
  198. IDS_CSIDL_PROGRAMS,
  199. TEXT("Common Programs"),
  200. HKEY_LOCAL_MACHINE,
  201. SDIF_CREATE_IN_ALLUSERS | SDIF_EMPTY_IF_NOT_IN_REG),
  202. FOLDER( CSIDL_COMMON_STARTMENU,
  203. IDS_CSIDL_STARTMENU,
  204. TEXT("Common Start Menu"),
  205. HKEY_LOCAL_MACHINE,
  206. SDIF_SHORTCUT_RELATIVE | SDIF_CREATE_IN_ALLUSERS | SDIF_EMPTY_IF_NOT_IN_REG),
  207. FOLDER( CSIDL_COMMON_DESKTOPDIRECTORY,
  208. IDS_CSIDL_DESKTOPDIRECTORY,
  209. TEXT("Common Desktop"),
  210. HKEY_LOCAL_MACHINE,
  211. SDIF_SHORTCUT_RELATIVE | SDIF_CREATE_IN_ALLUSERS),
  212. FOLDER( CSIDL_COMMON_FAVORITES,
  213. IDS_CSIDL_FAVORITES,
  214. TEXT("Common Favorites"),
  215. HKEY_LOCAL_MACHINE,
  216. SDIF_CREATE_IN_ALLUSERS),
  217. FOLDER( CSIDL_COMMON_APPDATA,
  218. IDS_CSIDL_APPDATA,
  219. TEXT("Common AppData"),
  220. HKEY_LOCAL_MACHINE,
  221. SDIF_SHORTCUT_RELATIVE | SDIF_CREATE_IN_ALLUSERS),
  222. FOLDER( CSIDL_COMMON_TEMPLATES,
  223. IDS_CSIDL_TEMPLATES,
  224. TEXT("Common Templates"),
  225. HKEY_LOCAL_MACHINE,
  226. SDIF_NOT_TRACKED | SDIF_CAN_DELETE | SDIF_CREATE_IN_ALLUSERS),
  227. LOCALFOLDER( CSIDL_COMMON_DOCUMENTS,
  228. IDS_CSIDL_ALLUSERS_DOCUMENTS,
  229. TEXT("Common Documents"),
  230. HKEY_LOCAL_MACHINE,
  231. SDIF_NOT_TRACKED | SDIF_CANT_MOVE_RENAME | SDIF_MAYBE_ALIASED | SDIF_CREATE_IN_ALLUSERS,
  232. IDS_LOCALGDN_FLD_SHARED_DOC),
  233. // Application Data special folder
  234. FOLDER( CSIDL_APPDATA,
  235. IDS_CSIDL_APPDATA,
  236. TEXT("AppData"),
  237. HKEY_CURRENT_USER, SDIF_SHORTCUT_RELATIVE),
  238. FOLDER( CSIDL_LOCAL_APPDATA,
  239. IDS_CSIDL_APPDATA,
  240. TEXT("Local AppData"),
  241. HKEY_CURRENT_USER, SDIF_CREATE_IN_LOCALSET),
  242. // Non-localized startup folder (do not localize this folder name)
  243. FOLDER( CSIDL_ALTSTARTUP,
  244. IDS_CSIDL_ALTSTARTUP,
  245. TEXT("AltStartup"),
  246. HKEY_CURRENT_USER,
  247. SDIF_EMPTY_IF_NOT_IN_REG),
  248. // Non-localized Common StartUp group (do not localize this folde name)
  249. FOLDER( CSIDL_COMMON_ALTSTARTUP,
  250. IDS_CSIDL_ALTSTARTUP,
  251. TEXT("Common AltStartup"),
  252. HKEY_LOCAL_MACHINE,
  253. SDIF_EMPTY_IF_NOT_IN_REG | SDIF_CREATE_IN_ALLUSERS),
  254. // Per-user Internet-related folders
  255. FOLDER( CSIDL_INTERNET_CACHE,
  256. IDS_CSIDL_CACHE,
  257. TEXT("Cache"),
  258. HKEY_CURRENT_USER,
  259. SDIF_CREATE_IN_LOCALSET),
  260. FOLDER( CSIDL_COOKIES,
  261. IDS_CSIDL_COOKIES,
  262. TEXT("Cookies"),
  263. HKEY_CURRENT_USER,
  264. SDIF_NONE),
  265. FOLDER( CSIDL_HISTORY,
  266. IDS_CSIDL_HISTORY,
  267. TEXT("History"),
  268. HKEY_CURRENT_USER,
  269. SDIF_CREATE_IN_LOCALSET),
  270. FIXEDFOLDER( CSIDL_SYSTEM,
  271. TEXT("System"),
  272. SDIF_NOT_TRACKED | SDIF_CANT_MOVE_RENAME | SDIF_SHORTCUT_RELATIVE),
  273. FIXEDFOLDER( CSIDL_SYSTEMX86,
  274. TEXT("SystemX86"),
  275. SDIF_NOT_TRACKED | SDIF_CANT_MOVE_RENAME | SDIF_WX86 | SDIF_SHORTCUT_RELATIVE),
  276. FIXEDFOLDER( CSIDL_WINDOWS,
  277. TEXT("Windows"),
  278. SDIF_NOT_TRACKED | SDIF_SHORTCUT_RELATIVE | SDIF_CANT_MOVE_RENAME),
  279. FIXEDFOLDER( CSIDL_PROFILE,
  280. TEXT("Profile"),
  281. SDIF_NOT_TRACKED | SDIF_CANT_MOVE_RENAME),
  282. PROCFOLDER( CSIDL_MYPICTURES,
  283. IDS_CSIDL_MYPICTURES,
  284. TEXT("My Pictures"),
  285. HKEY_CURRENT_USER,
  286. SDIF_CAN_DELETE | SDIF_NETWORKABLE | SDIF_REMOVABLE | SDIF_CREATE_IN_MYDOCUMENTS | SDIF_SHORTCUT_RELATIVE | SDIF_MAYBE_ALIASED | SDIF_PERSONALIZED | SDIF_POLICY_NO_MOVE,
  287. _InitPerUserMyPictures,
  288. 0),
  289. //
  290. // CSIDL_PROGRAM_FILES must come after CSIDL_PROGRAM_FILESX86 so that shell links for x86 apps
  291. // work correctly on non-x86 platforms.
  292. // Example: On IA64 a 32-bit app creates a shortcut via IShellLink to the Program
  293. // Files directory. A WOW64 registry hive maps "Program Files" to "Program Files (x86)". The shell
  294. // link code then tries to abstract the special folder part of the path by mapping to one of the
  295. // entries in this table. Since CSIDL_PROGRAM_FILES and CSIDL_PROGRAM_FILESX86 are the same it
  296. // will map to the one that appears first in this table. When the shortcut is accessed in
  297. // 64-bit mode the cidls are no longer the same. If CSIDL_PROGRAM_FILES was used instead of
  298. // CSIDL_PROGRAM_FILESX86 the shortcut will be broken.
  299. #ifdef WX86
  300. FIXEDFOLDER( CSIDL_PROGRAM_FILESX86,
  301. TEXT("ProgramFilesX86"),
  302. SDIF_NOT_TRACKED | SDIF_CAN_DELETE | SDIF_SHORTCUT_RELATIVE|SDIF_WX86),
  303. FIXEDFOLDER( CSIDL_PROGRAM_FILES_COMMONX86,
  304. TEXT("CommonProgramFilesX86"),
  305. SDIF_NOT_TRACKED | SDIF_CAN_DELETE | SDIF_WX86),
  306. #else
  307. FIXEDFOLDER( CSIDL_PROGRAM_FILESX86,
  308. TEXT("ProgramFilesX86"),
  309. SDIF_NOT_TRACKED | SDIF_CAN_DELETE | SDIF_SHORTCUT_RELATIVE),
  310. FIXEDFOLDER( CSIDL_PROGRAM_FILES_COMMONX86,
  311. TEXT("CommonProgramFilesX86"),
  312. SDIF_NOT_TRACKED | SDIF_CAN_DELETE),
  313. #endif
  314. // CSIDL_PROGRAM_FILES must come after CSIDL_PROGRAM_FILESX86. See comment above.
  315. FIXEDFOLDER( CSIDL_PROGRAM_FILES,
  316. TEXT("ProgramFiles"),
  317. SDIF_NOT_TRACKED | SDIF_CAN_DELETE | SDIF_SHORTCUT_RELATIVE),
  318. FIXEDFOLDER( CSIDL_PROGRAM_FILES_COMMON,
  319. TEXT("CommonProgramFiles"),
  320. SDIF_NOT_TRACKED | SDIF_CAN_DELETE),
  321. LOCALFOLDER( CSIDL_ADMINTOOLS,
  322. IDS_CSIDL_ADMINTOOLS,
  323. TEXT("Administrative Tools"),
  324. HKEY_CURRENT_USER,
  325. SDIF_NONE,
  326. IDS_LOCALGDN_FLD_ADMIN_TOOLS),
  327. LOCALFOLDER( CSIDL_COMMON_ADMINTOOLS,
  328. IDS_CSIDL_ADMINTOOLS,
  329. TEXT("Common Administrative Tools"),
  330. HKEY_LOCAL_MACHINE,
  331. SDIF_CREATE_IN_ALLUSERS,
  332. IDS_LOCALGDN_FLD_ADMIN_TOOLS),
  333. PROCFOLDER( CSIDL_MYMUSIC,
  334. IDS_CSIDL_MYMUSIC,
  335. TEXT("My Music"),
  336. HKEY_CURRENT_USER,
  337. SDIF_CAN_DELETE | SDIF_NETWORKABLE | SDIF_REMOVABLE | SDIF_CREATE_IN_MYDOCUMENTS | SDIF_MAYBE_ALIASED | SDIF_PERSONALIZED | SDIF_POLICY_NO_MOVE,
  338. _InitPerUserMyMusic,
  339. 0),
  340. PROCFOLDER( CSIDL_MYVIDEO,
  341. IDS_CSIDL_MYVIDEO,
  342. TEXT("My Video"),
  343. HKEY_CURRENT_USER,
  344. SDIF_CAN_DELETE | SDIF_NETWORKABLE | SDIF_REMOVABLE | SDIF_CREATE_IN_MYDOCUMENTS | SDIF_MAYBE_ALIASED | SDIF_PERSONALIZED | SDIF_POLICY_NO_MOVE,
  345. _InitMyVideos,
  346. 0),
  347. PROCFOLDER( CSIDL_COMMON_PICTURES,
  348. IDS_CSIDL_ALLUSERS_PICTURES,
  349. TEXT("CommonPictures"),
  350. HKEY_LOCAL_MACHINE,
  351. SDIF_SHORTCUT_RELATIVE | SDIF_CANT_MOVE_RENAME | SDIF_CAN_DELETE | SDIF_MAYBE_ALIASED | SDIF_CREATE_IN_ALLUSERS,
  352. _InitMyPictures,
  353. IDS_SHAREDPICTURES),
  354. PROCFOLDER( CSIDL_COMMON_MUSIC,
  355. IDS_CSIDL_ALLUSERS_MUSIC,
  356. TEXT("CommonMusic"),
  357. HKEY_LOCAL_MACHINE,
  358. SDIF_SHORTCUT_RELATIVE | SDIF_CANT_MOVE_RENAME | SDIF_CAN_DELETE | SDIF_MAYBE_ALIASED | SDIF_CREATE_IN_ALLUSERS,
  359. _InitMyMusic,
  360. IDS_SHAREDMUSIC),
  361. PROCFOLDER( CSIDL_COMMON_VIDEO,
  362. IDS_CSIDL_ALLUSERS_VIDEO,
  363. TEXT("CommonVideo"),
  364. HKEY_LOCAL_MACHINE,
  365. SDIF_SHORTCUT_RELATIVE | SDIF_CANT_MOVE_RENAME | SDIF_CAN_DELETE | SDIF_MAYBE_ALIASED | SDIF_CREATE_IN_ALLUSERS,
  366. _InitMyVideos,
  367. IDS_SHAREDVIDEO),
  368. FIXEDFOLDER( CSIDL_RESOURCES,
  369. TEXT("ResourceDir"),
  370. SDIF_NOT_TRACKED),
  371. FIXEDFOLDER( CSIDL_RESOURCES_LOCALIZED,
  372. TEXT("LocalizedResourcesDir"),
  373. SDIF_NOT_TRACKED),
  374. FOLDER( CSIDL_COMMON_OEM_LINKS,
  375. IDS_CSIDL_ALLUSERS_OEM_LINKS,
  376. TEXT("OEM Links"),
  377. HKEY_LOCAL_MACHINE,
  378. SDIF_CAN_DELETE | SDIF_CREATE_IN_ALLUSERS | SDIF_EMPTY_IF_NOT_IN_REG),
  379. FOLDER( CSIDL_CDBURN_AREA,
  380. IDS_CSIDL_CDBURN_AREA,
  381. TEXT("CD Burning"),
  382. HKEY_CURRENT_USER,
  383. SDIF_CAN_DELETE | SDIF_CREATE_IN_LOCALSET),
  384. FIXEDFOLDER( CSIDL_COMPUTERSNEARME,
  385. TEXT("ComputersNearMe"),
  386. SDIF_NONE),
  387. FIXEDFOLDER(-1, NULL, SDIF_NONE)
  388. };
  389. EXTERN_C const IDLREGITEM c_idlMyDocs =
  390. {
  391. {sizeof(IDREGITEM), SHID_ROOT_REGITEM, SORT_ORDER_MYDOCS,
  392. { 0x450d8fba, 0xad25, 0x11d0, 0x98,0xa8,0x08,0x00,0x36,0x1b,0x11,0x03, },}, // CLSID_MyDocuments
  393. 0,
  394. } ;
  395. EXTERN_C const IDREGITEM c_idlPrinters[] =
  396. {
  397. {sizeof(IDREGITEM), SHID_ROOT_REGITEM, SORT_ORDER_DRIVES,
  398. { 0x20D04FE0, 0x3AEA, 0x1069, 0xA2,0xD8,0x08,0x00,0x2B,0x30,0x30,0x9D, },}, // CLSID_MyComputer
  399. {sizeof(IDREGITEM), SHID_COMPUTER_REGITEM, 0,
  400. { 0x21EC2020, 0x3AEA, 0x1069, 0xA2,0xDD,0x08,0x00,0x2B,0x30,0x30,0x9D, },}, // CLSID_ControlPanel
  401. {sizeof(IDREGITEM), SHID_CONTROLPANEL_REGITEM, 0,
  402. { 0x2227A280, 0x3AEA, 0x1069, 0xA2, 0xDE, 0x08, 0x00, 0x2B, 0x30, 0x30, 0x9D, },}, // CLSID_Printers
  403. 0,
  404. } ;
  405. EXTERN_C const IDREGITEM c_idlControls[] =
  406. {
  407. {sizeof(IDREGITEM), SHID_ROOT_REGITEM, SORT_ORDER_DRIVES,
  408. { 0x20D04FE0, 0x3AEA, 0x1069, 0xA2,0xD8,0x08,0x00,0x2B,0x30,0x30,0x9D, },}, // CLSID_MyComputer
  409. {sizeof(IDREGITEM), SHID_COMPUTER_REGITEM, 0,
  410. { 0x21EC2020, 0x3AEA, 0x1069, 0xA2,0xDD,0x08,0x00,0x2B,0x30,0x30,0x9D, },}, // CLSID_ControlPanel
  411. 0,
  412. } ;
  413. EXTERN_C const IDLREGITEM c_idlBitBucket =
  414. {
  415. {sizeof(IDREGITEM), SHID_ROOT_REGITEM, SORT_ORDER_RECYCLEBIN,
  416. { 0x645FF040, 0x5081, 0x101B, 0x9F, 0x08, 0x00, 0xAA, 0x00, 0x2F, 0x95, 0x4E, },}, // CLSID_RecycleBin
  417. 0,
  418. } ;
  419. // this array holds a cache of the values of these folders. this cache can only
  420. // be used in the hToken == NULL case otherwise we would need a per user version
  421. // of this cache.
  422. #define SFENTRY(x) { (LPTSTR)-1, (LPITEMIDLIST)x , (LPITEMIDLIST)-1}
  423. EXTERN_C const IDREGITEM c_aidlConnections[];
  424. struct {
  425. LPTSTR psz;
  426. LPITEMIDLIST pidl;
  427. LPITEMIDLIST pidlNonAlias;
  428. } g_aFolderCache[] = {
  429. SFENTRY(&c_idlDesktop), // CSIDL_DESKTOP (0x0000)
  430. SFENTRY(&c_idlInetRoot), // CSIDL_INTERNET (0x0001)
  431. SFENTRY(-1), // CSIDL_PROGRAMS (0x0002)
  432. SFENTRY(&c_idlControls), // CSIDL_CONTROLS (0x0003)
  433. SFENTRY(&c_idlPrinters), // CSIDL_PRINTERS (0x0004)
  434. SFENTRY(&c_idlMyDocs), // CSIDL_PERSONAL (0x0005)
  435. SFENTRY(-1), // CSIDL_FAVORITES (0x0006)
  436. SFENTRY(-1), // CSIDL_STARTUP (0x0007)
  437. SFENTRY(-1), // CSIDL_RECENT (0x0008)
  438. SFENTRY(-1), // CSIDL_SENDTO (0x0009)
  439. SFENTRY(&c_idlBitBucket), // CSIDL_BITBUCKET (0x000a)
  440. SFENTRY(-1), // CSIDL_STARTMENU (0x000b)
  441. SFENTRY(-1), // CSIDL_MYDOCUMENTS (0x000c)
  442. SFENTRY(-1), // CSIDL_MYMUSIC (0x000d)
  443. SFENTRY(-1), // CSIDL_MYVIDEO (0x000e)
  444. SFENTRY(-1), // <unused> (0x000f)
  445. SFENTRY(-1), // CSIDL_DESKTOPDIRECTORY (0x0010)
  446. SFENTRY(&c_idlDrives), // CSIDL_DRIVES (0x0011)
  447. SFENTRY(&c_idlNet), // CSIDL_NETWORK (0x0012)
  448. SFENTRY(-1), // CSIDL_NETHOOD (0x0013)
  449. SFENTRY(-1), // CSIDL_FONTS (0x0014)
  450. SFENTRY(-1), // CSIDL_TEMPLATES (0x0015)
  451. SFENTRY(-1), // CSIDL_COMMON_STARTMENU (0x0016)
  452. SFENTRY(-1), // CSIDL_COMMON_PROGRAMS (0X0017)
  453. SFENTRY(-1), // CSIDL_COMMON_STARTUP (0x0018)
  454. SFENTRY(-1), // CSIDL_COMMON_DESKTOPDIRECTORY (0x0019)
  455. SFENTRY(-1), // CSIDL_APPDATA (0x001a)
  456. SFENTRY(-1), // CSIDL_PRINTHOOD (0x001b)
  457. SFENTRY(-1), // CSIDL_LOCAL_APPDATA (0x001c)
  458. SFENTRY(-1), // CSIDL_ALTSTARTUP (0x001d)
  459. SFENTRY(-1), // CSIDL_COMMON_ALTSTARTUP (0x001e)
  460. SFENTRY(-1), // CSIDL_COMMON_FAVORITES (0x001f)
  461. SFENTRY(-1), // CSIDL_INTERNET_CACHE (0x0020)
  462. SFENTRY(-1), // CSIDL_COOKIES (0x0021)
  463. SFENTRY(-1), // CSIDL_HISTORY (0x0022)
  464. SFENTRY(-1), // CSIDL_COMMON_APPDATA (0x0023)
  465. SFENTRY(-1), // CSIDL_WINDOWS (0x0024)
  466. SFENTRY(-1), // CSIDL_SYSTEM (0x0025)
  467. SFENTRY(-1), // CSIDL_PROGRAM_FILES (0x0026)
  468. SFENTRY(-1), // CSIDL_MYPICTURES (0x0027)
  469. SFENTRY(-1), // CSIDL_PROFILE (0x0028)
  470. SFENTRY(-1), // CSIDL_SYSTEMX86 (0x0029)
  471. SFENTRY(-1), // CSIDL_PROGRAM_FILESX86 (0x002a)
  472. SFENTRY(-1), // CSIDL_PROGRAM_FILES_COMMON (0x002b)
  473. SFENTRY(-1), // CSIDL_PROGRAM_FILES_COMMONX86 (0x002c)
  474. SFENTRY(-1), // CSIDL_COMMON_TEMPLATES (0x002d)
  475. SFENTRY(-1), // CSIDL_COMMON_DOCUMENTS (0x002e)
  476. SFENTRY(-1), // CSIDL_COMMON_ADMINTOOLS (0x002f)
  477. SFENTRY(-1), // CSIDL_ADMINTOOLS (0x0030)
  478. SFENTRY(c_aidlConnections), // CSIDL_CONNECTIONS (0x0031)
  479. SFENTRY(-1), // (0x0032)
  480. SFENTRY(-1), // (0x0033)
  481. SFENTRY(-1), // (0x0034)
  482. SFENTRY(-1), // CSIDL_COMMON_MUSIC (0x0035)
  483. SFENTRY(-1), // CSIDL_COMMON_PICTURES (0x0036)
  484. SFENTRY(-1), // CSIDL_COMMON_VIDEO (0x0037)
  485. SFENTRY(-1), // CSIDL_RESOURCES (0x0038)
  486. SFENTRY(-1), // CSIDL_RESOURCES_LOCALIZED (0x0039)
  487. SFENTRY(-1), // CSIDL_COMMON_OEM_LINKS (0x003a)
  488. SFENTRY(-1), // CSIDL_CDBURN_AREA (0x003b)
  489. SFENTRY(-1), // <unused> (0x003c)
  490. SFENTRY(-1), // CSIDL_COMPUTERSNEARME (0x003d)
  491. };
  492. HRESULT _OpenKeyForFolder(const FOLDER_INFO *pfi, HANDLE hToken, LPCTSTR pszSubKey, HKEY *phkey);
  493. void _UpdateShellFolderCache(void);
  494. BOOL GetUserProfileDir(HANDLE hToken, TCHAR *pszPath, UINT cchPath);
  495. HRESULT VerifyAndCreateFolder(HWND hwnd, const FOLDER_INFO *pfi, UINT uFlags, LPTSTR pszPath) ;
  496. #define _IsDefaultUserToken(hToken) ((HANDLE)-1 == hToken)
  497. const FOLDER_INFO *_GetFolderInfo(int csidl)
  498. {
  499. const FOLDER_INFO *pfi;
  500. // make sure g_aFolderCache can be indexed by the CSIDL values
  501. COMPILETIME_ASSERT((ARRAYSIZE(g_aFolderCache) - 1) == CSIDL_COMPUTERSNEARME);
  502. for (pfi = c_rgFolderInfo; pfi->id != -1; pfi++)
  503. {
  504. if (pfi->id == csidl)
  505. return pfi;
  506. }
  507. return NULL;
  508. }
  509. // expand an individual enviornment variable
  510. // in:
  511. // pszVar "%USERPROFILE%
  512. // pszValue "c:\winnt\profiles\user"
  513. //
  514. // in/out:
  515. // pszToExpand in: %USERPROFILE%\My Docs", out: c:\winnt\profiles\user\My Docs"
  516. BOOL ExpandEnvVar(LPCTSTR pszVar, LPCTSTR pszValue, LPTSTR pszToExpand, DWORD cchToExpand)
  517. {
  518. TCHAR *pszStart = StrStrI(pszToExpand, pszVar);
  519. if (pszStart)
  520. {
  521. TCHAR szAfter[MAX_PATH];
  522. StrCpyN(szAfter, pszStart + lstrlen(pszVar), ARRAYSIZE(szAfter)); // save the tail
  523. StrCpyN(pszStart, pszValue, (int) (cchToExpand - (pszStart - pszToExpand)));
  524. StrCatBuff(pszToExpand, szAfter, cchToExpand); // put the tail back on
  525. return TRUE;
  526. }
  527. return FALSE;
  528. }
  529. HANDLE GetCurrentUserToken()
  530. {
  531. HANDLE hToken;
  532. if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hToken) ||
  533. OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_IMPERSONATE, &hToken))
  534. return hToken;
  535. return NULL;
  536. }
  537. // like ExpandEnvironmentStrings but is robust to the enviornment variables
  538. // not being set. this works on...
  539. // %SYSTEMROOT%
  540. // %SYSTEMDRIVE%
  541. // %USERPROFILE%
  542. // %ALLUSERSPROFILE%
  543. //
  544. // in the rare case (Winstone!) that there is a NULL enviornment block
  545. DWORD ExpandEnvironmentStringsNoEnv(HANDLE hToken, LPCTSTR pszExpand, LPTSTR pszOut, UINT cchOut)
  546. {
  547. TCHAR szPath[MAX_PATH];
  548. if (hToken && !_IsDefaultUserToken(hToken))
  549. {
  550. if (!SHExpandEnvironmentStringsForUser(hToken, pszExpand, pszOut, cchOut))
  551. lstrcpyn(pszOut, pszExpand, cchOut);
  552. }
  553. else if (hToken == NULL)
  554. {
  555. // to debug env expansion failure...
  556. // lstrcpyn(pszOut, pszExpand, cchOut);
  557. SHExpandEnvironmentStrings(pszExpand, pszOut, cchOut);
  558. }
  559. // manually expand in this order since
  560. // %USERPROFILE% -> %SYSTEMDRIVE%\Docs & Settings
  561. if (StrChr(pszOut, TEXT('%')) && (hToken == NULL))
  562. {
  563. hToken = GetCurrentUserToken();
  564. if (hToken)
  565. {
  566. // this does %USERPROFILE% and other per user stuff
  567. SHExpandEnvironmentStringsForUser(hToken, pszExpand, pszOut, cchOut);
  568. CloseHandle(hToken);
  569. }
  570. }
  571. else if (_IsDefaultUserToken(hToken) && StrChr(pszOut, TEXT('%')))
  572. {
  573. GetUserProfileDir(hToken, szPath, ARRAYSIZE(szPath));
  574. ExpandEnvVar(TEXT("%USERPROFILE%"), szPath, pszOut, cchOut);
  575. }
  576. if (*pszOut == TEXT('%'))
  577. {
  578. GetAllUsersDirectory(szPath);
  579. ExpandEnvVar(TEXT("%ALLUSERSPROFILE%"), szPath, pszOut, cchOut);
  580. }
  581. if (*pszOut == TEXT('%'))
  582. {
  583. GetSystemWindowsDirectory(szPath, ARRAYSIZE(szPath));
  584. ExpandEnvVar(TEXT("%SYSTEMROOT%"), szPath, pszOut, cchOut);
  585. }
  586. if (*pszOut == TEXT('%'))
  587. {
  588. GetSystemWindowsDirectory(szPath, ARRAYSIZE(szPath));
  589. ASSERT(szPath[1] == TEXT(':')); // this better not be a UNC!
  590. szPath[2] = 0; // SYSTEMDRIVE = 'c:', not 'c:\'
  591. ExpandEnvVar(TEXT("%SYSTEMDRIVE%"), szPath, pszOut, cchOut);
  592. }
  593. if (*pszOut == TEXT('%'))
  594. *pszOut = 0;
  595. return lstrlen(pszOut) + 1; // +1 to cover the NULL
  596. }
  597. // get the user profile directory:
  598. // uses the hToken as needed to determine the proper user profile
  599. BOOL GetUserProfileDir(HANDLE hToken, TCHAR *pszPath, UINT cchPath)
  600. {
  601. DWORD dwcch = cchPath;
  602. HANDLE hClose = NULL;
  603. BOOL fRet;
  604. *pszPath = 0; // in case of error
  605. if (!hToken)
  606. {
  607. hClose = hToken = GetCurrentUserToken();
  608. }
  609. if (_IsDefaultUserToken(hToken))
  610. {
  611. fRet = GetDefaultUserProfileDirectory(pszPath, &dwcch);
  612. }
  613. else
  614. {
  615. fRet = GetUserProfileDirectory(hToken, pszPath, &dwcch);
  616. }
  617. if (hClose)
  618. {
  619. CloseHandle(hClose);
  620. }
  621. return fRet;
  622. }
  623. #ifdef WX86
  624. void SetUseKnownWx86Dll(const FOLDER_INFO *pfi, BOOL bValue)
  625. {
  626. if (pfi->dwFlags & SDIF_WX86)
  627. {
  628. // GetSystemDirectory() knows we're looking for the Wx86 system
  629. // directory when this flag is set.
  630. NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = bValue ? TRUE : FALSE;
  631. }
  632. }
  633. #else
  634. #define SetUseKnownWx86Dll(pfi, bValue)
  635. #endif
  636. // read from registry
  637. BOOL GetProgramFiles(LPCTSTR pszValue, LPTSTR pszPath, UINT cchPath)
  638. {
  639. DWORD cbPath = cchPath * sizeof(*pszPath);
  640. *pszPath = 0;
  641. SHGetValue(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows\\CurrentVersion"),
  642. pszValue, NULL, pszPath, &cbPath);
  643. return (BOOL)*pszPath;
  644. }
  645. LPTSTR GetFontsDirectory(LPTSTR pszPath, UINT cchPath)
  646. {
  647. *pszPath = 0;
  648. ASSERT(cchPath >= MAX_PATH);
  649. if ((cchPath >= MAX_PATH) && GetWindowsDirectory(pszPath, cchPath))
  650. {
  651. PathAppend(pszPath, TEXT("Fonts"));
  652. }
  653. return pszPath;
  654. }
  655. void LoadDefaultString(int idString, LPTSTR lpBuffer, int cchBufferMax)
  656. {
  657. BOOL fSucceeded = FALSE;
  658. HRSRC hResInfo;
  659. HANDLE hStringSeg;
  660. LPWSTR lpsz;
  661. int cch;
  662. HMODULE hmod = GetModuleHandle(TEXT("SHELL32"));
  663. // Make sure the parms are valid.
  664. if (lpBuffer == NULL || cchBufferMax == 0)
  665. {
  666. return;
  667. }
  668. cch = 0;
  669. // String Tables are broken up into 16 string segments. Find the segment
  670. // containing the string we are interested in.
  671. if (hResInfo = FindResourceExW(hmod, (LPCWSTR)RT_STRING,
  672. (LPWSTR)((LONG_PTR)(((USHORT)idString >> 4) + 1)), GetSystemDefaultUILanguage()))
  673. {
  674. // Load that segment.
  675. hStringSeg = LoadResource(hmod, hResInfo);
  676. // Lock the resource.
  677. if (lpsz = (LPWSTR)LockResource(hStringSeg))
  678. {
  679. // Move past the other strings in this segment.
  680. // (16 strings in a segment -> & 0x0F)
  681. idString &= 0x0F;
  682. while (TRUE)
  683. {
  684. cch = *((WORD *)lpsz++); // PASCAL like string count
  685. // first UTCHAR is count if TCHARs
  686. if (idString-- == 0) break;
  687. lpsz += cch; // Step to start if next string
  688. }
  689. // lpsz isn't NULL terminated, it's a bunch of strings stuck together with
  690. // little cch counts in between.
  691. // Account for the NULL
  692. cchBufferMax--;
  693. // Don't copy more than the max allowed.
  694. if (cch > cchBufferMax)
  695. cch = cchBufferMax;
  696. // Copy the string into the buffer.
  697. CopyMemory(lpBuffer, lpsz, cch*sizeof(WCHAR));
  698. // Attach Null terminator.
  699. lpBuffer[cch] = 0;
  700. fSucceeded = TRUE;
  701. }
  702. }
  703. if (!fSucceeded)
  704. {
  705. LoadString(HINST_THISDLL, idString, lpBuffer, cchBufferMax);
  706. }
  707. }
  708. BOOL GetLocalSettingsDir(HANDLE hToken, LPTSTR pszPath, UINT cchPath)
  709. {
  710. *pszPath = 0;
  711. ASSERT(cchPath >= MAX_PATH);
  712. if (cchPath >= MAX_PATH)
  713. {
  714. GetUserProfileDir(hToken, pszPath, cchPath);
  715. if (*pszPath)
  716. {
  717. TCHAR szEntry[MAX_PATH];
  718. LoadDefaultString(IDS_LOCALSETTINGS, szEntry, ARRAYSIZE(szEntry));
  719. PathAppend(pszPath, szEntry);
  720. }
  721. }
  722. return *pszPath ? TRUE : FALSE;
  723. }
  724. HRESULT GetResourcesDir(IN BOOL fLocalized, IN LPTSTR pszPath, IN DWORD cchSize)
  725. {
  726. HRESULT hr = E_FAIL;
  727. TCHAR szTemp[MAX_PATH];
  728. RIP(IS_VALID_WRITE_BUFFER(pszPath, TCHAR, cchSize));
  729. pszPath[0] = 0; // Terminate in case we fail.
  730. if (SHGetSystemWindowsDirectory(szTemp, ARRAYSIZE(szTemp)))
  731. {
  732. // It's now "%windir%\resources\".
  733. if (PathAppend(szTemp, TEXT("resources")))
  734. {
  735. if (fLocalized)
  736. {
  737. LANGID lidUI = GetUserDefaultUILanguage();
  738. TCHAR szSubDir[10];
  739. // Now make it "%windir%\resources\<LangID>\"
  740. if (SUCCEEDED(StringCchPrintf(szSubDir, ARRAYSIZE(szSubDir), TEXT("%04x"), lidUI)))
  741. {
  742. if (PathAppend(szTemp, szSubDir))
  743. {
  744. StrCpyN(pszPath, szTemp, cchSize);
  745. hr = S_OK;
  746. }
  747. }
  748. }
  749. else
  750. {
  751. StrCpyN(pszPath, szTemp, cchSize);
  752. hr = S_OK;
  753. }
  754. }
  755. }
  756. return hr;
  757. }
  758. // out:
  759. // pszPath fills in with the full path with no env gunk (MAX_PATH)
  760. HRESULT _GetFolderDefaultPath(const FOLDER_INFO *pfi, HANDLE hToken, LPTSTR pszPath, DWORD cchPath)
  761. {
  762. ASSERT(!(pfi->dwFlags & SDIF_NOT_FILESYS)); // speical folders should not come here
  763. *pszPath = 0;
  764. TCHAR szEntry[MAX_PATH];
  765. switch (pfi->id)
  766. {
  767. case CSIDL_PROFILE:
  768. GetUserProfileDir(hToken, pszPath, cchPath);
  769. break;
  770. case CSIDL_PROGRAM_FILES:
  771. GetProgramFiles(TEXT("ProgramFilesDir"), pszPath, cchPath);
  772. break;
  773. case CSIDL_PROGRAM_FILES_COMMON:
  774. GetProgramFiles(TEXT("CommonFilesDir"), pszPath, cchPath);
  775. break;
  776. case CSIDL_PROGRAM_FILESX86:
  777. GetProgramFiles(TEXT("ProgramFilesDir (x86)"), pszPath, cchPath);
  778. break;
  779. case CSIDL_PROGRAM_FILES_COMMONX86:
  780. GetProgramFiles(TEXT("CommonFilesDir (x86)"), pszPath, cchPath);
  781. break;
  782. #ifdef _WIN64
  783. case CSIDL_SYSTEMX86:
  784. //
  785. // downlevel systems do not have GetSystemWindowsDirectory export,
  786. // but shell thunking layer handles this gracefully
  787. GetSystemWindowsDirectory(pszPath, cchPath);
  788. //
  789. // tack on subdirectory
  790. //
  791. if ((cchPath < MAX_PATH) || !PathCombine(pszPath, pszPath, TEXT(WOW64_SYSTEM_DIRECTORY)))
  792. {
  793. *pszPath = 0;
  794. }
  795. break;
  796. #else
  797. case CSIDL_SYSTEMX86:
  798. #endif
  799. case CSIDL_SYSTEM:
  800. GetSystemDirectory(pszPath, cchPath);
  801. break;
  802. case CSIDL_WINDOWS:
  803. GetWindowsDirectory(pszPath, cchPath);
  804. break;
  805. case CSIDL_RESOURCES:
  806. GetResourcesDir(FALSE, pszPath, cchPath);
  807. break;
  808. case CSIDL_RESOURCES_LOCALIZED:
  809. GetResourcesDir(TRUE, pszPath, cchPath);
  810. break;
  811. case CSIDL_COMPUTERSNEARME:
  812. // no path for this
  813. break;
  814. case CSIDL_FONTS:
  815. GetFontsDirectory(pszPath, cchPath);
  816. break;
  817. default:
  818. switch (pfi->dwFlags & SDIF_CREATE_IN_MASK)
  819. {
  820. case SDIF_CREATE_IN_ROOT:
  821. GetWindowsDirectory(pszPath, cchPath);
  822. PathStripToRoot(pszPath);
  823. break;
  824. case SDIF_CREATE_IN_ALLUSERS:
  825. if (cchPath >= MAX_PATH)
  826. {
  827. GetAllUsersDirectory(pszPath);
  828. }
  829. break;
  830. case SDIF_CREATE_IN_WINDIR:
  831. GetWindowsDirectory(pszPath, cchPath);
  832. break;
  833. case SDIF_CREATE_IN_MYDOCUMENTS:
  834. // 99/10/21 Mil#104600: When asking for folders in "My Documents" don't
  835. // verify their existance. Just return the path. The caller will make
  836. // the decision to create the folder or not.
  837. // on failure *pszPath will be empty
  838. if (cchPath >= MAX_PATH)
  839. {
  840. SHGetFolderPath(NULL, CSIDL_PERSONAL | CSIDL_FLAG_DONT_VERIFY, hToken, SHGFP_TYPE_CURRENT, pszPath);
  841. }
  842. break;
  843. case SDIF_CREATE_IN_LOCALSET:
  844. GetLocalSettingsDir(hToken, pszPath, cchPath);
  845. break;
  846. default:
  847. GetUserProfileDir(hToken, pszPath, cchPath);
  848. break;
  849. }
  850. if (*pszPath && (cchPath >= MAX_PATH))
  851. {
  852. LoadDefaultString(pfi->idsDefault, szEntry, ARRAYSIZE(szEntry));
  853. PathAppend(pszPath, szEntry);
  854. }
  855. break;
  856. }
  857. return *pszPath ? S_OK : E_FAIL;
  858. }
  859. void RegSetFolderPath(const FOLDER_INFO *pfi, LPCTSTR pszSubKey, LPCTSTR pszPath)
  860. {
  861. HKEY hk;
  862. if (SUCCEEDED(_OpenKeyForFolder(pfi, NULL, pszSubKey, &hk)))
  863. {
  864. if (pszPath)
  865. RegSetValueEx(hk, pfi->pszValueName, 0, REG_SZ, (LPBYTE)pszPath, (1 + lstrlen(pszPath)) * sizeof(TCHAR));
  866. else
  867. RegDeleteValue(hk, pfi->pszValueName);
  868. RegCloseKey(hk);
  869. }
  870. }
  871. BOOL RegQueryPath(HKEY hk, LPCTSTR pszValue, LPTSTR pszPath, UINT cch)
  872. {
  873. DWORD cbPath = cch * sizeof(TCHAR);
  874. *pszPath = 0;
  875. SHQueryValueEx(hk, pszValue, 0, NULL, pszPath, &cbPath);
  876. return (BOOL)*pszPath;
  877. }
  878. // More than 50 is silly
  879. #define MAX_TEMP_FILE_TRIES 50
  880. // returns:
  881. // S_OK the path exists and it is a folder
  882. // FAILED() result
  883. HRESULT _IsFolderNotFile(LPCTSTR pszFolder)
  884. {
  885. HRESULT hr;
  886. DWORD dwAttribs = GetFileAttributes(pszFolder);
  887. if (dwAttribs == -1)
  888. {
  889. DWORD err = GetLastError();
  890. hr = HRESULT_FROM_WIN32(err);
  891. }
  892. else
  893. {
  894. // see if it is a file, if so we need to rename that file
  895. if (dwAttribs & FILE_ATTRIBUTE_DIRECTORY)
  896. {
  897. hr = S_OK;
  898. }
  899. else
  900. {
  901. int iExt = 0;
  902. do
  903. {
  904. TCHAR szDst[MAX_PATH];
  905. wnsprintf(szDst, ARRAYSIZE(szDst), L"%s.%03d", pszFolder, iExt);
  906. if (MoveFile(pszFolder, szDst))
  907. iExt = 0;
  908. else
  909. {
  910. // Normally we fail because .00x already exists but that may not be true.
  911. DWORD dwError = GetLastError();
  912. if (ERROR_ALREADY_EXISTS == dwError)
  913. iExt++; // Try the next one...
  914. else
  915. iExt = 0; // We have problems and need to give up. (No write access?)
  916. }
  917. } while (iExt && (iExt < MAX_TEMP_FILE_TRIES));
  918. hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
  919. }
  920. }
  921. return hr;
  922. }
  923. HRESULT _OpenKeyForFolder(const FOLDER_INFO *pfi, HANDLE hToken, LPCTSTR pszSubKey, HKEY *phkey)
  924. {
  925. TCHAR szRegPath[255];
  926. LONG err;
  927. HKEY hkRoot, hkeyToFree = NULL;
  928. *phkey = NULL;
  929. StrCpyN(szRegPath, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\"), ARRAYSIZE(szRegPath));
  930. StrCatBuff(szRegPath, pszSubKey, ARRAYSIZE(szRegPath));
  931. if (_IsDefaultUserToken(hToken) && (pfi->hKey == HKEY_CURRENT_USER))
  932. {
  933. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_USERS, TEXT(".Default"), 0, KEY_READ, &hkRoot))
  934. hkeyToFree = hkRoot;
  935. else
  936. return E_FAIL;
  937. }
  938. else if (hToken && (pfi->hKey == HKEY_CURRENT_USER))
  939. {
  940. if (GetUserProfileKey(hToken, KEY_CREATE_SUB_KEY, &hkRoot))
  941. hkeyToFree = hkRoot;
  942. else
  943. return E_FAIL;
  944. }
  945. else
  946. hkRoot = pfi->hKey;
  947. // This must be MAXIMUM_ALLOWED because the handle is handed out to
  948. // various different callers and they expect to fail/succeeded on their
  949. // individual calls with the handle.
  950. err = RegCreateKeyEx(hkRoot, szRegPath, 0, NULL, REG_OPTION_NON_VOLATILE,
  951. MAXIMUM_ALLOWED, NULL, phkey, NULL);
  952. if (hkeyToFree)
  953. RegCloseKey(hkeyToFree);
  954. return HRESULT_FROM_WIN32(err);
  955. }
  956. //
  957. // Roaming Profiles can set up the environment variables and registry
  958. // keys like so:
  959. //
  960. // HOMESHARE=\\server\share\user
  961. // HOMEPATH=\
  962. // My Music=%HOMESHARE%%HOMEPATH%\My Music
  963. //
  964. // so you end up with "\\server\share\user\\My Music", which is an
  965. // invalid path. Clean them up; otherwise SHGetSpecialFolderLocation will
  966. // fail.
  967. //
  968. void _CleanExpandedEnvironmentPath(LPTSTR pszExpand)
  969. {
  970. // Search for "\\" at a location other than the start of the string.
  971. // If found, collapse it.
  972. LPTSTR pszWhackWhack;
  973. while (lstrlen(pszExpand) > 2 &&
  974. (pszWhackWhack = StrStr(pszExpand+1, TEXT("\\\\"))))
  975. {
  976. // shlwapi handles overlap
  977. StrCpy(pszWhackWhack+1, pszWhackWhack+2);
  978. }
  979. }
  980. // returns:
  981. // S_OK found in registry, path well formed
  982. // S_FALSE empty registry
  983. // FAILED() failure result
  984. HRESULT _GetFolderFromReg(const FOLDER_INFO *pfi, HANDLE hToken, LPTSTR pszPath, size_t cchPath)
  985. {
  986. HKEY hkUSF;
  987. HRESULT hr;
  988. *pszPath = 0;
  989. hr = _OpenKeyForFolder(pfi, hToken, TEXT("User Shell Folders"), &hkUSF);
  990. if (SUCCEEDED(hr))
  991. {
  992. TCHAR szExpand[MAX_PATH];
  993. DWORD dwType, cbExpand = sizeof(szExpand);
  994. if (SHRegGetValue(hkUSF, NULL, pfi->pszValueName, SRRF_RT_REG_SZ | SRRF_RT_REG_EXPAND_SZ | SRRF_NOEXPAND, &dwType, szExpand, &cbExpand) == ERROR_SUCCESS)
  995. {
  996. if (REG_SZ == dwType)
  997. {
  998. lstrcpyn(pszPath, szExpand, cchPath);
  999. }
  1000. else if (REG_EXPAND_SZ == dwType)
  1001. {
  1002. ExpandEnvironmentStringsNoEnv(hToken, szExpand, pszPath, cchPath);
  1003. _CleanExpandedEnvironmentPath(pszPath);
  1004. }
  1005. TraceMsg(TF_PATH, "_CreateFolderPath 'User Shell Folders' %s = %s", pfi->pszValueName, pszPath);
  1006. }
  1007. if (*pszPath == 0)
  1008. {
  1009. hr = S_FALSE; // empty registry, success but empty
  1010. }
  1011. else if ((PathGetDriveNumber(pszPath) != -1) || PathIsUNC(pszPath))
  1012. {
  1013. hr = S_OK; // good reg path, fully qualified
  1014. }
  1015. else
  1016. {
  1017. *pszPath = 0; // bad reg data
  1018. hr = E_INVALIDARG;
  1019. }
  1020. RegCloseKey(hkUSF);
  1021. }
  1022. return hr;
  1023. }
  1024. HRESULT _GetFolderPath(HWND hwnd, const FOLDER_INFO *pfi, HANDLE hToken, UINT uFlags, LPTSTR pszPath, size_t cchPath)
  1025. {
  1026. HRESULT hr;
  1027. *pszPath = 0; // assume failure
  1028. if (pfi->hKey)
  1029. {
  1030. hr = _GetFolderFromReg(pfi, hToken, pszPath, cchPath);
  1031. if (SUCCEEDED(hr))
  1032. {
  1033. if (hr == S_FALSE)
  1034. {
  1035. // empty registry, SDIF_EMPTY_IF_NOT_IN_REG means they don't exist
  1036. // if the registry is not populated with a value. this lets us disable
  1037. // the common items on platforms that don't want them
  1038. if (pfi->dwFlags & SDIF_EMPTY_IF_NOT_IN_REG)
  1039. return S_FALSE; // success, but empty
  1040. hr = _GetFolderDefaultPath(pfi, hToken, pszPath, cchPath);
  1041. }
  1042. if (!(uFlags & CSIDL_FLAG_DONT_VERIFY))
  1043. {
  1044. hr = VerifyAndCreateFolder(hwnd, pfi, uFlags, pszPath) ;
  1045. }
  1046. if (hr != S_OK)
  1047. {
  1048. *pszPath = 0;
  1049. }
  1050. if (!(uFlags & CSIDL_FLAG_DONT_VERIFY))
  1051. {
  1052. HKEY hkey;
  1053. // record value in "Shell Folders", even in the failure case
  1054. // NOTE: we only do this for historical reasons. there may be some
  1055. // apps that depend on these values being in the registry, but in general
  1056. // the contetens here are unreliable as they are only written after someone
  1057. // asks for the folder through this API.
  1058. if (SUCCEEDED(_OpenKeyForFolder(pfi, hToken, TEXT("Shell Folders"), &hkey)))
  1059. {
  1060. RegSetValueEx(hkey, pfi->pszValueName, 0, REG_SZ, (LPBYTE)pszPath, (1 + lstrlen(pszPath)) * sizeof(TCHAR));
  1061. RegCloseKey(hkey);
  1062. }
  1063. }
  1064. }
  1065. }
  1066. else
  1067. {
  1068. hr = _GetFolderDefaultPath(pfi, hToken, pszPath, cchPath);
  1069. if ((S_OK == hr) && !(uFlags & CSIDL_FLAG_DONT_VERIFY))
  1070. {
  1071. hr = VerifyAndCreateFolder(hwnd, pfi, uFlags, pszPath);
  1072. }
  1073. if (hr != S_OK)
  1074. {
  1075. *pszPath = 0;
  1076. }
  1077. }
  1078. ASSERT(hr == S_OK ? *pszPath != 0 : *pszPath == 0);
  1079. return hr;
  1080. }
  1081. void _PostCreateStuff(const FOLDER_INFO *pfi, LPTSTR pszPath, BOOL fUpgrade)
  1082. {
  1083. if (pfi->pfnInit || pfi->idsLocalizedName || (pfi->dwFlags & SDIF_PERSONALIZED))
  1084. {
  1085. if (fUpgrade)
  1086. {
  1087. // if we are upgrading, torch all our previous meta data
  1088. TCHAR sz[MAX_PATH];
  1089. if (PathCombine(sz, pszPath, TEXT("desktop.ini")))
  1090. {
  1091. if (PathFileExistsAndAttributes(sz, NULL))
  1092. {
  1093. WritePrivateProfileSection(TEXT(".ShellClassInfo"), NULL, sz);
  1094. // in the upgrade case, sometimes the desktop.ini
  1095. // file was there but the folder wasnt marked.
  1096. // insure that it is marked.
  1097. PathMakeSystemFolder(pszPath);
  1098. }
  1099. }
  1100. }
  1101. // now call the create proc if we have one
  1102. if (pfi->pfnInit)
  1103. pfi->pfnInit(pfi->id, pszPath);
  1104. // does the table specify a localized resource name that we should be
  1105. // using for this object?
  1106. if (pfi->idsLocalizedName)
  1107. SHSetLocalizedName(pszPath, TEXT("shell32.dll"), pfi->idsLocalizedName);
  1108. // do we need to store the user name for this folder?
  1109. if (pfi->dwFlags & SDIF_PERSONALIZED)
  1110. {
  1111. TCHAR szName[UNLEN+1];
  1112. DWORD dwName = ARRAYSIZE(szName);
  1113. if (GetUserName(szName, &dwName))
  1114. {
  1115. // CSharedDocuments depends on a per system list of MyDocs folders
  1116. // this is where we make sure that list is setup
  1117. if (!IsOS(OS_DOMAINMEMBER) && (pfi->id == CSIDL_PERSONAL))
  1118. {
  1119. SKSetValue(SHELLKEY_HKLM_EXPLORER, L"DocFolderPaths",
  1120. szName, REG_SZ, pszPath, (lstrlen(pszPath) + 1) * sizeof(TCHAR));
  1121. }
  1122. SetFolderString(TRUE, pszPath, NULL, L"DeleteOnCopy", SZ_CANBEUNICODE TEXT("Owner"), szName);
  1123. wnsprintf(szName, ARRAYSIZE(szName), L"%d", pfi->id);
  1124. SetFolderString(TRUE, pszPath, NULL, L"DeleteOnCopy", TEXT("Personalized"), szName);
  1125. LoadDefaultString(pfi->idsDefault, szName, ARRAYSIZE(szName));
  1126. SetFolderString(TRUE, pszPath, NULL, L"DeleteOnCopy", SZ_CANBEUNICODE TEXT("PersonalizedName"), szName);
  1127. }
  1128. }
  1129. }
  1130. }
  1131. HRESULT VerifyAndCreateFolder(HWND hwnd, const FOLDER_INFO *pfi, UINT uFlags, LPTSTR pszPath)
  1132. {
  1133. HRESULT hr = _IsFolderNotFile(pszPath);
  1134. // this code supports a UI mode of this API. but generally this is not used
  1135. // this code should be removed
  1136. if ((hr != S_OK) && hwnd)
  1137. {
  1138. // we might be able to reconnect if this is a net path
  1139. if (PathIsUNC(pszPath))
  1140. {
  1141. if (SHValidateUNC(hwnd, pszPath, 0))
  1142. hr = _IsFolderNotFile(pszPath);
  1143. }
  1144. else if (IsDisconnectedNetDrive(DRIVEID(pszPath)))
  1145. {
  1146. TCHAR szDrive[3];
  1147. PathBuildSimpleRoot(DRIVEID(pszPath), szDrive);
  1148. if (WNetRestoreConnection(hwnd, szDrive) == WN_SUCCESS)
  1149. hr = _IsFolderNotFile(pszPath);
  1150. }
  1151. }
  1152. // to avoid a sequence of long net timeouts or calls we know won't
  1153. // succeed test for these specific errors and don't try to create
  1154. // the folder
  1155. if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) ||
  1156. hr == HRESULT_FROM_WIN32(ERROR_BAD_NETPATH))
  1157. {
  1158. return hr;
  1159. }
  1160. if ((hr != S_OK) && (uFlags & CSIDL_FLAG_CREATE))
  1161. {
  1162. DWORD err = SHCreateDirectory(NULL, pszPath);
  1163. hr = HRESULT_FROM_WIN32(err);
  1164. if (hr == S_OK)
  1165. {
  1166. ASSERT(NULL == StrChr(pszPath, TEXT('%')));
  1167. if (pfi->dwFlags & SDIF_HIDE)
  1168. SetFileAttributes(pszPath, GetFileAttributes(pszPath) | FILE_ATTRIBUTE_HIDDEN);
  1169. _PostCreateStuff(pfi, pszPath, FALSE);
  1170. }
  1171. }
  1172. else if (hr == S_OK)
  1173. {
  1174. if (uFlags & CSIDL_FLAG_PER_USER_INIT)
  1175. _PostCreateStuff(pfi, pszPath, TRUE);
  1176. }
  1177. return hr;
  1178. }
  1179. void _SetPathCache(const FOLDER_INFO *pfi, LPCTSTR psz)
  1180. {
  1181. LPTSTR pszOld = (LPTSTR)InterlockedExchangePointer((void **)&g_aFolderCache[pfi->id].psz, (void *)psz);
  1182. if (pszOld && pszOld != (LPTSTR)-1)
  1183. {
  1184. // check for the concurent use... very rare case
  1185. LocalFree(pszOld);
  1186. }
  1187. }
  1188. HRESULT _GetFolderPathCached(HWND hwnd, const FOLDER_INFO *pfi, HANDLE hToken, UINT uFlags, LPTSTR pszPath, size_t cchPath)
  1189. {
  1190. HRESULT hr;
  1191. *pszPath = 0;
  1192. // can only cache for the current user, hToken == NULL or per machine folders
  1193. if (!hToken || (pfi->hKey != HKEY_CURRENT_USER))
  1194. {
  1195. _UpdateShellFolderCache();
  1196. LPTSTR pszCache = (LPTSTR)InterlockedExchangePointer((void **)&g_aFolderCache[pfi->id].psz, (void *)-1);
  1197. if ((pszCache == (LPTSTR)-1) || (pszCache == NULL))
  1198. {
  1199. // either not cached or cached failed state
  1200. if ((pszCache == (LPTSTR)-1) || (uFlags & (CSIDL_FLAG_CREATE | CSIDL_FLAG_DONT_VERIFY)))
  1201. {
  1202. hr = _GetFolderPath(hwnd, pfi, hToken, uFlags, pszPath, cchPath);
  1203. // only set the cache value if CSIDL_FLAG_DONT_VERIFY was NOT passed
  1204. if (!(uFlags & CSIDL_FLAG_DONT_VERIFY))
  1205. {
  1206. if (hr == S_OK)
  1207. {
  1208. // dupe the string so we can add it to the cache
  1209. pszCache = StrDup(pszPath);
  1210. }
  1211. else
  1212. {
  1213. // we failed to get the folder path, null out the cache
  1214. ASSERT(*pszPath == 0);
  1215. pszCache = NULL;
  1216. }
  1217. _SetPathCache(pfi, pszCache);
  1218. }
  1219. }
  1220. else
  1221. {
  1222. // cache was null and user didnt pass create flag so we just fail
  1223. ASSERT(pszCache == NULL);
  1224. ASSERT(*pszPath == 0);
  1225. hr = E_FAIL;
  1226. }
  1227. }
  1228. else
  1229. {
  1230. // cache hit case: copy the cached string and then restore the cached value back
  1231. lstrcpyn(pszPath, pszCache, cchPath);
  1232. _SetPathCache(pfi, pszCache);
  1233. hr = S_OK;
  1234. }
  1235. }
  1236. else
  1237. {
  1238. hr = _GetFolderPath(hwnd, pfi, hToken, uFlags, pszPath, cchPath);
  1239. }
  1240. return hr;
  1241. }
  1242. // NOTE: possibly we need a csidlSkip param to avoid recursion?
  1243. BOOL _ReparentAliases(HWND hwnd, HANDLE hToken, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlAlias, DWORD dwXlateAliases)
  1244. {
  1245. static const struct {DWORD dwXlate; int idPath; int idAlias; BOOL fCommon;} s_rgidAliases[]=
  1246. {
  1247. { XLATEALIAS_MYDOCS, CSIDL_PERSONAL | CSIDL_FLAG_NO_ALIAS, CSIDL_PERSONAL, FALSE},
  1248. { XLATEALIAS_COMMONDOCS, CSIDL_COMMON_DOCUMENTS | CSIDL_FLAG_NO_ALIAS, CSIDL_COMMON_DOCUMENTS, FALSE},
  1249. { XLATEALIAS_DESKTOP, CSIDL_DESKTOPDIRECTORY, CSIDL_DESKTOP, FALSE},
  1250. { XLATEALIAS_DESKTOP, CSIDL_COMMON_DESKTOPDIRECTORY, CSIDL_DESKTOP, TRUE},
  1251. };
  1252. BOOL fContinue = TRUE;
  1253. *ppidlAlias = NULL;
  1254. for (int i = 0; fContinue && i < ARRAYSIZE(s_rgidAliases); i++)
  1255. {
  1256. LPITEMIDLIST pidlPath;
  1257. if ((dwXlateAliases & s_rgidAliases[i].dwXlate) &&
  1258. (S_OK == SHGetFolderLocation(hwnd, s_rgidAliases[i].idPath, hToken, 0, &pidlPath)))
  1259. {
  1260. LPCITEMIDLIST pidlChild = ILFindChild(pidlPath, pidl);
  1261. if (pidlChild)
  1262. {
  1263. // ok we need to use the alias instead of the path
  1264. LPITEMIDLIST pidlAlias;
  1265. if (S_OK == SHGetFolderLocation(hwnd, s_rgidAliases[i].idAlias, hToken, 0, &pidlAlias))
  1266. {
  1267. if (SUCCEEDED(SHILCombine(pidlAlias, pidlChild, ppidlAlias)))
  1268. {
  1269. if (s_rgidAliases[i].fCommon && !ILIsEmpty(*ppidlAlias))
  1270. {
  1271. // find the size of the special part (subtacting for null pidl terminator)
  1272. UINT cbSize = ILGetSize(pidlAlias) - sizeof(pidlAlias->mkid.cb);
  1273. LPITEMIDLIST pidlChildFirst = _ILSkip(*ppidlAlias, cbSize);
  1274. // We set the first ID under the common path to have the SHID_FS_COMMONITEM so that when we bind we
  1275. // can hand this to the proper merged psf
  1276. pidlChildFirst->mkid.abID[0] |= SHID_FS_COMMONITEM;
  1277. }
  1278. ILFree(pidlAlias);
  1279. }
  1280. fContinue = FALSE;
  1281. }
  1282. }
  1283. ILFree(pidlPath);
  1284. }
  1285. }
  1286. return (*ppidlAlias != NULL);
  1287. }
  1288. STDAPI SHILAliasTranslate(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlAlias, DWORD dwXlateAliases)
  1289. {
  1290. return _ReparentAliases(NULL, NULL, pidl, ppidlAlias, dwXlateAliases) ? S_OK : E_FAIL;
  1291. }
  1292. HRESULT _CreateFolderIDList(HWND hwnd, const FOLDER_INFO *pfi, HANDLE hToken, UINT uFlags, LPITEMIDLIST *ppidl)
  1293. {
  1294. HRESULT hr = S_OK;
  1295. *ppidl = NULL; // assume failure or empty
  1296. if (pfi->id == CSIDL_PRINTERS && (ACF_STAROFFICE5PRINTER & SHGetAppCompatFlags(ACF_STAROFFICE5PRINTER)))
  1297. {
  1298. // Star Office 5.0 relies on the fact that the printer pidl used to be like below. They skip the
  1299. // first simple pidl (My Computer) and do not check if there is anything else, they assume that the
  1300. // second simple pidl is the Printer folder one. (stephstm, 07/30/99)
  1301. // CLSID_MyComputer, CLSID_Printers
  1302. hr = ILCreateFromPathEx(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}"), NULL, ILCFP_FLAG_NO_MAP_ALIAS, ppidl, NULL);
  1303. }
  1304. else if (pfi->id == CSIDL_COMPUTERSNEARME)
  1305. {
  1306. if (IsOS(OS_DOMAINMEMBER))
  1307. {
  1308. // only if you are in a workgroup - fail otherwise
  1309. hr = E_FAIL;
  1310. }
  1311. else
  1312. {
  1313. // we computer this IDLIST from the domain/workgroup you are a member of
  1314. hr = SHGetDomainWorkgroupIDList(ppidl);
  1315. }
  1316. }
  1317. else if ((pfi->id == CSIDL_COMMON_DOCUMENTS)
  1318. && !(uFlags & CSIDL_FLAG_NO_ALIAS))
  1319. {
  1320. // CLSID_MyComputer \ SharedDocumnets (canonical name)
  1321. hr = ILCreateFromPathEx(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{59031a47-3f72-44a7-89c5-5595fe6b30ee},SharedDocuments"), NULL, ILCFP_FLAG_NO_MAP_ALIAS, ppidl, NULL);
  1322. }
  1323. else if ((pfi->dwFlags & SDIF_CONST_IDLIST)
  1324. && (!(uFlags & CSIDL_FLAG_NO_ALIAS) || !(pfi->dwFlags & SDIF_MAYBE_ALIASED)))
  1325. {
  1326. // these are CONST, never change
  1327. hr = SHILClone(g_aFolderCache[pfi->id].pidl, ppidl);
  1328. }
  1329. else
  1330. {
  1331. TCHAR szPath[MAX_PATH];
  1332. hr = _GetFolderPathCached(hwnd, pfi, hToken, uFlags, szPath, ARRAYSIZE(szPath));
  1333. if (hr == S_OK)
  1334. {
  1335. HRESULT hrInit = SHCoInitialize();
  1336. hr = ILCreateFromPathEx(szPath, NULL, ILCFP_FLAG_SKIPJUNCTIONS, ppidl, NULL);
  1337. // attempt to reparent aliased pidls.
  1338. if (SUCCEEDED(hr)
  1339. && (pfi->dwFlags & SDIF_MAYBE_ALIASED)
  1340. && !(uFlags & CSIDL_FLAG_NO_ALIAS))
  1341. {
  1342. LPITEMIDLIST pidlAlias;
  1343. if (_ReparentAliases(hwnd, hToken, *ppidl, &pidlAlias, XLATEALIAS_ALL))
  1344. {
  1345. ILFree(*ppidl);
  1346. *ppidl = pidlAlias;
  1347. }
  1348. }
  1349. SHCoUninitialize(hrInit);
  1350. }
  1351. }
  1352. return hr;
  1353. }
  1354. void _SetIDListCache(const FOLDER_INFO *pfi, LPCITEMIDLIST pidl, BOOL fNonAlias)
  1355. {
  1356. if (fNonAlias || !(pfi->dwFlags & SDIF_CONST_IDLIST))
  1357. {
  1358. void **ppv = (void **) (fNonAlias ? &g_aFolderCache[pfi->id].pidlNonAlias : &g_aFolderCache[pfi->id].pidl);
  1359. LPITEMIDLIST pidlOld = (LPITEMIDLIST)InterlockedExchangePointer(ppv, (void *)pidl);
  1360. if (pidlOld && pidlOld != (LPITEMIDLIST)-1)
  1361. {
  1362. // check for the concurent use... very rare case
  1363. // ASSERT(pidl == (LPCITEMIDLIST)-1); // should not really be ASSERT
  1364. ILFree(pidlOld);
  1365. }
  1366. }
  1367. }
  1368. LPITEMIDLIST _GetIDListCache(const FOLDER_INFO *pfi, BOOL fNonAlias)
  1369. {
  1370. void **ppv = (void **) (fNonAlias ? &g_aFolderCache[pfi->id].pidlNonAlias : &g_aFolderCache[pfi->id].pidl);
  1371. ASSERT(fNonAlias || !(pfi->dwFlags & SDIF_CONST_IDLIST));
  1372. return (LPITEMIDLIST)InterlockedExchangePointer(ppv, (void *)-1);
  1373. }
  1374. // hold this lock for the minimal amout of time possible to avoid other users
  1375. // of this resource requring them to re-create the pidl
  1376. HRESULT _GetFolderIDListCached(HWND hwnd, const FOLDER_INFO *pfi, UINT uFlags, LPITEMIDLIST *ppidl)
  1377. {
  1378. HRESULT hr;
  1379. BOOL fNonAlias = uFlags & CSIDL_FLAG_NO_ALIAS;
  1380. ASSERT(pfi->id < ARRAYSIZE(g_aFolderCache));
  1381. if ((pfi->dwFlags & SDIF_CONST_IDLIST) &&
  1382. (!fNonAlias || !(pfi->dwFlags & SDIF_MAYBE_ALIASED)))
  1383. {
  1384. // these are CONST, never change
  1385. hr = SHILClone(g_aFolderCache[pfi->id].pidl, ppidl);
  1386. }
  1387. else
  1388. {
  1389. LPITEMIDLIST pidlCache;
  1390. _UpdateShellFolderCache();
  1391. pidlCache = _GetIDListCache(pfi, fNonAlias);
  1392. if ((pidlCache == (LPCITEMIDLIST)-1) || (pidlCache == NULL))
  1393. {
  1394. // either uninitalized cache state OR cached failure (NULL)
  1395. if ((pidlCache == (LPCITEMIDLIST)-1) || (uFlags & CSIDL_FLAG_CREATE))
  1396. {
  1397. // not initialized (or concurent use) try creating it for this use
  1398. hr = _CreateFolderIDList(hwnd, pfi, NULL, uFlags, ppidl);
  1399. if (S_OK == hr)
  1400. hr = SHILClone(*ppidl, &pidlCache); // create cache copy
  1401. else
  1402. pidlCache = NULL;
  1403. }
  1404. else
  1405. hr = E_FAIL; // return cached failure
  1406. }
  1407. else
  1408. {
  1409. hr = SHILClone(pidlCache, ppidl); // cache hit
  1410. }
  1411. // store back the PIDL if it is non NULL or they specified CREATE
  1412. // and we failed to create it (cache the not existant state). this is needed
  1413. // so we don't cache a NULL if the first callers don't ask for create and
  1414. // subsequent callers do
  1415. if (pidlCache || (uFlags & CSIDL_FLAG_CREATE))
  1416. _SetIDListCache(pfi, pidlCache, fNonAlias);
  1417. }
  1418. return hr;
  1419. }
  1420. void _ClearCacheEntry(const FOLDER_INFO *pfi)
  1421. {
  1422. if (!(pfi->dwFlags & SDIF_CONST_IDLIST))
  1423. _SetIDListCache(pfi, (LPCITEMIDLIST)-1, FALSE);
  1424. if (pfi->dwFlags & SDIF_MAYBE_ALIASED)
  1425. _SetIDListCache(pfi, (LPCITEMIDLIST)-1, TRUE);
  1426. _SetPathCache(pfi, (LPCTSTR)-1);
  1427. }
  1428. void _ClearAllCacheEntrys()
  1429. {
  1430. for (const FOLDER_INFO *pfi = c_rgFolderInfo; pfi->id != -1; pfi++)
  1431. {
  1432. _ClearCacheEntry(pfi);
  1433. }
  1434. }
  1435. void _ClearAllAliasCacheEntrys()
  1436. {
  1437. for (const FOLDER_INFO *pfi = c_rgFolderInfo; pfi->id != -1; pfi++)
  1438. {
  1439. if (pfi->dwFlags & SDIF_MAYBE_ALIASED)
  1440. {
  1441. _SetIDListCache(pfi, (LPCITEMIDLIST)-1, FALSE); // nuke the aliased pidl
  1442. }
  1443. }
  1444. }
  1445. // Per instance count of mods to Special Folder cache.
  1446. EXTERN_C HANDLE g_hCounter;
  1447. HANDLE g_hCounter = NULL; // Global count of mods to Special Folder cache.
  1448. int g_lPerProcessCount = 0;
  1449. // Make sure the special folder cache is up to date.
  1450. void _UpdateShellFolderCache(void)
  1451. {
  1452. HANDLE hCounter = SHGetCachedGlobalCounter(&g_hCounter, &GUID_SystemPidlChange);
  1453. // Is the cache up to date?
  1454. long lGlobalCount = SHGlobalCounterGetValue(hCounter);
  1455. if (lGlobalCount != g_lPerProcessCount)
  1456. {
  1457. _ClearAllCacheEntrys();
  1458. g_lPerProcessCount = lGlobalCount;
  1459. }
  1460. }
  1461. STDAPI_(void) SHFlushSFCache(void)
  1462. {
  1463. // Increment the shared variable; the per-process versions will no
  1464. // longer match, causing this and/or other processes to refresh their
  1465. // pidl caches when they next need to access a folder.
  1466. if (g_hCounter)
  1467. SHGlobalCounterIncrement(g_hCounter);
  1468. }
  1469. // use SHGetFolderLocation() instead using CSIDL_FLAG_CREATE
  1470. STDAPI_(LPITEMIDLIST) SHCloneSpecialIDList(HWND hwnd, int csidl, BOOL fCreate)
  1471. {
  1472. LPITEMIDLIST pidlReturn;
  1473. if (fCreate)
  1474. csidl |= CSIDL_FLAG_CREATE;
  1475. SHGetSpecialFolderLocation(hwnd, csidl, &pidlReturn);
  1476. return pidlReturn;
  1477. }
  1478. STDAPI SHGetSpecialFolderLocation(HWND hwnd, int csidl, LPITEMIDLIST *ppidl)
  1479. {
  1480. HRESULT hr = SHGetFolderLocation(hwnd, csidl, NULL, 0, ppidl);
  1481. if (hr == S_FALSE)
  1482. hr = E_FAIL; // mail empty case into failure for compat with this API
  1483. return hr;
  1484. }
  1485. // return IDLIST for special folder
  1486. // fCreate encoded in csidl with CSIDL_FLAG_CREATE (new for NT5)
  1487. //
  1488. // in:
  1489. // hwnd should be NULL
  1490. // csidl CSIDL_ value with CSIDL_FLAG_ values ORed in as well
  1491. // dwType must be SHGFP_TYPE_CURRENT
  1492. //
  1493. // out:
  1494. // *ppild NULL on failure or empty, PIDL to be freed by caller on success
  1495. //
  1496. // returns:
  1497. // S_OK *ppidl is non NULL
  1498. // S_FALISE *ppidl is NULL, but valid csidl was passed (folder does not exist)
  1499. // FAILED(hr)
  1500. STDAPI SHGetFolderLocation(HWND hwnd, int csidl, HANDLE hToken, DWORD dwType, LPITEMIDLIST *ppidl)
  1501. {
  1502. const FOLDER_INFO *pfi;
  1503. HRESULT hr;
  1504. *ppidl = NULL; // in case of error or empty
  1505. // -1 is an invalid csidl
  1506. if ((dwType != SHGFP_TYPE_CURRENT) || (-1 == csidl))
  1507. return E_INVALIDARG; // no flags used yet, validate this param
  1508. pfi = _GetFolderInfo(csidl & ~CSIDL_FLAG_MASK);
  1509. if (pfi)
  1510. {
  1511. HANDLE hTokenToFree = NULL;
  1512. if ((hToken == NULL) && (pfi->hKey == HKEY_CURRENT_USER))
  1513. {
  1514. if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hToken))
  1515. hTokenToFree = hToken;
  1516. }
  1517. if (hToken && (pfi->hKey == HKEY_CURRENT_USER))
  1518. {
  1519. // we don't cache PIDLs for other users, do all of the work
  1520. hr = _CreateFolderIDList(hwnd, pfi, hToken, csidl & CSIDL_FLAG_MASK, (LPITEMIDLIST *)ppidl);
  1521. }
  1522. else
  1523. {
  1524. hr = _GetFolderIDListCached(hwnd, pfi, csidl & CSIDL_FLAG_MASK, ppidl);
  1525. }
  1526. if (hTokenToFree)
  1527. CloseHandle(hTokenToFree);
  1528. }
  1529. else
  1530. hr = E_INVALIDARG; // bad CSIDL (apps can check to veryify our support)
  1531. return hr;
  1532. }
  1533. STDAPI_(BOOL) SHGetSpecialFolderPath(HWND hwnd, LPWSTR pszPath, int csidl, BOOL fCreate)
  1534. {
  1535. if (fCreate)
  1536. csidl |= CSIDL_FLAG_CREATE;
  1537. return SHGetFolderPath(hwnd, csidl, NULL, 0, pszPath) == S_OK;
  1538. }
  1539. // in:
  1540. // hwnd should be NULL
  1541. // csidl CSIDL_ value with CSIDL_FLAG_ values ORed in as well
  1542. // dwType must be SHGFP_TYPE_CURRENT
  1543. //
  1544. // out:
  1545. // *pszPath MAX_PATH buffer to get path name, zeroed on failure or empty case
  1546. //
  1547. // returns:
  1548. // S_OK filled in pszPath with path value
  1549. // S_FALSE pszPath is NULL, valid CSIDL value, but this folder does not exist
  1550. // E_FAIL
  1551. STDAPI SHGetFolderPath(HWND hwnd, int csidl, HANDLE hToken, DWORD dwType, LPWSTR pszPath)
  1552. {
  1553. HRESULT hr = E_INVALIDARG;
  1554. const FOLDER_INFO *pfi;
  1555. ASSERT(IS_VALID_WRITE_BUFFER(pszPath, TCHAR, MAX_PATH));
  1556. *pszPath = 0;
  1557. pfi = _GetFolderInfo(csidl & ~CSIDL_FLAG_MASK);
  1558. if (pfi && !(pfi->dwFlags & SDIF_NOT_FILESYS))
  1559. {
  1560. switch (dwType)
  1561. {
  1562. case SHGFP_TYPE_DEFAULT:
  1563. ASSERT((csidl & CSIDL_FLAG_MASK) == 0); // meaningless for default
  1564. hr = _GetFolderDefaultPath(pfi, hToken, pszPath, MAX_PATH); //assumed buffer size!
  1565. break;
  1566. case SHGFP_TYPE_CURRENT:
  1567. {
  1568. HANDLE hTokenToFree = NULL;
  1569. if ((hToken == NULL) && (pfi->hKey == HKEY_CURRENT_USER))
  1570. {
  1571. if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hToken))
  1572. hTokenToFree = hToken;
  1573. }
  1574. hr = _GetFolderPathCached(hwnd, pfi, hToken, csidl & CSIDL_FLAG_MASK, pszPath, MAX_PATH); //assumed buffer size!
  1575. if (hTokenToFree)
  1576. CloseHandle(hTokenToFree);
  1577. }
  1578. break;
  1579. }
  1580. }
  1581. return hr;
  1582. }
  1583. STDAPI SHGetFolderPathA(HWND hwnd, int csidl, HANDLE hToken, DWORD dwType, LPSTR pszPath)
  1584. {
  1585. WCHAR wsz[MAX_PATH];
  1586. HRESULT hr = SHGetFolderPath(hwnd, csidl, hToken, dwType, wsz);
  1587. ASSERT(IS_VALID_WRITE_BUFFER(pszPath, CHAR, MAX_PATH));
  1588. SHUnicodeToAnsi(wsz, pszPath, MAX_PATH);
  1589. return hr;
  1590. }
  1591. STDAPI_(BOOL) SHGetSpecialFolderPathA(HWND hwnd, LPSTR pszPath, int csidl, BOOL fCreate)
  1592. {
  1593. if (fCreate)
  1594. csidl |= CSIDL_FLAG_CREATE;
  1595. return SHGetFolderPathA(hwnd, csidl, NULL, 0, pszPath) == S_OK;
  1596. }
  1597. // Similar to SHGetFolderPath, but appends an optional subdirectory path after
  1598. // the csidl folder path. Handles creating the subdirectories.
  1599. STDAPI SHGetFolderPathAndSubDir(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPCWSTR pszSubDir, LPWSTR pszPath)
  1600. {
  1601. HRESULT hr = SHGetFolderPath(hwnd, csidl, hToken, dwFlags, pszPath);
  1602. if (S_OK == hr && pszSubDir && *pszSubDir)
  1603. {
  1604. // assumed pszPath >= MAX_PATH
  1605. if (!PathAppend(pszPath, pszSubDir))
  1606. {
  1607. hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
  1608. }
  1609. else if (csidl & CSIDL_FLAG_CREATE)
  1610. {
  1611. int err = SHCreateDirectoryEx(NULL, pszPath, NULL);
  1612. if (ERROR_ALREADY_EXISTS == err)
  1613. {
  1614. err = ERROR_SUCCESS;
  1615. }
  1616. hr = HRESULT_FROM_WIN32(err);
  1617. }
  1618. else if (!(csidl & CSIDL_FLAG_DONT_VERIFY))
  1619. {
  1620. DWORD dwAttributes;
  1621. if (PathFileExistsAndAttributes(pszPath, &dwAttributes))
  1622. {
  1623. if ((dwAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
  1624. {
  1625. hr = HRESULT_FROM_WIN32(ERROR_FILE_EXISTS);
  1626. }
  1627. }
  1628. else
  1629. {
  1630. hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
  1631. }
  1632. }
  1633. if (S_OK != hr)
  1634. {
  1635. *pszPath = 0;
  1636. }
  1637. }
  1638. return hr;
  1639. }
  1640. STDAPI SHGetFolderPathAndSubDirA(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPCSTR pszSubDir, LPSTR pszPath)
  1641. {
  1642. WCHAR wsz[MAX_PATH];
  1643. WCHAR wszSubDir[MAX_PATH];
  1644. SHAnsiToUnicode(pszSubDir, wszSubDir, MAX_PATH);
  1645. HRESULT hr = SHGetFolderPathAndSubDir(hwnd, csidl, hToken, dwFlags, wszSubDir, wsz);
  1646. ASSERT(IS_VALID_WRITE_BUFFER(pszPath, CHAR, MAX_PATH));
  1647. SHUnicodeToAnsi(wsz, pszPath, MAX_PATH);
  1648. return hr;
  1649. }
  1650. // HRESULT SHSetFolderPath (int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)
  1651. //
  1652. // in:
  1653. // csidl CSIDL_ value with CSIDL_FLAG_ values ORed in as well
  1654. // dwFlags reserved: should be 0x00000000
  1655. // pszPath path to change shell folder to (will optionally be unexpanded)
  1656. //
  1657. // returns:
  1658. // S_OK function succeeded and flushed cache
  1659. STDAPI SHSetFolderPath(int csidl, HANDLE hToken, DWORD dwFlags, LPCTSTR pszPath)
  1660. {
  1661. HRESULT hr = E_INVALIDARG;
  1662. // Validate csidl and dwFlags. Add extra valid flags as needed.
  1663. RIPMSG(((csidl & CSIDL_FLAG_MASK) & ~(CSIDL_FLAG_DONT_UNEXPAND | 0x00000000)) == 0, "SHSetFolderPath: CSIDL flag(s) invalid");
  1664. RIPMSG(dwFlags == 0, "SHSetFolderPath: dwFlags parameter must be 0x00000000");
  1665. // Exit with E_INVALIDARG if bad parameters.
  1666. if ((((csidl & CSIDL_FLAG_MASK) & ~(CSIDL_FLAG_DONT_UNEXPAND | 0x00000000)) != 0) ||
  1667. (dwFlags != 0) ||
  1668. (pszPath == NULL) ||
  1669. (pszPath[0] == 0))
  1670. {
  1671. return hr;
  1672. }
  1673. const FOLDER_INFO *pfi = _GetFolderInfo(csidl & ~CSIDL_FLAG_MASK);
  1674. // Only allow setting for SDIF_NOT_FILESYS is clear
  1675. // SDIF_NOT_TRACKED is clear
  1676. // SDIF_CANT_MOVE_RENAME is clear
  1677. // and for non-NULL value
  1678. // If HKLM is used then rely on security or registry restrictions
  1679. // to enforce whether the change can be made.
  1680. if ((pfi != NULL) &&
  1681. ((pfi->dwFlags & (SDIF_NOT_FILESYS | SDIF_NOT_TRACKED | SDIF_CANT_MOVE_RENAME)) == 0))
  1682. {
  1683. BOOL fSuccessfulUnexpand, fSuccessfulExpand, fEmptyOrNullPath;
  1684. LONG lError;
  1685. HANDLE hTokenToFree;
  1686. TCHAR szPath[MAX_PATH];
  1687. TCHAR szExpandedPath[MAX_PATH]; // holds expanded path for "Shell Folder" compat key
  1688. LPCTSTR pszWritePath;
  1689. hTokenToFree = NULL;
  1690. if ((hToken == NULL) && (pfi->hKey == HKEY_CURRENT_USER))
  1691. {
  1692. if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hToken))
  1693. {
  1694. hTokenToFree = hToken;
  1695. }
  1696. }
  1697. fEmptyOrNullPath = ((pszPath == NULL) || (pszPath[0] == 0));
  1698. if (fEmptyOrNullPath)
  1699. {
  1700. HKEY hKeyDefaultUser;
  1701. pszWritePath = NULL;
  1702. if (SUCCEEDED(_OpenKeyForFolder(pfi, (HANDLE)-1, TEXT("User Shell Folders"), &hKeyDefaultUser)))
  1703. {
  1704. DWORD cbPath = sizeof(szPath);
  1705. if (ERROR_SUCCESS == SHRegGetValue(hKeyDefaultUser, NULL, pfi->pszValueName,
  1706. SRRF_RT_REG_SZ | SRRF_NOEXPAND, NULL, szPath, &cbPath))
  1707. {
  1708. pszWritePath = szPath;
  1709. }
  1710. RegCloseKey(hKeyDefaultUser);
  1711. }
  1712. fSuccessfulUnexpand = TRUE;
  1713. }
  1714. else if (csidl & CSIDL_FLAG_DONT_UNEXPAND)
  1715. {
  1716. // Does the caller want to write the string as is? Leave
  1717. // it alone if so.
  1718. pszWritePath = pszPath;
  1719. fSuccessfulUnexpand = TRUE;
  1720. }
  1721. else
  1722. {
  1723. if (pfi->hKey == HKEY_CURRENT_USER)
  1724. {
  1725. fSuccessfulUnexpand = (PathUnExpandEnvStringsForUser(hToken, pszPath, szPath, ARRAYSIZE(szPath)) != FALSE);
  1726. }
  1727. else
  1728. {
  1729. fSuccessfulUnexpand = FALSE;
  1730. }
  1731. // Choose the appropriate source if the unexpansion was successful or not.
  1732. // Either way the unexpansion failure should be ignored.
  1733. if (fSuccessfulUnexpand)
  1734. {
  1735. pszWritePath = szPath;
  1736. }
  1737. else
  1738. {
  1739. fSuccessfulUnexpand = TRUE;
  1740. pszWritePath = pszPath;
  1741. }
  1742. }
  1743. if (fSuccessfulUnexpand)
  1744. {
  1745. HKEY hKeyUser, hKeyUSF, hKeyToFree;
  1746. // we also get the fully expanded path so that we can write it out to the "Shell Folders" key for apps that depend on
  1747. // the old registry values
  1748. fSuccessfulExpand = (SHExpandEnvironmentStringsForUser(hToken, pszPath, szExpandedPath, ARRAYSIZE(szExpandedPath)) != 0);
  1749. // Get either the current users HKCU or HKU\SID if a token
  1750. // was specified and running in NT.
  1751. if (hToken && GetUserProfileKey(hToken, KEY_READ, &hKeyUser))
  1752. {
  1753. hKeyToFree = hKeyUser;
  1754. }
  1755. else
  1756. {
  1757. hKeyUser = pfi->hKey;
  1758. hKeyToFree = NULL;
  1759. }
  1760. // Open the key to the User Shell Folders and write the string
  1761. // there. Clear the shell folder cache.
  1762. // NOTE: This functionality is duplicated in SetFolderPath but
  1763. // that function deals with the USF key only. This function
  1764. // requires HKU\SID so while there is identical functionality
  1765. // from the point of view of settings the USF value that is
  1766. // where it ends. To make this function simple it just writes
  1767. // the value to registry itself.
  1768. // Additional note: there is a threading issue here with
  1769. // clearing the cache entry incrementing the counter. This
  1770. // should be locked access.
  1771. lError = RegOpenKeyEx(hKeyUser, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders"),
  1772. 0, KEY_READ | KEY_WRITE, &hKeyUSF);
  1773. if (lError == ERROR_SUCCESS)
  1774. {
  1775. if (pszWritePath)
  1776. {
  1777. lError = RegSetValueEx(hKeyUSF, pfi->pszValueName, 0, REG_EXPAND_SZ,
  1778. (LPBYTE)pszWritePath, (lstrlen(pszWritePath) + 1) * sizeof(TCHAR));
  1779. }
  1780. else
  1781. {
  1782. lError = RegDeleteValue(hKeyUSF, pfi->pszValueName);
  1783. }
  1784. RegCloseKey(hKeyUSF);
  1785. // nuke the cache state for this folder
  1786. _ClearCacheEntry(pfi);
  1787. // and all folders that might be aliased as those
  1788. // could be related to this folder (under MyDocs for example)
  1789. // and now their aliases forms my no longer be valid
  1790. _ClearAllAliasCacheEntrys();
  1791. g_lPerProcessCount = SHGlobalCounterIncrement(g_hCounter);
  1792. }
  1793. // update the old "Shell Folders" value for compat
  1794. if ((lError == ERROR_SUCCESS) && fSuccessfulExpand)
  1795. {
  1796. HKEY hkeySF;
  1797. if (RegOpenKeyEx(hKeyUser, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"),
  1798. 0, KEY_READ | KEY_WRITE, &hkeySF) == ERROR_SUCCESS)
  1799. {
  1800. if (pszWritePath)
  1801. {
  1802. RegSetValueEx(hkeySF, pfi->pszValueName, 0, REG_SZ,
  1803. (LPBYTE)szExpandedPath, (lstrlen(szExpandedPath) + 1) * sizeof(TCHAR));
  1804. }
  1805. else
  1806. {
  1807. RegDeleteValue(hkeySF, pfi->pszValueName);
  1808. }
  1809. RegCloseKey(hkeySF);
  1810. }
  1811. }
  1812. if ((lError == ERROR_SUCCESS) && (pfi->hKey == HKEY_CURRENT_USER))
  1813. {
  1814. switch (csidl & ~CSIDL_FLAG_MASK)
  1815. {
  1816. case CSIDL_APPDATA:
  1817. {
  1818. HKEY hKeyVolatileEnvironment;
  1819. // In the case of AppData there is a matching environment variable
  1820. // for this shell folder. Make sure the place in the registry where
  1821. // userenv.dll places this value is updated and correct so that when
  1822. // the user context is created by winlogon it will have the updated
  1823. // value.
  1824. // It's probably also a good thing to check for a %APPDATA% variable
  1825. // in the calling process' context but this would only be good for
  1826. // the life of the process. What is really required is a mechanism
  1827. // to change the environment variable for the entire logon session.
  1828. lError = RegOpenKeyEx(hKeyUser, TEXT("Volatile Environment"), 0,
  1829. KEY_READ | KEY_WRITE, &hKeyVolatileEnvironment);
  1830. if (lError == ERROR_SUCCESS)
  1831. {
  1832. if (SUCCEEDED(SHGetFolderPath(NULL, csidl | CSIDL_FLAG_DONT_VERIFY,
  1833. hToken, SHGFP_TYPE_CURRENT, szPath)))
  1834. {
  1835. lError = RegSetValueEx(hKeyVolatileEnvironment, TEXT("APPDATA"),
  1836. 0, REG_SZ, (LPBYTE)szPath, (lstrlen(szPath) + 1) * sizeof(TCHAR));
  1837. }
  1838. RegCloseKey(hKeyVolatileEnvironment);
  1839. }
  1840. break;
  1841. }
  1842. }
  1843. }
  1844. if (hKeyToFree)
  1845. {
  1846. RegCloseKey(hKeyToFree);
  1847. }
  1848. if (lError == ERROR_SUCCESS)
  1849. {
  1850. hr = S_OK;
  1851. }
  1852. else
  1853. {
  1854. hr = HRESULT_FROM_WIN32(lError);
  1855. }
  1856. }
  1857. if (hTokenToFree)
  1858. {
  1859. CloseHandle(hTokenToFree);
  1860. }
  1861. SHChangeDWORDAsIDList dwidl = { sizeof(dwidl) - sizeof(dwidl.cbZero), SHCNEE_UPDATEFOLDERLOCATION, csidl & ~CSIDL_FLAG_MASK, 0};
  1862. SHChangeNotify(SHCNE_EXTENDED_EVENT, SHCNF_ONLYNOTIFYINTERNALS | SHCNF_IDLIST, (LPCITEMIDLIST)&dwidl, NULL);
  1863. }
  1864. return hr;
  1865. }
  1866. STDAPI SHSetFolderPathA(int csidl, HANDLE hToken, DWORD dwType, LPCSTR pszPath)
  1867. {
  1868. WCHAR wsz[MAX_PATH];
  1869. SHAnsiToUnicode(pszPath, wsz, ARRAYSIZE(wsz));
  1870. return SHSetFolderPath(csidl, hToken, dwType, wsz);
  1871. }
  1872. // NOTE: called from DllEntry
  1873. void SpecialFolderIDTerminate()
  1874. {
  1875. ASSERTDLLENTRY // does not require a critical section
  1876. _ClearAllCacheEntrys();
  1877. if (g_hCounter)
  1878. {
  1879. CloseHandle(g_hCounter);
  1880. g_hCounter = NULL;
  1881. }
  1882. }
  1883. // update our cache and the registry for pfi with pszPath. this also invalidates the
  1884. // cache in other processes so they stay in sync
  1885. void SetFolderPath(const FOLDER_INFO *pfi, LPCTSTR pszPath)
  1886. {
  1887. _ClearCacheEntry(pfi);
  1888. if (pszPath)
  1889. {
  1890. HKEY hk;
  1891. if (SUCCEEDED(_OpenKeyForFolder(pfi, NULL, TEXT("User Shell Folders"), &hk)))
  1892. {
  1893. LONG err;
  1894. TCHAR szDefaultPath[MAX_PATH];
  1895. // Check for an existing path, and if the unexpanded version
  1896. // of the existing path does not match the new path, then
  1897. // write the new path to the registry.
  1898. //
  1899. // RegQueryPath expands the environment variables for us
  1900. // so we can't just blindly set the new value to the registry.
  1901. //
  1902. RegQueryPath(hk, pfi->pszValueName, szDefaultPath, ARRAYSIZE(szDefaultPath));
  1903. if (lstrcmpi(szDefaultPath, pszPath) != 0)
  1904. {
  1905. // The paths are different. Write to the registry as file
  1906. // system path.
  1907. err = SHRegSetPath(hk, NULL, pfi->pszValueName, pszPath, 0);
  1908. }
  1909. else
  1910. {
  1911. err = ERROR_SUCCESS;
  1912. }
  1913. // clear out any temp paths
  1914. RegSetFolderPath(pfi, TEXT("User Shell Folders\\New"), NULL);
  1915. if (err == ERROR_SUCCESS)
  1916. {
  1917. // this will force a new creation (see TRUE as fCreate).
  1918. // This will also copy the path from "User Shell Folders"
  1919. // to "Shell Folders".
  1920. LPITEMIDLIST pidl;
  1921. if (S_OK == _GetFolderIDListCached(NULL, pfi, CSIDL_FLAG_CREATE, &pidl))
  1922. {
  1923. ILFree(pidl);
  1924. }
  1925. else
  1926. {
  1927. // failed! null out the entry. this will go back to our default
  1928. RegDeleteValue(hk, pfi->pszValueName);
  1929. _ClearCacheEntry(pfi);
  1930. }
  1931. }
  1932. RegCloseKey(hk);
  1933. }
  1934. }
  1935. else
  1936. {
  1937. RegSetFolderPath(pfi, TEXT("User Shell Folders"), NULL);
  1938. // clear out any temp paths
  1939. RegSetFolderPath(pfi, TEXT("User Shell Folders\\New"), NULL);
  1940. }
  1941. // set the global different from the per process variable
  1942. // to signal an update needs to happen other processes
  1943. g_lPerProcessCount = SHGlobalCounterIncrement(g_hCounter);
  1944. }
  1945. // file system change notifies come in here AFTER the folders have been moved/deleted
  1946. // we fix up the registry to match what occured in the file system
  1947. EXTERN_C void SFP_FSEvent(LONG lEvent, LPITEMIDLIST pidl, LPITEMIDLIST pidlExtra)
  1948. {
  1949. const FOLDER_INFO *pfi;
  1950. TCHAR szSrc[MAX_PATH];
  1951. if (!(lEvent & (SHCNE_RENAMEFOLDER | SHCNE_RMDIR | SHCNE_MKDIR)) ||
  1952. !SHGetPathFromIDList(pidl, szSrc) ||
  1953. (pidlExtra && ILIsEqual(pidl, pidlExtra))) // when volume label changes, pidl==pidlExtra so we detect this case and skip it for perf
  1954. {
  1955. return;
  1956. }
  1957. for (pfi = c_rgFolderInfo; pfi->id != -1; pfi++)
  1958. {
  1959. if (0 == (pfi->dwFlags & (SDIF_NOT_TRACKED | SDIF_NOT_FILESYS)))
  1960. {
  1961. TCHAR szCurrent[MAX_PATH];
  1962. if (S_OK == _GetFolderPathCached(NULL, pfi, NULL, CSIDL_FLAG_DONT_VERIFY, szCurrent, ARRAYSIZE(szCurrent)) &&
  1963. PathIsEqualOrSubFolder(szSrc, szCurrent))
  1964. {
  1965. TCHAR szDest[MAX_PATH];
  1966. szDest[0] = 0;
  1967. if (lEvent & SHCNE_RMDIR)
  1968. {
  1969. // complete the "move accross volume" case
  1970. HKEY hk;
  1971. if (SUCCEEDED(_OpenKeyForFolder(pfi, NULL, TEXT("User Shell Folders\\New"), &hk)))
  1972. {
  1973. RegQueryPath(hk, pfi->pszValueName, szDest, ARRAYSIZE(szDest));
  1974. RegCloseKey(hk);
  1975. }
  1976. }
  1977. else if (pidlExtra)
  1978. {
  1979. SHGetPathFromIDList(pidlExtra, szDest);
  1980. }
  1981. if (szDest[0])
  1982. {
  1983. // rename the specal folder
  1984. UINT cch = PathCommonPrefix(szCurrent, szSrc, NULL);
  1985. ASSERT(cch != 0);
  1986. if (szCurrent[cch])
  1987. {
  1988. if (PathAppend(szDest, szCurrent + cch))
  1989. SetFolderPath(pfi, szDest);
  1990. }
  1991. else
  1992. {
  1993. SetFolderPath(pfi, szDest);
  1994. }
  1995. }
  1996. }
  1997. }
  1998. }
  1999. }
  2000. ULONG _ILGetChildOffset(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlChild)
  2001. {
  2002. DWORD cbOff = 0;
  2003. LPCITEMIDLIST pidlChildT = ILFindChild(pidlParent, pidlChild);
  2004. if (pidlChildT)
  2005. {
  2006. cbOff = (ULONG)((LPBYTE)pidlChildT - (LPBYTE)pidlChild);
  2007. }
  2008. return cbOff;
  2009. }
  2010. // returns the first special folder CSIDL_ id that is a parent
  2011. // of the passed in pidl or 0 if not found. only CSIDL_ entries marked as
  2012. // SDIF_SHORTCUT_RELATIVE are considered for this.
  2013. //
  2014. // returns:
  2015. // CSIDL_ values
  2016. // *pcbOffset offset into pidl
  2017. STDAPI_(int) GetSpecialFolderParentIDAndOffset(LPCITEMIDLIST pidl, ULONG *pcbOffset)
  2018. {
  2019. int iRet = 0; // everything is desktop relative
  2020. const FOLDER_INFO *pfi;
  2021. *pcbOffset = 0;
  2022. for (pfi = c_rgFolderInfo; pfi->id != -1; pfi++)
  2023. {
  2024. if (pfi->dwFlags & SDIF_SHORTCUT_RELATIVE)
  2025. {
  2026. LPITEMIDLIST pidlFolder;
  2027. if (S_OK == _GetFolderIDListCached(NULL, pfi, 0, &pidlFolder))
  2028. {
  2029. ULONG cbOff = _ILGetChildOffset(pidlFolder, pidl);
  2030. if (cbOff > *pcbOffset)
  2031. {
  2032. *pcbOffset = cbOff;
  2033. iRet = pfi->id;
  2034. }
  2035. ILFree(pidlFolder);
  2036. }
  2037. }
  2038. }
  2039. return iRet;
  2040. }
  2041. // note, only works for file system path (bummer, we would like others supported too)
  2042. STDAPI_(BOOL) MakeShellURLFromPath(LPCTSTR pszPathIn, LPTSTR pszUrl, DWORD dwCch)
  2043. {
  2044. const FOLDER_INFO *pfi;
  2045. for (pfi = c_rgFolderInfo; pfi->id != -1; pfi++)
  2046. {
  2047. if ((pfi->dwFlags & SDIF_SHORTCUT_RELATIVE) &&
  2048. !(pfi->dwFlags & SDIF_NOT_FILESYS))
  2049. {
  2050. TCHAR szCurrent[MAX_PATH];
  2051. if (S_OK == _GetFolderPathCached(NULL, pfi, 0, CSIDL_FLAG_DONT_VERIFY, szCurrent, ARRAYSIZE(szCurrent)))
  2052. {
  2053. if (PathIsPrefix(szCurrent, pszPathIn))
  2054. {
  2055. StrCpyN(pszUrl, TEXT("shell:"), dwCch);
  2056. StrCatBuff(pszUrl, pfi->pszValueName, dwCch);
  2057. if ((dwCch >= MAX_PATH) && PathAppend(pszUrl, &pszPathIn[lstrlen(szCurrent)]))
  2058. {
  2059. return TRUE;
  2060. }
  2061. }
  2062. }
  2063. }
  2064. }
  2065. return FALSE;
  2066. }
  2067. STDAPI_(BOOL) MakeShellURLFromPathA(LPCSTR pszPathIn, LPSTR pszUrl, DWORD dwCch)
  2068. {
  2069. WCHAR szTmp1[MAX_PATH], szTmp2[MAX_PATH];
  2070. SHAnsiToUnicode(pszPathIn, szTmp1, ARRAYSIZE(szTmp1));
  2071. BOOL bRet = MakeShellURLFromPathW(szTmp1, szTmp2, ARRAYSIZE(szTmp2));
  2072. SHUnicodeToAnsi(szTmp2, pszUrl, dwCch);
  2073. return bRet;
  2074. }
  2075. BOOL MoveBlockedByPolicy(const FOLDER_INFO *pfi)
  2076. {
  2077. BOOL bRet = FALSE;
  2078. if (pfi->dwFlags & SDIF_POLICY_NO_MOVE)
  2079. {
  2080. // similar to code in mydocs.dll
  2081. TCHAR szValue[128];
  2082. wnsprintf(szValue, ARRAYSIZE(szValue), TEXT("Disable%sDirChange"), pfi->pszValueName);
  2083. if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER,
  2084. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"),
  2085. szValue, NULL, NULL, NULL))
  2086. {
  2087. bRet = TRUE;
  2088. }
  2089. }
  2090. return bRet;
  2091. }
  2092. // this is called from the copy engine (like all other copy hooks)
  2093. // this is where we put up UI blocking the delete/move of some special folders
  2094. EXTERN_C int PathCopyHookCallback(HWND hwnd, UINT wFunc, LPCTSTR pszSrc, LPCTSTR pszDest)
  2095. {
  2096. int ret = IDYES;
  2097. if ((wFunc == FO_DELETE) || (wFunc == FO_MOVE) || (wFunc == FO_RENAME))
  2098. {
  2099. const FOLDER_INFO *pfi;
  2100. // is one of our system directories being affected?
  2101. for (pfi = c_rgFolderInfo; ret == IDYES && pfi->id != -1; pfi++)
  2102. {
  2103. // even non tracked folders (windows, system) come through here
  2104. if (0 == (pfi->dwFlags & SDIF_NOT_FILESYS))
  2105. {
  2106. TCHAR szCurrent[MAX_PATH];
  2107. if (S_OK == _GetFolderPathCached(NULL, pfi, NULL, CSIDL_FLAG_DONT_VERIFY, szCurrent, ARRAYSIZE(szCurrent)) &&
  2108. PathIsEqualOrSubFolder(pszSrc, szCurrent))
  2109. {
  2110. // Yes
  2111. if (wFunc == FO_DELETE)
  2112. {
  2113. if (pfi->dwFlags & SDIF_CAN_DELETE)
  2114. {
  2115. SetFolderPath(pfi, NULL); // Let them delete some folders
  2116. }
  2117. else
  2118. {
  2119. ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_CANTDELETESPECIALDIR),
  2120. MAKEINTRESOURCE(IDS_DELETE), MB_OK | MB_ICONINFORMATION, PathFindFileName(pszSrc));
  2121. ret = IDNO;
  2122. }
  2123. }
  2124. else
  2125. {
  2126. int idSrc = PathGetDriveNumber(pszSrc);
  2127. int idDest = PathGetDriveNumber(pszDest);
  2128. ASSERT((wFunc == FO_MOVE) || (wFunc == FO_RENAME));
  2129. if ((pfi->dwFlags & SDIF_CANT_MOVE_RENAME) ||
  2130. ((idSrc != -1) && (idDest == -1) && !(pfi->dwFlags & SDIF_NETWORKABLE)) ||
  2131. ((idSrc != idDest) && PathIsRemovable(pszDest) && !(pfi->dwFlags & SDIF_REMOVABLE)) ||
  2132. MoveBlockedByPolicy(pfi))
  2133. {
  2134. ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_CANTMOVESPECIALDIRHERE),
  2135. wFunc == FO_MOVE ? MAKEINTRESOURCE(IDS_MOVE) : MAKEINTRESOURCE(IDS_RENAME),
  2136. MB_ICONERROR, PathFindFileName(pszSrc));
  2137. ret = IDNO;
  2138. }
  2139. else
  2140. {
  2141. //
  2142. // store this info here
  2143. // if we need it we will use it.
  2144. //
  2145. // we used to try to optimise in the case of same
  2146. // volume renames. we assumed that if it was the same
  2147. // volume we would later get a SHCNE_RENAME. but sometimes
  2148. // we have to do a copy even on the same volume. so
  2149. // we need to always set this value.
  2150. //
  2151. RegSetFolderPath(pfi, TEXT("User Shell Folders\\New"), pszDest);
  2152. }
  2153. }
  2154. }
  2155. }
  2156. }
  2157. }
  2158. return ret;
  2159. }
  2160. // Given a key name ("programs", "desktop", "start menu"), convert it to
  2161. // the corresponding CSIDL.
  2162. STDAPI_(int) SHGetSpecialFolderID(LPCWSTR pszName)
  2163. {
  2164. // make sure g_aFolderCache can be indexed by the CSIDL values
  2165. COMPILETIME_ASSERT((ARRAYSIZE(g_aFolderCache) - 1) == CSIDL_COMPUTERSNEARME);
  2166. for (int i = 0; c_rgFolderInfo[i].id != -1; i++)
  2167. {
  2168. if (c_rgFolderInfo[i].pszValueName &&
  2169. (0 == StrCmpI(pszName, c_rgFolderInfo[i].pszValueName)))
  2170. {
  2171. return c_rgFolderInfo[i].id;
  2172. }
  2173. }
  2174. return -1;
  2175. }
  2176. // Return the special folder ID, if this folder is one of them.
  2177. // At this point, we handle PROGRAMS folder only.
  2178. //
  2179. // GetSpecialFolderID()
  2180. // this allows a list of CSIDLs to be passed in.
  2181. // they will be searched in order for the specified csidl
  2182. // and the path will be checked against it.
  2183. // if -1 is specified as the csidl, then all of array entries should
  2184. // be checked for a match with the folder.
  2185. //
  2186. int GetSpecialFolderID(LPCTSTR pszFolder, const int *rgcsidl, UINT count)
  2187. {
  2188. for (UINT i = 0; i < count; i++)
  2189. {
  2190. int csidlSpecial = rgcsidl[i] & ~TEST_SUBFOLDER;
  2191. TCHAR szPath[MAX_PATH];
  2192. if (S_OK == SHGetFolderPath(NULL, csidlSpecial | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szPath))
  2193. {
  2194. if (((rgcsidl[i] & TEST_SUBFOLDER) && PathIsEqualOrSubFolder(szPath, pszFolder)) ||
  2195. (lstrcmpi(szPath, pszFolder) == 0))
  2196. {
  2197. return csidlSpecial;
  2198. }
  2199. }
  2200. }
  2201. return -1;
  2202. }
  2203. /**
  2204. * Tacks a name onto a CSIDL, e.g. gets a pidl for
  2205. * CSIDL_COMMON_PICTURES\Sample Pictures
  2206. * if it exists.
  2207. * Called must free ppidlSampleMedia
  2208. * Note: The folder is *not* created if it does not exist.
  2209. */
  2210. HRESULT _AppendPathToPIDL(int nAllUsersMediaFolder, LPCWSTR pszName, LPITEMIDLIST *ppidlSampleMedia)
  2211. {
  2212. LPITEMIDLIST pidlAllUsersMedia;
  2213. HRESULT hr = SHGetFolderLocation(NULL, nAllUsersMediaFolder, NULL, 0, &pidlAllUsersMedia);
  2214. if (SUCCEEDED(hr))
  2215. {
  2216. // Get the shellfolder for this guy.
  2217. IShellFolder *psf;
  2218. hr = SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidlAllUsersMedia, &psf));
  2219. if (SUCCEEDED(hr))
  2220. {
  2221. // And now the pidl for the sample folder
  2222. LPITEMIDLIST pidlSampleMediaRel;
  2223. ULONG dwAttributes = 0;
  2224. hr = psf->ParseDisplayName(NULL, NULL, (LPOLESTR)pszName, NULL, &pidlSampleMediaRel, &dwAttributes);
  2225. if (SUCCEEDED(hr))
  2226. {
  2227. // It exists!
  2228. hr = SHILCombine(pidlAllUsersMedia, pidlSampleMediaRel, ppidlSampleMedia);
  2229. ILFree(pidlSampleMediaRel);
  2230. }
  2231. psf->Release();
  2232. }
  2233. ILFree(pidlAllUsersMedia);
  2234. }
  2235. return hr;
  2236. }
  2237. /**
  2238. * Returns a pidl to the samples folder under a particular CSIDL
  2239. * Caller must free ppidlSampleMedia
  2240. */
  2241. HRESULT _ParseSubfolderResource(int csidl, UINT ids, LPITEMIDLIST *ppidl)
  2242. {
  2243. WCHAR szSub[MAX_PATH];
  2244. LoadDefaultString(ids, szSub, ARRAYSIZE(szSub));
  2245. return _AppendPathToPIDL(csidl, szSub, ppidl);
  2246. }
  2247. HRESULT SHGetSampleMediaFolder(int nAllUsersMediaFolder, LPITEMIDLIST *ppidlSampleMedia)
  2248. {
  2249. UINT uID = -1;
  2250. switch (nAllUsersMediaFolder)
  2251. {
  2252. case CSIDL_COMMON_PICTURES:
  2253. uID = IDS_SAMPLEPICTURES;
  2254. break;
  2255. case CSIDL_COMMON_MUSIC:
  2256. uID = IDS_SAMPLEMUSIC;
  2257. break;
  2258. default:
  2259. ASSERT(FALSE);
  2260. return E_INVALIDARG;
  2261. break;
  2262. }
  2263. return _ParseSubfolderResource(nAllUsersMediaFolder, uID, ppidlSampleMedia);
  2264. }
  2265. void _CreateLinkToSampleMedia(LPCWSTR pszNewFolderPath, int nAllUsersMediaFolder, UINT uIDSampleFolderName)
  2266. {
  2267. if (!IsOS(OS_DOMAINMEMBER))
  2268. {
  2269. LPITEMIDLIST pidl;
  2270. if (SUCCEEDED(SHGetSampleMediaFolder(nAllUsersMediaFolder, &pidl)))
  2271. {
  2272. // Check to make sure the link doesn't already exist.
  2273. WCHAR szSampleFolderName[MAX_PATH];
  2274. WCHAR szFullLnkPath[MAX_PATH];
  2275. LoadString(HINST_THISDLL, uIDSampleFolderName, szSampleFolderName, ARRAYSIZE(szSampleFolderName));
  2276. StrCatBuff(szSampleFolderName, L".lnk", ARRAYSIZE(szSampleFolderName));
  2277. if (PathCombine(szFullLnkPath, pszNewFolderPath, szSampleFolderName))
  2278. {
  2279. if (!PathFileExists(szFullLnkPath))
  2280. {
  2281. // MUI-WARNING - we are not doing a SHSetLocalizedName for this link - ZekeL - 15-MAY-2001
  2282. // this means that this link is always created in the default system UI language
  2283. // we should probably call SHSetLocalizedName() here but i am scared right now of perf implications.
  2284. CreateLinkToPidl(pidl, pszNewFolderPath, NULL, 0);
  2285. }
  2286. }
  2287. ILFree(pidl);
  2288. }
  2289. }
  2290. }
  2291. void _InitFolder(LPCTSTR pszPath, UINT idsInfoTip, HINSTANCE hinstIcon, UINT idiIcon)
  2292. {
  2293. // Set the default custom settings for the folder.
  2294. SHFOLDERCUSTOMSETTINGS fcs = {sizeof(fcs), 0};
  2295. TCHAR szInfoTip[128];
  2296. TCHAR szPath[MAX_PATH];
  2297. // Get the infotip for this folder
  2298. if (idsInfoTip)
  2299. {
  2300. wnsprintf(szInfoTip,ARRAYSIZE(szInfoTip),TEXT("@Shell32.dll,-%u"),idsInfoTip);
  2301. fcs.pszInfoTip = szInfoTip;
  2302. fcs.cchInfoTip = ARRAYSIZE(szInfoTip);
  2303. fcs.dwMask |= FCSM_INFOTIP;
  2304. }
  2305. // this will be encoded to the %SystemRoot% style path when setting the folder information.
  2306. if (idiIcon)
  2307. {
  2308. GetModuleFileName(hinstIcon, szPath, ARRAYSIZE(szPath));
  2309. fcs.pszIconFile = szPath;
  2310. fcs.cchIconFile = ARRAYSIZE(szPath);
  2311. fcs.iIconIndex = idiIcon;
  2312. fcs.dwMask |= FCSM_ICONFILE;
  2313. }
  2314. // NOTE: we need FCS_FORCEWRITE because we didn't used to specify iIconIndex
  2315. // and so "0" was written to the ini file. When we upgrade, this API won't
  2316. // fix the ini file unless we pass FCS_FORCEWRITE
  2317. SHGetSetFolderCustomSettings(&fcs, pszPath, FCS_FORCEWRITE);
  2318. }
  2319. void _InitMyPictures(int id, LPCTSTR pszPath)
  2320. {
  2321. // Get the path to the icon. We reference MyDocs.dll for backwards compat.
  2322. HINSTANCE hinstMyDocs = LoadLibrary(TEXT("mydocs.dll"));
  2323. if (hinstMyDocs)
  2324. {
  2325. _InitFolder(pszPath, IDS_FOLDER_MYPICS_TT, hinstMyDocs, -101); // known index for IDI_MYPICS in mydocs.dll
  2326. FreeLibrary(hinstMyDocs);
  2327. }
  2328. }
  2329. void _InitMyMusic(int id, LPCTSTR pszPath)
  2330. {
  2331. _InitFolder(pszPath, IDS_FOLDER_MYMUSIC_TT, HINST_THISDLL, -IDI_MYMUSIC);
  2332. }
  2333. void _InitPerUserMyPictures(int id, LPCTSTR pszPath)
  2334. {
  2335. _InitMyPictures(id, pszPath);
  2336. _CreateLinkToSampleMedia(pszPath, CSIDL_COMMON_PICTURES, IDS_SAMPLEPICTURES);
  2337. }
  2338. void _InitPerUserMyMusic(int id, LPCTSTR pszPath)
  2339. {
  2340. _InitMyMusic(id, pszPath);
  2341. _CreateLinkToSampleMedia(pszPath, CSIDL_COMMON_MUSIC, IDS_SAMPLEMUSIC);
  2342. }
  2343. void _InitMyVideos(int id, LPCTSTR pszPath)
  2344. {
  2345. _InitFolder(pszPath, IDS_FOLDER_MYVIDEOS_TT, HINST_THISDLL, -IDI_MYVIDEOS);
  2346. }
  2347. void _InitRecentDocs(int id, LPCTSTR pszPath)
  2348. {
  2349. _InitFolder(pszPath, IDS_FOLDER_RECENTDOCS_TT, HINST_THISDLL, -IDI_STDOCS);
  2350. }
  2351. void _InitFavorites(int id, LPCTSTR pszPath)
  2352. {
  2353. _InitFolder(pszPath, 0, HINST_THISDLL, -IDI_FAVORITES);
  2354. }