Source code of Windows XP (NT5)
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.

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