xref: /reactos/dll/win32/shell32/wine/shellpath.c (revision 0623a6f8)
1 /*
2  * Path Functions
3  *
4  * Copyright 1998, 1999, 2000 Juergen Schmied
5  * Copyright 2004 Juan Lang
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * NOTES:
22  *
23  * Many of these functions are in SHLWAPI.DLL also
24  *
25  */
26 
27 #define WIN32_NO_STATUS
28 #define _INC_WINDOWS
29 #define COBJMACROS
30 
31 #include <wine/config.h>
32 
33 #include <windef.h>
34 #include <winbase.h>
35 #include <shlobj.h>
36 #include <undocshell.h>
37 #include <shlwapi.h>
38 #include <sddl.h>
39 #include <wine/debug.h>
40 #include <wine/unicode.h>
41 
42 #include <userenv.h>
43 
44 #include "pidl.h"
45 #include "shell32_main.h"
46 #include "shresdef.h"
47 
48 WINE_DEFAULT_DEBUG_CHANNEL(shell);
49 
50 static const BOOL is_win64 = sizeof(void *) > sizeof(int);
51 
52 /*
53 	########## Combining and Constructing paths ##########
54 */
55 
56 /*************************************************************************
57  * PathAppend		[SHELL32.36]
58  */
59 BOOL WINAPI PathAppendAW(
60 	LPVOID lpszPath1,
61 	LPCVOID lpszPath2)
62 {
63 	if (SHELL_OsIsUnicode())
64 	  return PathAppendW(lpszPath1, lpszPath2);
65 	return PathAppendA(lpszPath1, lpszPath2);
66 }
67 
68 /*************************************************************************
69  * PathGetExtensionA		[internal]
70  *
71  * NOTES
72  *  exported by ordinal
73  *  return value points to the first char after the dot
74  */
75 static LPSTR PathGetExtensionA(LPCSTR lpszPath)
76 {
77 	TRACE("(%s)\n",lpszPath);
78 
79 	lpszPath = PathFindExtensionA(lpszPath);
80 	return (LPSTR)(*lpszPath?(lpszPath+1):lpszPath);
81 }
82 
83 /*************************************************************************
84  * PathGetExtensionW		[internal]
85  */
86 static LPWSTR PathGetExtensionW(LPCWSTR lpszPath)
87 {
88 	TRACE("(%s)\n",debugstr_w(lpszPath));
89 
90 	lpszPath = PathFindExtensionW(lpszPath);
91 	return (LPWSTR)(*lpszPath?(lpszPath+1):lpszPath);
92 }
93 
94 /*************************************************************************
95  * SHPathGetExtension        [SHELL32.158]
96  */
97 LPVOID WINAPI SHPathGetExtensionW(LPCWSTR lpszPath, DWORD void1, DWORD void2)
98 {
99     return PathGetExtensionW(lpszPath);
100 }
101 
102 /*************************************************************************
103  * PathRemoveFileSpec [SHELL32.35]
104  */
105 BOOL WINAPI PathRemoveFileSpecAW(LPVOID lpszPath)
106 {
107 	if (SHELL_OsIsUnicode())
108 	  return PathRemoveFileSpecW(lpszPath);
109 	return PathRemoveFileSpecA(lpszPath);
110 }
111 
112 /*
113 	Path Manipulations
114 */
115 
116 /*************************************************************************
117  * PathGetShortPathA [internal]
118  */
119 static void PathGetShortPathA(LPSTR pszPath)
120 {
121 	CHAR path[MAX_PATH];
122 
123 	TRACE("%s\n", pszPath);
124 
125 	if (GetShortPathNameA(pszPath, path, MAX_PATH))
126 	{
127 	  lstrcpyA(pszPath, path);
128 	}
129 }
130 
131 /*************************************************************************
132  * PathGetShortPathW [internal]
133  */
134 static void PathGetShortPathW(LPWSTR pszPath)
135 {
136 	WCHAR path[MAX_PATH];
137 
138 	TRACE("%s\n", debugstr_w(pszPath));
139 
140 	if (GetShortPathNameW(pszPath, path, MAX_PATH))
141 	{
142 	  lstrcpyW(pszPath, path);
143 	}
144 }
145 
146 /*************************************************************************
147  * PathGetShortPath [SHELL32.92]
148  */
149 VOID WINAPI PathGetShortPathAW(LPVOID pszPath)
150 {
151 	if(SHELL_OsIsUnicode())
152 	  PathGetShortPathW(pszPath);
153 	PathGetShortPathA(pszPath);
154 }
155 
156 /*
157 	########## Path Testing ##########
158 */
159 
160 /*************************************************************************
161  * PathIsRoot		[SHELL32.29]
162  */
163 BOOL WINAPI PathIsRootAW(LPCVOID lpszPath)
164 {
165 	if (SHELL_OsIsUnicode())
166 	  return PathIsRootW(lpszPath);
167 	return PathIsRootA(lpszPath);
168 }
169 
170 /*************************************************************************
171  *  PathIsExeA		[internal]
172  */
173 static BOOL PathIsExeA (LPCSTR lpszPath)
174 {
175 	LPCSTR lpszExtension = PathGetExtensionA(lpszPath);
176         int i;
177         static const char * const lpszExtensions[] =
178             {"exe", "com", "pif", "cmd", "bat", "scf", "scr", NULL };
179 
180 	TRACE("path=%s\n",lpszPath);
181 
182 	for(i=0; lpszExtensions[i]; i++)
183 	  if (!lstrcmpiA(lpszExtension,lpszExtensions[i])) return TRUE;
184 
185 	return FALSE;
186 }
187 
188 /*************************************************************************
189  *  PathIsExeW		[internal]
190  */
191 BOOL PathIsExeW (LPCWSTR lpszPath)
192 {
193 	LPCWSTR lpszExtension = PathGetExtensionW(lpszPath);
194         int i;
195         static const WCHAR lpszExtensions[][4] =
196             {{'e','x','e','\0'}, {'c','o','m','\0'}, {'p','i','f','\0'},
197              {'c','m','d','\0'}, {'b','a','t','\0'}, {'s','c','f','\0'},
198              {'s','c','r','\0'}, {'\0'} };
199 
200 	TRACE("path=%s\n",debugstr_w(lpszPath));
201 
202 	for(i=0; lpszExtensions[i][0]; i++)
203 	  if (!strcmpiW(lpszExtension,lpszExtensions[i])) return TRUE;
204 
205 	return FALSE;
206 }
207 
208 /*************************************************************************
209  *  PathIsExe		[SHELL32.43]
210  */
211 BOOL WINAPI PathIsExeAW (LPCVOID path)
212 {
213 	if (SHELL_OsIsUnicode())
214 	  return PathIsExeW (path);
215 	return PathIsExeA(path);
216 }
217 
218 /*************************************************************************
219  * PathFileExists	[SHELL32.45]
220  */
221 BOOL WINAPI PathFileExistsAW (LPCVOID lpszPath)
222 {
223 	if (SHELL_OsIsUnicode())
224 	  return PathFileExistsW (lpszPath);
225 	return PathFileExistsA (lpszPath);
226 }
227 
228 /*************************************************************************
229  * IsLFNDriveA		[SHELL32.41]
230  */
231 BOOL WINAPI IsLFNDriveA(LPCSTR lpszPath)
232 {
233     DWORD	fnlen;
234 
235     if (!GetVolumeInformationA(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
236 	return FALSE;
237     return fnlen > 12;
238 }
239 
240 /*************************************************************************
241  * IsLFNDriveW		[SHELL32.42]
242  */
243 BOOL WINAPI IsLFNDriveW(LPCWSTR lpszPath)
244 {
245     DWORD	fnlen;
246 
247     if (!GetVolumeInformationW(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
248 	return FALSE;
249     return fnlen > 12;
250 }
251 
252 /*************************************************************************
253  * IsLFNDrive		[SHELL32.119]
254  */
255 BOOL WINAPI IsLFNDriveAW(LPCVOID lpszPath)
256 {
257 	if (SHELL_OsIsUnicode())
258 	  return IsLFNDriveW(lpszPath);
259 	return IsLFNDriveA(lpszPath);
260 }
261 
262 /*
263 	########## Creating Something Unique ##########
264 */
265 /*************************************************************************
266  * PathMakeUniqueNameA	[internal]
267  */
268 static BOOL PathMakeUniqueNameA(
269 	LPSTR lpszBuffer,
270 	DWORD dwBuffSize,
271 	LPCSTR lpszShortName,
272 	LPCSTR lpszLongName,
273 	LPCSTR lpszPathName)
274 {
275 	FIXME("%p %u %s %s %s stub\n",
276 	 lpszBuffer, dwBuffSize, debugstr_a(lpszShortName),
277 	 debugstr_a(lpszLongName), debugstr_a(lpszPathName));
278 	return TRUE;
279 }
280 
281 /*************************************************************************
282  * PathMakeUniqueNameW	[internal]
283  */
284 static BOOL PathMakeUniqueNameW(
285 	LPWSTR lpszBuffer,
286 	DWORD dwBuffSize,
287 	LPCWSTR lpszShortName,
288 	LPCWSTR lpszLongName,
289 	LPCWSTR lpszPathName)
290 {
291 	FIXME("%p %u %s %s %s stub\n",
292 	 lpszBuffer, dwBuffSize, debugstr_w(lpszShortName),
293 	 debugstr_w(lpszLongName), debugstr_w(lpszPathName));
294 	return TRUE;
295 }
296 
297 /*************************************************************************
298  * PathMakeUniqueName	[SHELL32.47]
299  */
300 BOOL WINAPI PathMakeUniqueNameAW(
301 	LPVOID lpszBuffer,
302 	DWORD dwBuffSize,
303 	LPCVOID lpszShortName,
304 	LPCVOID lpszLongName,
305 	LPCVOID lpszPathName)
306 {
307 	if (SHELL_OsIsUnicode())
308 	  return PathMakeUniqueNameW(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
309 	return PathMakeUniqueNameA(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
310 }
311 
312 /*************************************************************************
313  * PathYetAnotherMakeUniqueName [SHELL32.75]
314  */
315 BOOL WINAPI PathYetAnotherMakeUniqueName(LPWSTR buffer, LPCWSTR path, LPCWSTR shortname, LPCWSTR longname)
316 {
317     WCHAR pathW[MAX_PATH], retW[MAX_PATH];
318     const WCHAR *file, *ext;
319     int i = 2;
320 
321     TRACE("(%p, %s, %s, %s)\n", buffer, debugstr_w(path), debugstr_w(shortname), debugstr_w(longname));
322 
323     file = longname ? longname : shortname;
324     PathCombineW(pathW, path, file);
325     strcpyW(retW, pathW);
326     PathRemoveExtensionW(pathW);
327 
328     ext = PathFindExtensionW(file);
329 
330     /* now try to make it unique */
331     while (PathFileExistsW(retW))
332     {
333         static const WCHAR fmtW[] = {'%','s',' ','(','%','d',')','%','s',0};
334 
335         sprintfW(retW, fmtW, pathW, i, ext);
336         i++;
337     }
338 
339     strcpyW(buffer, retW);
340     TRACE("ret - %s\n", debugstr_w(buffer));
341 
342     return TRUE;
343 }
344 
345 /*
346 	########## cleaning and resolving paths ##########
347  */
348 
349 /*************************************************************************
350  * PathCleanupSpec	[SHELL32.171]
351  *
352  * lpszFile is changed in place.
353  */
354 int WINAPI PathCleanupSpec( LPCWSTR lpszPathW, LPWSTR lpszFileW )
355 {
356     int i = 0;
357     DWORD rc = 0;
358     int length = 0;
359 
360     if (SHELL_OsIsUnicode())
361     {
362         LPWSTR p = lpszFileW;
363 
364         TRACE("Cleanup %s\n",debugstr_w(lpszFileW));
365 
366         if (lpszPathW)
367             length = strlenW(lpszPathW);
368 
369         while (*p)
370         {
371             int gct = PathGetCharTypeW(*p);
372             if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
373             {
374                 lpszFileW[i]='-';
375                 rc |= PCS_REPLACEDCHAR;
376             }
377             else
378                 lpszFileW[i]=*p;
379             i++;
380             p++;
381             if (length + i == MAX_PATH)
382             {
383                 rc |= PCS_FATAL | PCS_PATHTOOLONG;
384                 break;
385             }
386         }
387         lpszFileW[i]=0;
388     }
389     else
390     {
391         LPSTR lpszFileA = (LPSTR)lpszFileW;
392         LPCSTR lpszPathA = (LPCSTR)lpszPathW;
393         LPSTR p = lpszFileA;
394 
395         TRACE("Cleanup %s\n",debugstr_a(lpszFileA));
396 
397         if (lpszPathA)
398             length = strlen(lpszPathA);
399 
400         while (*p)
401         {
402             int gct = PathGetCharTypeA(*p);
403             if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
404             {
405                 lpszFileA[i]='-';
406                 rc |= PCS_REPLACEDCHAR;
407             }
408             else
409                 lpszFileA[i]=*p;
410             i++;
411             p++;
412             if (length + i == MAX_PATH)
413             {
414                 rc |= PCS_FATAL | PCS_PATHTOOLONG;
415                 break;
416             }
417         }
418         lpszFileA[i]=0;
419     }
420     return rc;
421 }
422 
423 /*************************************************************************
424  * PathQualifyA		[SHELL32]
425  */
426 static BOOL PathQualifyA(LPCSTR pszPath)
427 {
428 	FIXME("%s\n",pszPath);
429 	return FALSE;
430 }
431 
432 /*************************************************************************
433  * PathQualifyW		[SHELL32]
434  */
435 static BOOL PathQualifyW(LPCWSTR pszPath)
436 {
437 	FIXME("%s\n",debugstr_w(pszPath));
438 	return FALSE;
439 }
440 
441 /*************************************************************************
442  * PathQualify	[SHELL32.49]
443  */
444 BOOL WINAPI PathQualifyAW(LPCVOID pszPath)
445 {
446 	if (SHELL_OsIsUnicode())
447 	  return PathQualifyW(pszPath);
448 	return PathQualifyA(pszPath);
449 }
450 
451 static BOOL PathResolveA(LPSTR path, LPCSTR *paths, DWORD flags)
452 {
453     FIXME("(%s,%p,0x%08x),stub!\n", debugstr_a(path), paths, flags);
454     return FALSE;
455 }
456 
457 static BOOL PathResolveW(LPWSTR path, LPCWSTR *paths, DWORD flags)
458 {
459     FIXME("(%s,%p,0x%08x),stub!\n", debugstr_w(path), paths, flags);
460     return FALSE;
461 }
462 
463 /*************************************************************************
464  * PathResolve [SHELL32.51]
465  */
466 BOOL WINAPI PathResolveAW(LPVOID path, LPCVOID *paths, DWORD flags)
467 {
468     if (SHELL_OsIsUnicode())
469         return PathResolveW(path, (LPCWSTR*)paths, flags);
470     else
471         return PathResolveA(path, (LPCSTR*)paths, flags);
472 }
473 
474 /*************************************************************************
475 *	PathProcessCommandA
476 */
477 static LONG PathProcessCommandA (
478 	LPCSTR lpszPath,
479 	LPSTR lpszBuff,
480 	DWORD dwBuffSize,
481 	DWORD dwFlags)
482 {
483 	FIXME("%s %p 0x%04x 0x%04x stub\n",
484 	lpszPath, lpszBuff, dwBuffSize, dwFlags);
485 	if(!lpszPath) return -1;
486 	if(lpszBuff) strcpy(lpszBuff, lpszPath);
487 	return strlen(lpszPath);
488 }
489 
490 /*************************************************************************
491 *	PathProcessCommandW
492 */
493 static LONG PathProcessCommandW (
494 	LPCWSTR lpszPath,
495 	LPWSTR lpszBuff,
496 	DWORD dwBuffSize,
497 	DWORD dwFlags)
498 {
499 	FIXME("(%s, %p, 0x%04x, 0x%04x) stub\n",
500 	debugstr_w(lpszPath), lpszBuff, dwBuffSize, dwFlags);
501 	if(!lpszPath) return -1;
502 	if(lpszBuff) strcpyW(lpszBuff, lpszPath);
503 	return strlenW(lpszPath);
504 }
505 
506 /*************************************************************************
507 *	PathProcessCommand (SHELL32.653)
508 */
509 LONG WINAPI PathProcessCommandAW (
510 	LPCVOID lpszPath,
511 	LPVOID lpszBuff,
512 	DWORD dwBuffSize,
513 	DWORD dwFlags)
514 {
515 	if (SHELL_OsIsUnicode())
516 	  return PathProcessCommandW(lpszPath, lpszBuff, dwBuffSize, dwFlags);
517 	return PathProcessCommandA(lpszPath, lpszBuff, dwBuffSize, dwFlags);
518 }
519 
520 /*
521 	########## special ##########
522 */
523 
524 static const WCHAR szCurrentVersion[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\0'};
525 static const WCHAR Administrative_ToolsW[] = {'A','d','m','i','n','i','s','t','r','a','t','i','v','e',' ','T','o','o','l','s','\0'};
526 static const WCHAR AppDataW[] = {'A','p','p','D','a','t','a','\0'};
527 #ifndef __REACTOS__
528 static const WCHAR AppData_LocalLowW[] = {'A','p','p','D','a','t','a','\\','L','o','c','a','l','L','o','w','\0'};
529 static const WCHAR Application_DataW[] = {'A','p','p','l','i','c','a','t','i','o','n',' ','D','a','t','a','\0'};
530 #endif
531 static const WCHAR CacheW[] = {'C','a','c','h','e','\0'};
532 static const WCHAR CD_BurningW[] = {'C','D',' ','B','u','r','n','i','n','g','\0'};
533 static const WCHAR Common_Administrative_ToolsW[] = {'C','o','m','m','o','n',' ','A','d','m','i','n','i','s','t','r','a','t','i','v','e',' ','T','o','o','l','s','\0'};
534 static const WCHAR Common_AppDataW[] = {'C','o','m','m','o','n',' ','A','p','p','D','a','t','a','\0'};
535 static const WCHAR Common_DesktopW[] = {'C','o','m','m','o','n',' ','D','e','s','k','t','o','p','\0'};
536 static const WCHAR Common_DocumentsW[] = {'C','o','m','m','o','n',' ','D','o','c','u','m','e','n','t','s','\0'};
537 static const WCHAR Common_FavoritesW[] = {'C','o','m','m','o','n',' ','F','a','v','o','r','i','t','e','s','\0'};
538 static const WCHAR CommonFilesDirW[] = {'C','o','m','m','o','n','F','i','l','e','s','D','i','r','\0'};
539 static const WCHAR CommonFilesDirX86W[] = {'C','o','m','m','o','n','F','i','l','e','s','D','i','r',' ','(','x','8','6',')','\0'};
540 static const WCHAR CommonMusicW[] = {'C','o','m','m','o','n','M','u','s','i','c','\0'};
541 static const WCHAR CommonPicturesW[] = {'C','o','m','m','o','n','P','i','c','t','u','r','e','s','\0'};
542 static const WCHAR Common_ProgramsW[] = {'C','o','m','m','o','n',' ','P','r','o','g','r','a','m','s','\0'};
543 static const WCHAR Common_StartUpW[] = {'C','o','m','m','o','n',' ','S','t','a','r','t','U','p','\0'};
544 static const WCHAR Common_Start_MenuW[] = {'C','o','m','m','o','n',' ','S','t','a','r','t',' ','M','e','n','u','\0'};
545 static const WCHAR Common_TemplatesW[] = {'C','o','m','m','o','n',' ','T','e','m','p','l','a','t','e','s','\0'};
546 static const WCHAR CommonVideoW[] = {'C','o','m','m','o','n','V','i','d','e','o','\0'};
547 #ifndef __REACTOS__
548 static const WCHAR ContactsW[] = {'C','o','n','t','a','c','t','s','\0'};
549 #endif
550 static const WCHAR CookiesW[] = {'C','o','o','k','i','e','s','\0'};
551 static const WCHAR DesktopW[] = {'D','e','s','k','t','o','p','\0'};
552 #ifndef __REACTOS__
553 static const WCHAR DocumentsW[] = {'D','o','c','u','m','e','n','t','s','\0'};
554 static const WCHAR DownloadsW[] = {'D','o','w','n','l','o','a','d','s','\0'};
555 #endif
556 static const WCHAR FavoritesW[] = {'F','a','v','o','r','i','t','e','s','\0'};
557 static const WCHAR FontsW[] = {'F','o','n','t','s','\0'};
558 static const WCHAR HistoryW[] = {'H','i','s','t','o','r','y','\0'};
559 #ifndef __REACTOS__
560 static const WCHAR LinksW[] = {'L','i','n','k','s','\0'};
561 #endif
562 static const WCHAR Local_AppDataW[] = {'L','o','c','a','l',' ','A','p','p','D','a','t','a','\0'};
563 #ifndef __REACTOS__
564 static const WCHAR Local_Settings_Application_DataW[] = {'L','o','c','a','l',' ','S','e','t','t','i','n','g','s','\\','A','p','p','l','i','c','a','t','i','o','n',' ','D','a','t','a','\0'};
565 #endif
566 static const WCHAR Local_Settings_CD_BurningW[] = {'L','o','c','a','l',' ','S','e','t','t','i','n','g','s','\\','A','p','p','l','i','c','a','t','i','o','n',' ','D','a','t','a','\\','M','i','c','r','o','s','o','f','t','\\','C','D',' ','B','u','r','n','i','n','g','\0'};
567 #ifndef __REACTOS__
568 static const WCHAR Local_Settings_HistoryW[] = {'L','o','c','a','l',' ','S','e','t','t','i','n','g','s','\\','H','i','s','t','o','r','y','\0'};
569 static const WCHAR Local_Settings_Temporary_Internet_FilesW[] = {'L','o','c','a','l',' ','S','e','t','t','i','n','g','s','\\','T','e','m','p','o','r','a','r','y',' ','I','n','t','e','r','n','e','t',' ','F','i','l','e','s','\0'};
570 static const WCHAR Microsoft_Windows_GameExplorerW[] = {'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','G','a','m','e','E','x','p','l','o','r','e','r','\0'};
571 static const WCHAR Microsoft_Windows_LibrariesW[] = {'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','L','i','b','r','a','r','i','e','s','\0'};
572 static const WCHAR Microsoft_Windows_RingtonesW[] = {'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','R','i','n','g','t','o','n','e','s','\0'};
573 static const WCHAR MusicW[] = {'M','u','s','i','c','\0'};
574 static const WCHAR Music_PlaylistsW[] = {'M','u','s','i','c','\\','P','l','a','y','l','i','s','t','s','\0'};
575 static const WCHAR Music_Sample_MusicW[] = {'M','u','s','i','c','\\','S','a','m','p','l','e',' ','M','u','s','i','c','\0'};
576 static const WCHAR Music_Sample_PlaylistsW[] = {'M','u','s','i','c','\\','S','a','m','p','l','e',' ','P','l','a','y','l','i','s','t','s','\0'};
577 static const WCHAR My_DocumentsW[] = {'M','y',' ','D','o','c','u','m','e','n','t','s','\0'};
578 #endif
579 static const WCHAR My_MusicW[] = {'M','y',' ','M','u','s','i','c','\0'};
580 static const WCHAR My_PicturesW[] = {'M','y',' ','P','i','c','t','u','r','e','s','\0'};
581 static const WCHAR My_VideoW[] = {'M','y',' ','V','i','d','e','o','\0'};
582 static const WCHAR NetHoodW[] = {'N','e','t','H','o','o','d','\0'};
583 static const WCHAR OEM_LinksW[] = {'O','E','M',' ','L','i','n','k','s','\0'};
584 static const WCHAR PersonalW[] = {'P','e','r','s','o','n','a','l','\0'};
585 #ifndef __REACTOS__
586 static const WCHAR PicturesW[] = {'P','i','c','t','u','r','e','s','\0'};
587 static const WCHAR Pictures_Sample_PicturesW[] = {'P','i','c','t','u','r','e','s','\\','S','a','m','p','l','e',' ','P','i','c','t','u','r','e','s','\0'};
588 static const WCHAR Pictures_Slide_ShowsW[] = {'P','i','c','t','u','r','e','s','\\','S','l','i','d','e',' ','S','h','o','w','s','\0'};
589 #endif
590 static const WCHAR PrintHoodW[] = {'P','r','i','n','t','H','o','o','d','\0'};
591 #ifndef __REACTOS__
592 static const WCHAR Program_FilesW[] = {'P','r','o','g','r','a','m',' ','F','i','l','e','s','\0'};
593 static const WCHAR Program_Files_Common_FilesW[] = {'P','r','o','g','r','a','m',' ','F','i','l','e','s','\\','C','o','m','m','o','n',' ','F','i','l','e','s','\0'};
594 #endif
595 static const WCHAR Program_Files_x86W[] = {'P','r','o','g','r','a','m',' ','F','i','l','e','s',' ','(','x','8','6',')','\0'};
596 static const WCHAR Program_Files_x86_Common_FilesW[] = {'P','r','o','g','r','a','m',' ','F','i','l','e','s',' ','(','x','8','6',')','\\','C','o','m','m','o','n',' ','F','i','l','e','s','\0'};
597 static const WCHAR ProgramFilesDirW[] = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r','\0'};
598 static const WCHAR ProgramFilesDirX86W[] = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r',' ','(','x','8','6',')','\0'};
599 static const WCHAR ProgramsW[] = {'P','r','o','g','r','a','m','s','\0'};
600 static const WCHAR RecentW[] = {'R','e','c','e','n','t','\0'};
601 static const WCHAR ResourcesW[] = {'R','e','s','o','u','r','c','e','s','\0'};
602 #ifndef __REACTOS__
603 static const WCHAR Saved_GamesW[] = {'S','a','v','e','d',' ','G','a','m','e','s','\0'};
604 static const WCHAR SearchesW[] = {'S','e','a','r','c','h','e','s','\0'};
605 #endif
606 static const WCHAR SendToW[] = {'S','e','n','d','T','o','\0'};
607 static const WCHAR StartUpW[] = {'S','t','a','r','t','U','p','\0'};
608 static const WCHAR Start_MenuW[] = {'S','t','a','r','t',' ','M','e','n','u','\0'};
609 #ifndef __REACTOS__
610 static const WCHAR Start_Menu_ProgramsW[] = {'S','t','a','r','t',' ','M','e','n','u','\\','P','r','o','g','r','a','m','s','\0'};
611 static const WCHAR Start_Menu_Admin_ToolsW[] = {'S','t','a','r','t',' ','M','e','n','u','\\','P','r','o','g','r','a','m','s','\\','A','d','m','i','n','i','s','t','r','a','t','i','v','e',' ','T','o','o','l','s','\0'};
612 static const WCHAR Start_Menu_StartupW[] = {'S','t','a','r','t',' ','M','e','n','u','\\','P','r','o','g','r','a','m','s','\\','S','t','a','r','t','U','p','\0'};
613 #endif
614 static const WCHAR TemplatesW[] = {'T','e','m','p','l','a','t','e','s','\0'};
615 #ifndef __REACTOS__
616 static const WCHAR UsersW[] = {'U','s','e','r','s','\0'};
617 static const WCHAR UsersPublicW[] = {'U','s','e','r','s','\\','P','u','b','l','i','c','\0'};
618 static const WCHAR VideosW[] = {'V','i','d','e','o','s','\0'};
619 static const WCHAR Videos_Sample_VideosW[] = {'V','i','d','e','o','s','\\','S','a','m','p','l','e',' ','V','i','d','e','o','s','\0'};
620 #endif
621 static const WCHAR DefaultW[] = {'.','D','e','f','a','u','l','t','\0'};
622 static const WCHAR AllUsersProfileW[] = {'%','A','L','L','U','S','E','R','S','P','R','O','F','I','L','E','%','\0'};
623 static const WCHAR UserProfileW[] = {'%','U','S','E','R','P','R','O','F','I','L','E','%','\0'};
624 static const WCHAR SystemDriveW[] = {'%','S','y','s','t','e','m','D','r','i','v','e','%','\0'};
625 static const WCHAR ProfileListW[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s',' ','N','T','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','P','r','o','f','i','l','e','L','i','s','t',0};
626 static const WCHAR ProfilesDirectoryW[] = {'P','r','o','f','i','l','e','s','D','i','r','e','c','t','o','r','y',0};
627 static const WCHAR AllUsersProfileValueW[] = {'A','l','l','U','s','e','r','s','P','r','o','f','i','l','e','\0'};
628 static const WCHAR szSHFolders[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l','o','r','e','r','\\','S','h','e','l','l',' ','F','o','l','d','e','r','s','\0'};
629 static const WCHAR szSHUserFolders[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l','o','r','e','r','\\','U','s','e','r',' ','S','h','e','l','l',' ','F','o','l','d','e','r','s','\0'};
630 static const WCHAR szDefaultProfileDirW[] = {'u','s','e','r','s',0};
631 #ifndef __REACTOS__
632 static const WCHAR szKnownFolderDescriptions[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l','o','r','e','r','\\','F','o','l','d','e','r','D','e','s','c','r','i','p','t','i','o','n','s','\0'};
633 static const WCHAR szKnownFolderRedirections[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l','o','r','e','r','\\','U','s','e','r',' ','S','h','e','l','l',' ','F','o','l','d','e','r','s',0};
634 #endif
635 static const WCHAR AllUsersW[] = {'P','u','b','l','i','c',0};
636 
637 typedef enum _CSIDL_Type {
638     CSIDL_Type_User,
639     CSIDL_Type_AllUsers,
640     CSIDL_Type_CurrVer,
641     CSIDL_Type_Disallowed,
642     CSIDL_Type_NonExistent,
643     CSIDL_Type_WindowsPath,
644     CSIDL_Type_SystemPath,
645     CSIDL_Type_SystemX86Path,
646 } CSIDL_Type;
647 
648 /* Cannot use #if _WIN32_WINNT >= 0x0600 because _WIN32_WINNT == 0x0600 here. */
649 #ifndef __REACTOS__
650 #define CSIDL_CONTACTS         0x0043
651 #define CSIDL_DOWNLOADS        0x0047
652 #define CSIDL_LINKS            0x004d
653 #define CSIDL_APPDATA_LOCALLOW 0x004e
654 #define CSIDL_SAVED_GAMES      0x0062
655 #define CSIDL_SEARCHES         0x0063
656 #endif
657 
658 typedef struct
659 {
660     const KNOWNFOLDERID *id;
661     CSIDL_Type type;
662     LPCWSTR    szValueName;
663     LPCWSTR    szDefaultPath; /* fallback string or resource ID */
664 } CSIDL_DATA;
665 
666 static const CSIDL_DATA CSIDL_Data[] =
667 {
668     { /* 0x00 - CSIDL_DESKTOP */
669         &FOLDERID_Desktop,
670         CSIDL_Type_User,
671         DesktopW,
672         MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
673     },
674     { /* 0x01 - CSIDL_INTERNET */
675         &FOLDERID_InternetFolder,
676         CSIDL_Type_Disallowed,
677         NULL,
678         NULL
679     },
680     { /* 0x02 - CSIDL_PROGRAMS */
681         &FOLDERID_Programs,
682         CSIDL_Type_User,
683         ProgramsW,
684         MAKEINTRESOURCEW(IDS_PROGRAMS)
685     },
686     { /* 0x03 - CSIDL_CONTROLS (.CPL files) */
687         &FOLDERID_ControlPanelFolder,
688         CSIDL_Type_SystemPath,
689         NULL,
690         NULL
691     },
692     { /* 0x04 - CSIDL_PRINTERS */
693         &FOLDERID_PrintersFolder,
694         CSIDL_Type_SystemPath,
695         NULL,
696         NULL
697     },
698     { /* 0x05 - CSIDL_PERSONAL */
699         &FOLDERID_Documents,
700         CSIDL_Type_User,
701         PersonalW,
702         MAKEINTRESOURCEW(IDS_PERSONAL)
703     },
704     { /* 0x06 - CSIDL_FAVORITES */
705         &FOLDERID_Favorites,
706         CSIDL_Type_User,
707         FavoritesW,
708         MAKEINTRESOURCEW(IDS_FAVORITES)
709     },
710     { /* 0x07 - CSIDL_STARTUP */
711         &FOLDERID_Startup,
712         CSIDL_Type_User,
713         StartUpW,
714         MAKEINTRESOURCEW(IDS_STARTUP)
715     },
716     { /* 0x08 - CSIDL_RECENT */
717         &FOLDERID_Recent,
718         CSIDL_Type_User,
719         RecentW,
720         MAKEINTRESOURCEW(IDS_RECENT)
721     },
722     { /* 0x09 - CSIDL_SENDTO */
723         &FOLDERID_SendTo,
724         CSIDL_Type_User,
725         SendToW,
726         MAKEINTRESOURCEW(IDS_SENDTO)
727     },
728     { /* 0x0a - CSIDL_BITBUCKET - Recycle Bin */
729         &FOLDERID_RecycleBinFolder,
730         CSIDL_Type_Disallowed,
731         NULL,
732         NULL,
733     },
734     { /* 0x0b - CSIDL_STARTMENU */
735         &FOLDERID_StartMenu,
736         CSIDL_Type_User,
737         Start_MenuW,
738         MAKEINTRESOURCEW(IDS_STARTMENU)
739     },
740     { /* 0x0c - CSIDL_MYDOCUMENTS */
741         &GUID_NULL,
742         CSIDL_Type_Disallowed, /* matches WinXP--can't get its path */
743         NULL,
744         NULL
745     },
746     { /* 0x0d - CSIDL_MYMUSIC */
747         &FOLDERID_Music,
748         CSIDL_Type_User,
749         My_MusicW,
750         MAKEINTRESOURCEW(IDS_MYMUSIC)
751     },
752     { /* 0x0e - CSIDL_MYVIDEO */
753         &FOLDERID_Videos,
754         CSIDL_Type_User,
755         My_VideoW,
756         MAKEINTRESOURCEW(IDS_MYVIDEO)
757     },
758     { /* 0x0f - unassigned */
759         &GUID_NULL,
760         CSIDL_Type_Disallowed,
761         NULL,
762         NULL,
763     },
764     { /* 0x10 - CSIDL_DESKTOPDIRECTORY */
765         &FOLDERID_Desktop,
766         CSIDL_Type_User,
767         DesktopW,
768         MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
769     },
770     { /* 0x11 - CSIDL_DRIVES */
771         &FOLDERID_ComputerFolder,
772         CSIDL_Type_Disallowed,
773         NULL,
774         NULL,
775     },
776     { /* 0x12 - CSIDL_NETWORK */
777         &FOLDERID_NetworkFolder,
778         CSIDL_Type_Disallowed,
779         NULL,
780         NULL,
781     },
782     { /* 0x13 - CSIDL_NETHOOD */
783         &FOLDERID_NetHood,
784         CSIDL_Type_User,
785         NetHoodW,
786         MAKEINTRESOURCEW(IDS_NETHOOD)
787     },
788     { /* 0x14 - CSIDL_FONTS */
789         &FOLDERID_Fonts,
790         CSIDL_Type_WindowsPath,
791         FontsW,
792         FontsW
793     },
794     { /* 0x15 - CSIDL_TEMPLATES */
795         &FOLDERID_Templates,
796         CSIDL_Type_User,
797         TemplatesW,
798         MAKEINTRESOURCEW(IDS_TEMPLATES)
799     },
800     { /* 0x16 - CSIDL_COMMON_STARTMENU */
801         &FOLDERID_CommonStartMenu,
802         CSIDL_Type_AllUsers,
803         Common_Start_MenuW,
804         MAKEINTRESOURCEW(IDS_STARTMENU)
805     },
806     { /* 0x17 - CSIDL_COMMON_PROGRAMS */
807         &FOLDERID_CommonPrograms,
808         CSIDL_Type_AllUsers,
809         Common_ProgramsW,
810         MAKEINTRESOURCEW(IDS_PROGRAMS)
811     },
812     { /* 0x18 - CSIDL_COMMON_STARTUP */
813         &FOLDERID_CommonStartup,
814         CSIDL_Type_AllUsers,
815         Common_StartUpW,
816         MAKEINTRESOURCEW(IDS_STARTUP)
817     },
818     { /* 0x19 - CSIDL_COMMON_DESKTOPDIRECTORY */
819         &FOLDERID_PublicDesktop,
820         CSIDL_Type_AllUsers,
821         Common_DesktopW,
822         MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
823     },
824     { /* 0x1a - CSIDL_APPDATA */
825         &FOLDERID_RoamingAppData,
826         CSIDL_Type_User,
827         AppDataW,
828         MAKEINTRESOURCEW(IDS_APPDATA)
829     },
830     { /* 0x1b - CSIDL_PRINTHOOD */
831         &FOLDERID_PrintHood,
832         CSIDL_Type_User,
833         PrintHoodW,
834         MAKEINTRESOURCEW(IDS_PRINTHOOD)
835     },
836     { /* 0x1c - CSIDL_LOCAL_APPDATA */
837         &FOLDERID_LocalAppData,
838         CSIDL_Type_User,
839         Local_AppDataW,
840         MAKEINTRESOURCEW(IDS_LOCAL_APPDATA)
841     },
842     { /* 0x1d - CSIDL_ALTSTARTUP */
843         &GUID_NULL,
844         CSIDL_Type_NonExistent,
845         NULL,
846         NULL
847     },
848     { /* 0x1e - CSIDL_COMMON_ALTSTARTUP */
849         &GUID_NULL,
850         CSIDL_Type_NonExistent,
851         NULL,
852         NULL
853     },
854     { /* 0x1f - CSIDL_COMMON_FAVORITES */
855         &FOLDERID_Favorites,
856         CSIDL_Type_AllUsers,
857         Common_FavoritesW,
858         MAKEINTRESOURCEW(IDS_FAVORITES)
859     },
860     { /* 0x20 - CSIDL_INTERNET_CACHE */
861         &FOLDERID_InternetCache,
862         CSIDL_Type_User,
863         CacheW,
864         MAKEINTRESOURCEW(IDS_INTERNET_CACHE)
865     },
866     { /* 0x21 - CSIDL_COOKIES */
867         &FOLDERID_Cookies,
868         CSIDL_Type_User,
869         CookiesW,
870         MAKEINTRESOURCEW(IDS_COOKIES)
871     },
872     { /* 0x22 - CSIDL_HISTORY */
873         &FOLDERID_History,
874         CSIDL_Type_User,
875         HistoryW,
876         MAKEINTRESOURCEW(IDS_HISTORY)
877     },
878     { /* 0x23 - CSIDL_COMMON_APPDATA */
879         &FOLDERID_ProgramData,
880         CSIDL_Type_AllUsers,
881         Common_AppDataW,
882         MAKEINTRESOURCEW(IDS_APPDATA)
883     },
884     { /* 0x24 - CSIDL_WINDOWS */
885         &FOLDERID_Windows,
886         CSIDL_Type_WindowsPath,
887         NULL,
888         NULL
889     },
890     { /* 0x25 - CSIDL_SYSTEM */
891         &FOLDERID_System,
892         CSIDL_Type_SystemPath,
893         NULL,
894         NULL
895     },
896     { /* 0x26 - CSIDL_PROGRAM_FILES */
897         &FOLDERID_ProgramFiles,
898         CSIDL_Type_CurrVer,
899         ProgramFilesDirW,
900         MAKEINTRESOURCEW(IDS_PROGRAM_FILES)
901     },
902     { /* 0x27 - CSIDL_MYPICTURES */
903         &FOLDERID_Pictures,
904         CSIDL_Type_User,
905         My_PicturesW,
906         MAKEINTRESOURCEW(IDS_MYPICTURES)
907     },
908     { /* 0x28 - CSIDL_PROFILE */
909         &FOLDERID_Profile,
910         CSIDL_Type_User,
911         NULL,
912         NULL
913     },
914     { /* 0x29 - CSIDL_SYSTEMX86 */
915         &FOLDERID_SystemX86,
916         CSIDL_Type_SystemX86Path,
917         NULL,
918         NULL
919     },
920     { /* 0x2a - CSIDL_PROGRAM_FILESX86 */
921         &FOLDERID_ProgramFilesX86,
922         CSIDL_Type_CurrVer,
923         ProgramFilesDirX86W,
924         Program_Files_x86W
925     },
926     { /* 0x2b - CSIDL_PROGRAM_FILES_COMMON */
927         &FOLDERID_ProgramFilesCommon,
928         CSIDL_Type_CurrVer,
929         CommonFilesDirW,
930         MAKEINTRESOURCEW(IDS_PROGRAM_FILES_COMMON)
931     },
932     { /* 0x2c - CSIDL_PROGRAM_FILES_COMMONX86 */
933         &FOLDERID_ProgramFilesCommonX86,
934         CSIDL_Type_CurrVer,
935         CommonFilesDirX86W,
936         Program_Files_x86_Common_FilesW
937     },
938     { /* 0x2d - CSIDL_COMMON_TEMPLATES */
939         &FOLDERID_CommonTemplates,
940         CSIDL_Type_AllUsers,
941         Common_TemplatesW,
942         MAKEINTRESOURCEW(IDS_TEMPLATES)
943     },
944     { /* 0x2e - CSIDL_COMMON_DOCUMENTS */
945         &FOLDERID_PublicDocuments,
946         CSIDL_Type_AllUsers,
947         Common_DocumentsW,
948         MAKEINTRESOURCEW(IDS_PERSONAL)
949     },
950     { /* 0x2f - CSIDL_COMMON_ADMINTOOLS */
951         &FOLDERID_CommonAdminTools,
952         CSIDL_Type_AllUsers,
953         Common_Administrative_ToolsW,
954         MAKEINTRESOURCEW(IDS_ADMINTOOLS)
955     },
956     { /* 0x30 - CSIDL_ADMINTOOLS */
957         &FOLDERID_AdminTools,
958         CSIDL_Type_User,
959         Administrative_ToolsW,
960         MAKEINTRESOURCEW(IDS_ADMINTOOLS)
961     },
962     { /* 0x31 - CSIDL_CONNECTIONS */
963         &FOLDERID_ConnectionsFolder,
964         CSIDL_Type_Disallowed,
965         NULL,
966         NULL
967     },
968     { /* 0x32 - unassigned */
969         &GUID_NULL,
970         CSIDL_Type_Disallowed,
971         NULL,
972         NULL
973     },
974     { /* 0x33 - unassigned */
975         &GUID_NULL,
976         CSIDL_Type_Disallowed,
977         NULL,
978         NULL
979     },
980     { /* 0x34 - unassigned */
981         &GUID_NULL,
982         CSIDL_Type_Disallowed,
983         NULL,
984         NULL
985     },
986     { /* 0x35 - CSIDL_COMMON_MUSIC */
987         &FOLDERID_PublicMusic,
988         CSIDL_Type_AllUsers,
989         CommonMusicW,
990         MAKEINTRESOURCEW(IDS_COMMON_MUSIC)
991     },
992     { /* 0x36 - CSIDL_COMMON_PICTURES */
993         &FOLDERID_PublicPictures,
994         CSIDL_Type_AllUsers,
995         CommonPicturesW,
996         MAKEINTRESOURCEW(IDS_COMMON_PICTURES)
997     },
998     { /* 0x37 - CSIDL_COMMON_VIDEO */
999         &FOLDERID_PublicVideos,
1000         CSIDL_Type_AllUsers,
1001         CommonVideoW,
1002         MAKEINTRESOURCEW(IDS_COMMON_VIDEO)
1003     },
1004     { /* 0x38 - CSIDL_RESOURCES */
1005         &FOLDERID_ResourceDir,
1006         CSIDL_Type_WindowsPath,
1007         NULL,
1008         ResourcesW
1009     },
1010     { /* 0x39 - CSIDL_RESOURCES_LOCALIZED */
1011         &FOLDERID_LocalizedResourcesDir,
1012         CSIDL_Type_NonExistent,
1013         NULL,
1014         NULL
1015     },
1016     { /* 0x3a - CSIDL_COMMON_OEM_LINKS */
1017         &FOLDERID_CommonOEMLinks,
1018         CSIDL_Type_AllUsers,
1019         NULL,
1020         OEM_LinksW
1021     },
1022     { /* 0x3b - CSIDL_CDBURN_AREA */
1023         &FOLDERID_CDBurning,
1024         CSIDL_Type_User,
1025         CD_BurningW,
1026         Local_Settings_CD_BurningW
1027     },
1028     { /* 0x3c unassigned */
1029         &GUID_NULL,
1030         CSIDL_Type_Disallowed,
1031         NULL,
1032         NULL
1033     },
1034     { /* 0x3d - CSIDL_COMPUTERSNEARME */
1035         &GUID_NULL,
1036         CSIDL_Type_Disallowed, /* FIXME */
1037         NULL,
1038         NULL
1039     },
1040     { /* 0x3e - CSIDL_PROFILES */
1041         &GUID_NULL,
1042         CSIDL_Type_Disallowed, /* oddly, this matches WinXP */
1043         NULL,
1044         NULL
1045     },
1046 /* Cannot use #if _WIN32_WINNT >= 0x0600 because _WIN32_WINNT == 0x0600 here. */
1047 #ifndef __REACTOS__
1048     { /* 0x3f */
1049         &FOLDERID_AddNewPrograms,
1050         CSIDL_Type_Disallowed,
1051         NULL,
1052         NULL
1053     },
1054     { /* 0x40 */
1055         &FOLDERID_AppUpdates,
1056         CSIDL_Type_Disallowed,
1057         NULL,
1058         NULL
1059     },
1060     { /* 0x41 */
1061         &FOLDERID_ChangeRemovePrograms,
1062         CSIDL_Type_Disallowed,
1063         NULL,
1064         NULL
1065     },
1066     { /* 0x42 */
1067         &FOLDERID_ConflictFolder,
1068         CSIDL_Type_Disallowed,
1069         NULL,
1070         NULL
1071     },
1072     { /* 0x43 - CSIDL_CONTACTS */
1073         &FOLDERID_Contacts,
1074         CSIDL_Type_User,
1075         NULL,
1076         ContactsW
1077     },
1078     { /* 0x44 */
1079         &FOLDERID_DeviceMetadataStore,
1080         CSIDL_Type_Disallowed, /* FIXME */
1081         NULL,
1082         NULL
1083     },
1084     { /* 0x45 */
1085         &GUID_NULL,
1086         CSIDL_Type_User,
1087         NULL,
1088         DocumentsW
1089     },
1090     { /* 0x46 */
1091         &FOLDERID_DocumentsLibrary,
1092         CSIDL_Type_Disallowed, /* FIXME */
1093         NULL,
1094         NULL
1095     },
1096     { /* 0x47 - CSIDL_DOWNLOADS */
1097         &FOLDERID_Downloads,
1098         CSIDL_Type_User,
1099         NULL,
1100         DownloadsW
1101     },
1102     { /* 0x48 */
1103         &FOLDERID_Games,
1104         CSIDL_Type_Disallowed,
1105         NULL,
1106         NULL
1107     },
1108     { /* 0x49 */
1109         &FOLDERID_GameTasks,
1110         CSIDL_Type_Disallowed, /* FIXME */
1111         NULL,
1112         NULL
1113     },
1114     { /* 0x4a */
1115         &FOLDERID_HomeGroup,
1116         CSIDL_Type_Disallowed,
1117         NULL,
1118         NULL
1119     },
1120     { /* 0x4b */
1121         &FOLDERID_ImplicitAppShortcuts,
1122         CSIDL_Type_Disallowed, /* FIXME */
1123         NULL,
1124         NULL
1125     },
1126     { /* 0x4c */
1127         &FOLDERID_Libraries,
1128         CSIDL_Type_Disallowed, /* FIXME */
1129         NULL,
1130         NULL
1131     },
1132     { /* 0x4d - CSIDL_LINKS */
1133         &FOLDERID_Links,
1134         CSIDL_Type_User,
1135         NULL,
1136         LinksW
1137     },
1138     { /* 0x4e - CSIDL_APPDATA_LOCALLOW */
1139         &FOLDERID_LocalAppDataLow,
1140         CSIDL_Type_User,
1141         NULL,
1142         AppData_LocalLowW
1143     },
1144     { /* 0x4f */
1145         &FOLDERID_MusicLibrary,
1146         CSIDL_Type_Disallowed, /* FIXME */
1147         NULL,
1148         NULL
1149     },
1150     { /* 0x50 */
1151         &FOLDERID_OriginalImages,
1152         CSIDL_Type_Disallowed, /* FIXME */
1153         NULL,
1154         NULL
1155     },
1156     { /* 0x51 */
1157         &FOLDERID_PhotoAlbums,
1158         CSIDL_Type_User,
1159         NULL,
1160         Pictures_Slide_ShowsW
1161     },
1162     { /* 0x52 */
1163         &FOLDERID_PicturesLibrary,
1164         CSIDL_Type_Disallowed, /* FIXME */
1165         NULL,
1166         NULL
1167     },
1168     { /* 0x53 */
1169         &FOLDERID_Playlists,
1170         CSIDL_Type_User,
1171         NULL,
1172         Music_PlaylistsW
1173     },
1174     { /* 0x54 */
1175         &FOLDERID_ProgramFilesX64,
1176         CSIDL_Type_NonExistent,
1177         NULL,
1178         NULL
1179     },
1180     { /* 0x55 */
1181         &FOLDERID_ProgramFilesCommonX64,
1182         CSIDL_Type_NonExistent,
1183         NULL,
1184         NULL
1185     },
1186     { /* 0x56 */
1187         &FOLDERID_Public,
1188         CSIDL_Type_CurrVer, /* FIXME */
1189         NULL,
1190         UsersPublicW
1191     },
1192     { /* 0x57 */
1193         &FOLDERID_PublicDownloads,
1194         CSIDL_Type_AllUsers,
1195         NULL,
1196         DownloadsW
1197     },
1198     { /* 0x58 */
1199         &FOLDERID_PublicGameTasks,
1200         CSIDL_Type_AllUsers,
1201         NULL,
1202         Microsoft_Windows_GameExplorerW
1203     },
1204     { /* 0x59 */
1205         &FOLDERID_PublicLibraries,
1206         CSIDL_Type_AllUsers,
1207         NULL,
1208         Microsoft_Windows_LibrariesW
1209     },
1210     { /* 0x5a */
1211         &FOLDERID_PublicRingtones,
1212         CSIDL_Type_AllUsers,
1213         NULL,
1214         Microsoft_Windows_RingtonesW
1215     },
1216     { /* 0x5b */
1217         &FOLDERID_QuickLaunch,
1218         CSIDL_Type_Disallowed, /* FIXME */
1219         NULL,
1220         NULL
1221     },
1222     { /* 0x5c */
1223         &FOLDERID_RecordedTVLibrary,
1224         CSIDL_Type_Disallowed, /* FIXME */
1225         NULL,
1226         NULL
1227     },
1228     { /* 0x5d */
1229         &FOLDERID_Ringtones,
1230         CSIDL_Type_Disallowed, /* FIXME */
1231         NULL,
1232         NULL
1233     },
1234     { /* 0x5e */
1235         &FOLDERID_SampleMusic,
1236         CSIDL_Type_AllUsers,
1237         NULL,
1238         Music_Sample_MusicW
1239     },
1240     { /* 0x5f */
1241         &FOLDERID_SamplePictures,
1242         CSIDL_Type_AllUsers,
1243         NULL,
1244         Pictures_Sample_PicturesW
1245     },
1246     { /* 0x60 */
1247         &FOLDERID_SamplePlaylists,
1248         CSIDL_Type_AllUsers,
1249         NULL,
1250         Music_Sample_PlaylistsW
1251     },
1252     { /* 0x61 */
1253         &FOLDERID_SampleVideos,
1254         CSIDL_Type_AllUsers,
1255         NULL,
1256         Videos_Sample_VideosW
1257     },
1258     { /* 0x62 - CSIDL_SAVED_GAMES */
1259         &FOLDERID_SavedGames,
1260         CSIDL_Type_User,
1261         NULL,
1262         Saved_GamesW
1263     },
1264     { /* 0x63 - CSIDL_SEARCHES */
1265         &FOLDERID_SavedSearches,
1266         CSIDL_Type_User,
1267         NULL,
1268         SearchesW
1269     },
1270     { /* 0x64 */
1271         &FOLDERID_SEARCH_CSC,
1272         CSIDL_Type_Disallowed,
1273         NULL,
1274         NULL
1275     },
1276     { /* 0x65 */
1277         &FOLDERID_SEARCH_MAPI,
1278         CSIDL_Type_Disallowed,
1279         NULL,
1280         NULL
1281     },
1282     { /* 0x66 */
1283         &FOLDERID_SearchHome,
1284         CSIDL_Type_Disallowed,
1285         NULL,
1286         NULL
1287     },
1288     { /* 0x67 */
1289         &FOLDERID_SidebarDefaultParts,
1290         CSIDL_Type_Disallowed, /* FIXME */
1291         NULL,
1292         NULL
1293     },
1294     { /* 0x68 */
1295         &FOLDERID_SidebarParts,
1296         CSIDL_Type_Disallowed, /* FIXME */
1297         NULL,
1298         NULL
1299     },
1300     { /* 0x69 */
1301         &FOLDERID_SyncManagerFolder,
1302         CSIDL_Type_Disallowed,
1303         NULL,
1304         NULL
1305     },
1306     { /* 0x6a */
1307         &FOLDERID_SyncResultsFolder,
1308         CSIDL_Type_Disallowed,
1309         NULL,
1310         NULL
1311     },
1312     { /* 0x6b */
1313         &FOLDERID_SyncSetupFolder,
1314         CSIDL_Type_Disallowed,
1315         NULL,
1316         NULL
1317     },
1318     { /* 0x6c */
1319         &FOLDERID_UserPinned,
1320         CSIDL_Type_Disallowed, /* FIXME */
1321         NULL,
1322         NULL
1323     },
1324     { /* 0x6d */
1325         &FOLDERID_UserProfiles,
1326         CSIDL_Type_CurrVer,
1327         UsersW,
1328         UsersW
1329     },
1330     { /* 0x6e */
1331         &FOLDERID_UserProgramFiles,
1332         CSIDL_Type_Disallowed, /* FIXME */
1333         NULL,
1334         NULL
1335     },
1336     { /* 0x6f */
1337         &FOLDERID_UserProgramFilesCommon,
1338         CSIDL_Type_Disallowed, /* FIXME */
1339         NULL,
1340         NULL
1341     },
1342     { /* 0x70 */
1343         &FOLDERID_UsersFiles,
1344         CSIDL_Type_Disallowed,
1345         NULL,
1346         NULL
1347     },
1348     { /* 0x71 */
1349         &FOLDERID_UsersLibraries,
1350         CSIDL_Type_Disallowed,
1351         NULL,
1352         NULL
1353     },
1354     { /* 0x72 */
1355         &FOLDERID_VideosLibrary,
1356         CSIDL_Type_Disallowed, /* FIXME */
1357         NULL,
1358         NULL
1359     }
1360 #endif
1361 };
1362 
1363 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest);
1364 
1365 /* Gets the value named value from the registry key
1366  * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
1367  * (or from rootKey\userPrefix\... if userPrefix is not NULL) into path, which
1368  * is assumed to be MAX_PATH WCHARs in length.
1369  * If it exists, expands the value and writes the expanded value to
1370  * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
1371  * Returns successful error code if the value was retrieved from the registry,
1372  * and a failure otherwise.
1373  */
1374 static HRESULT _SHGetUserShellFolderPath(HKEY rootKey, LPCWSTR userPrefix,
1375  LPCWSTR value, LPWSTR path)
1376 {
1377     HRESULT hr;
1378     WCHAR shellFolderPath[MAX_PATH], userShellFolderPath[MAX_PATH];
1379     LPCWSTR pShellFolderPath, pUserShellFolderPath;
1380     DWORD dwType, dwPathLen = MAX_PATH;
1381     HKEY userShellFolderKey, shellFolderKey;
1382 
1383     TRACE("%p,%s,%s,%p\n",rootKey, debugstr_w(userPrefix), debugstr_w(value),
1384      path);
1385 
1386     if (userPrefix)
1387     {
1388         strcpyW(shellFolderPath, userPrefix);
1389         PathAddBackslashW(shellFolderPath);
1390         strcatW(shellFolderPath, szSHFolders);
1391         pShellFolderPath = shellFolderPath;
1392         strcpyW(userShellFolderPath, userPrefix);
1393         PathAddBackslashW(userShellFolderPath);
1394         strcatW(userShellFolderPath, szSHUserFolders);
1395         pUserShellFolderPath = userShellFolderPath;
1396     }
1397     else
1398     {
1399         pUserShellFolderPath = szSHUserFolders;
1400         pShellFolderPath = szSHFolders;
1401     }
1402 
1403     if (RegCreateKeyW(rootKey, pShellFolderPath, &shellFolderKey))
1404     {
1405         TRACE("Failed to create %s\n", debugstr_w(pShellFolderPath));
1406         return E_FAIL;
1407     }
1408     if (RegCreateKeyW(rootKey, pUserShellFolderPath, &userShellFolderKey))
1409     {
1410         TRACE("Failed to create %s\n",
1411          debugstr_w(pUserShellFolderPath));
1412         RegCloseKey(shellFolderKey);
1413         return E_FAIL;
1414     }
1415 
1416     if (!RegQueryValueExW(userShellFolderKey, value, NULL, &dwType,
1417      (LPBYTE)path, &dwPathLen) && (dwType == REG_EXPAND_SZ || dwType == REG_SZ))
1418     {
1419         LONG ret;
1420 
1421         path[dwPathLen / sizeof(WCHAR)] = '\0';
1422         if (dwType == REG_EXPAND_SZ && path[0] == '%')
1423         {
1424             WCHAR szTemp[MAX_PATH];
1425 
1426             _SHExpandEnvironmentStrings(path, szTemp);
1427             lstrcpynW(path, szTemp, MAX_PATH);
1428         }
1429         ret = RegSetValueExW(shellFolderKey, value, 0, REG_SZ, (LPBYTE)path,
1430          (strlenW(path) + 1) * sizeof(WCHAR));
1431         if (ret != ERROR_SUCCESS)
1432             hr = HRESULT_FROM_WIN32(ret);
1433         else
1434             hr = S_OK;
1435     }
1436     else
1437         hr = E_FAIL;
1438     RegCloseKey(shellFolderKey);
1439     RegCloseKey(userShellFolderKey);
1440     TRACE("returning 0x%08x\n", hr);
1441     return hr;
1442 }
1443 
1444 BOOL _SHGetUserProfileDirectoryW(HANDLE hToken, LPWSTR szPath, LPDWORD lpcchPath)
1445 {
1446     BOOL result;
1447     if (!hToken)
1448     {
1449         OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
1450         result = GetUserProfileDirectoryW(hToken, szPath, lpcchPath);
1451         CloseHandle(hToken);
1452     }
1453     else if ((INT) hToken == -1)
1454     {
1455         result = GetDefaultUserProfileDirectoryW(szPath, lpcchPath);
1456     }
1457     else
1458     {
1459         result = GetUserProfileDirectoryW(hToken, szPath, lpcchPath);
1460     }
1461     DbgPrint("_SHGetUserProfileDirectoryW returning %S\n", szPath);
1462     return result;
1463 }
1464 
1465 /* Gets a 'semi-expanded' default value of the CSIDL with index folder into
1466  * pszPath, based on the entries in CSIDL_Data.  By semi-expanded, I mean:
1467  * - The entry's szDefaultPath may be either a string value or an integer
1468  *   resource identifier.  In the latter case, the string value of the resource
1469  *   is written.
1470  * - Depending on the entry's type, the path may begin with an (unexpanded)
1471  *   environment variable name.  The caller is responsible for expanding
1472  *   environment strings if so desired.
1473  *   The types that are prepended with environment variables are:
1474  *   CSIDL_Type_User:     %USERPROFILE%
1475  *   CSIDL_Type_AllUsers: %ALLUSERSPROFILE%
1476  *   CSIDL_Type_CurrVer:  %SystemDrive%
1477  *   (Others might make sense too, but as yet are unneeded.)
1478  */
1479 static HRESULT _SHGetDefaultValue(HANDLE hToken, BYTE folder, LPWSTR pszPath)
1480 {
1481     DWORD cchSize;
1482     HRESULT hr;
1483     WCHAR resourcePath[MAX_PATH];
1484 
1485     TRACE("0x%02x,%p\n", folder, pszPath);
1486 
1487     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1488         return E_INVALIDARG;
1489     if (!pszPath)
1490         return E_INVALIDARG;
1491 
1492     if (!is_win64)
1493     {
1494         BOOL is_wow64;
1495 
1496         switch (folder)
1497         {
1498         case CSIDL_PROGRAM_FILES:
1499         case CSIDL_PROGRAM_FILESX86:
1500             IsWow64Process( GetCurrentProcess(), &is_wow64 );
1501             folder = is_wow64 ? CSIDL_PROGRAM_FILESX86 : CSIDL_PROGRAM_FILES;
1502             break;
1503         case CSIDL_PROGRAM_FILES_COMMON:
1504         case CSIDL_PROGRAM_FILES_COMMONX86:
1505             IsWow64Process( GetCurrentProcess(), &is_wow64 );
1506             folder = is_wow64 ? CSIDL_PROGRAM_FILES_COMMONX86 : CSIDL_PROGRAM_FILES_COMMON;
1507             break;
1508         }
1509     }
1510 
1511     switch (CSIDL_Data[folder].type)
1512     {
1513     case CSIDL_Type_User:
1514         cchSize = MAX_PATH;
1515         if (!_SHGetUserProfileDirectoryW(hToken, pszPath, &cchSize))
1516             return HRESULT_FROM_WIN32(GetLastError());
1517         break;
1518     case CSIDL_Type_AllUsers:
1519         cchSize = MAX_PATH;
1520         if (!GetAllUsersProfileDirectoryW(pszPath, &cchSize))
1521             return HRESULT_FROM_WIN32(GetLastError());
1522         break;
1523     case CSIDL_Type_CurrVer:
1524         strcpyW(pszPath, SystemDriveW);
1525         break;
1526     default:
1527         ; /* no corresponding env. var, do nothing */
1528     }
1529 
1530     hr = S_OK;
1531     if (CSIDL_Data[folder].szDefaultPath)
1532     {
1533         if (IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath))
1534         {
1535             if (LoadStringW(shell32_hInstance,
1536                 LOWORD(CSIDL_Data[folder].szDefaultPath), resourcePath, MAX_PATH))
1537             {
1538                 PathAppendW(pszPath, resourcePath);
1539             }
1540             else
1541             {
1542                 ERR("(%d,%s), LoadString failed, missing translation?\n", folder,
1543                       debugstr_w(pszPath));
1544                 hr = E_FAIL;
1545             }
1546         }
1547         else
1548         {
1549             PathAppendW(pszPath, CSIDL_Data[folder].szDefaultPath);
1550         }
1551     }
1552     TRACE("returning 0x%08x\n", hr);
1553     return hr;
1554 }
1555 
1556 /* Gets the (unexpanded) value of the folder with index folder into pszPath.
1557  * The folder's type is assumed to be CSIDL_Type_CurrVer.  Its default value
1558  * can be overridden in the HKLM\\szCurrentVersion key.
1559  * If dwFlags has SHGFP_TYPE_DEFAULT set or if the value isn't overridden in
1560  * the registry, uses _SHGetDefaultValue to get the value.
1561  */
1562 static HRESULT _SHGetCurrentVersionPath(DWORD dwFlags, BYTE folder,
1563  LPWSTR pszPath)
1564 {
1565     HRESULT hr;
1566 
1567     TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
1568 
1569     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1570         return E_INVALIDARG;
1571     if (CSIDL_Data[folder].type != CSIDL_Type_CurrVer)
1572         return E_INVALIDARG;
1573     if (!pszPath)
1574         return E_INVALIDARG;
1575 
1576     if (dwFlags & SHGFP_TYPE_DEFAULT)
1577         hr = _SHGetDefaultValue(NULL, folder, pszPath);
1578     else
1579     {
1580         HKEY hKey;
1581 
1582         if (RegCreateKeyW(HKEY_LOCAL_MACHINE, szCurrentVersion, &hKey))
1583             hr = E_FAIL;
1584         else
1585         {
1586             DWORD dwType, dwPathLen = MAX_PATH * sizeof(WCHAR);
1587 
1588             if (RegQueryValueExW(hKey, CSIDL_Data[folder].szValueName, NULL,
1589              &dwType, (LPBYTE)pszPath, &dwPathLen) ||
1590              (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
1591             {
1592                 hr = _SHGetDefaultValue(NULL, folder, pszPath);
1593                 dwType = REG_EXPAND_SZ;
1594                 switch (folder)
1595                 {
1596                 case CSIDL_PROGRAM_FILESX86:
1597                 case CSIDL_PROGRAM_FILES_COMMONX86:
1598                     /* these two should never be set on 32-bit setups */
1599                     if (!is_win64)
1600                     {
1601                         BOOL is_wow64;
1602                         IsWow64Process( GetCurrentProcess(), &is_wow64 );
1603                         if (!is_wow64) break;
1604                     }
1605                     /* fall through */
1606                 default:
1607                     RegSetValueExW(hKey, CSIDL_Data[folder].szValueName, 0, dwType,
1608                                    (LPBYTE)pszPath, (strlenW(pszPath)+1)*sizeof(WCHAR));
1609                 }
1610             }
1611             else
1612             {
1613                 pszPath[dwPathLen / sizeof(WCHAR)] = '\0';
1614                 hr = S_OK;
1615             }
1616             RegCloseKey(hKey);
1617         }
1618     }
1619     TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1620     return hr;
1621 }
1622 
1623 static LPWSTR _GetUserSidStringFromToken(HANDLE Token)
1624 {
1625     char InfoBuffer[64];
1626     PTOKEN_USER UserInfo;
1627     DWORD InfoSize;
1628     LPWSTR SidStr;
1629 
1630     UserInfo = (PTOKEN_USER) InfoBuffer;
1631     if (! GetTokenInformation(Token, TokenUser, InfoBuffer, sizeof(InfoBuffer),
1632                               &InfoSize))
1633     {
1634         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1635             return NULL;
1636         UserInfo = HeapAlloc(GetProcessHeap(), 0, InfoSize);
1637         if (UserInfo == NULL)
1638             return NULL;
1639         if (! GetTokenInformation(Token, TokenUser, UserInfo, InfoSize,
1640                                   &InfoSize))
1641         {
1642             HeapFree(GetProcessHeap(), 0, UserInfo);
1643             return NULL;
1644         }
1645     }
1646 
1647     if (! ConvertSidToStringSidW(UserInfo->User.Sid, &SidStr))
1648         SidStr = NULL;
1649 
1650     if (UserInfo != (PTOKEN_USER) InfoBuffer)
1651         HeapFree(GetProcessHeap(), 0, UserInfo);
1652 
1653     return SidStr;
1654 }
1655 
1656 /* Gets the user's path (unexpanded) for the CSIDL with index folder:
1657  * If SHGFP_TYPE_DEFAULT is set, calls _SHGetDefaultValue for it.  Otherwise
1658  * calls _SHGetUserShellFolderPath for it.  Where it looks depends on hToken:
1659  * - if hToken is -1, looks in HKEY_USERS\.Default
1660  * - otherwise looks first in HKEY_CURRENT_USER, followed by HKEY_LOCAL_MACHINE
1661  *   if HKEY_CURRENT_USER doesn't contain any entries.  If both fail, finally
1662  *   calls _SHGetDefaultValue for it.
1663  */
1664 static HRESULT _SHGetUserProfilePath(HANDLE hToken, DWORD dwFlags, BYTE folder,
1665  LPWSTR pszPath)
1666 {
1667     const WCHAR *szValueName;
1668     WCHAR buffer[40];
1669     HRESULT hr;
1670 
1671     TRACE("%p,0x%08x,0x%02x,%p\n", hToken, dwFlags, folder, pszPath);
1672 
1673     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1674         return E_INVALIDARG;
1675     if (CSIDL_Data[folder].type != CSIDL_Type_User)
1676         return E_INVALIDARG;
1677     if (!pszPath)
1678         return E_INVALIDARG;
1679 
1680     if (dwFlags & SHGFP_TYPE_DEFAULT)
1681     {
1682         hr = _SHGetDefaultValue(hToken, folder, pszPath);
1683     }
1684     else
1685     {
1686         LPCWSTR userPrefix = NULL;
1687         HKEY hRootKey;
1688 
1689         if (hToken == (HANDLE)-1)
1690         {
1691             hRootKey = HKEY_USERS;
1692             userPrefix = DefaultW;
1693         }
1694         else if (hToken == NULL)
1695             hRootKey = HKEY_CURRENT_USER;
1696         else
1697         {
1698             hRootKey = HKEY_USERS;
1699             userPrefix = _GetUserSidStringFromToken(hToken);
1700             if (userPrefix == NULL)
1701             {
1702                 hr = E_FAIL;
1703                 goto error;
1704             }
1705         }
1706 
1707         /* For CSIDL_Type_User we also use the GUID if no szValueName is provided */
1708         szValueName = CSIDL_Data[folder].szValueName;
1709         if (!szValueName)
1710         {
1711             StringFromGUID2( CSIDL_Data[folder].id, buffer, 39 );
1712             szValueName = &buffer[0];
1713         }
1714 
1715         hr = _SHGetUserShellFolderPath(hRootKey, userPrefix, szValueName, pszPath);
1716         if (FAILED(hr) && hRootKey != HKEY_LOCAL_MACHINE)
1717             hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL, szValueName, pszPath);
1718         if (FAILED(hr))
1719             hr = _SHGetDefaultValue(hToken, folder, pszPath);
1720         if (userPrefix != NULL && userPrefix != DefaultW)
1721             LocalFree((HLOCAL) userPrefix);
1722     }
1723 error:
1724     TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1725     return hr;
1726 }
1727 
1728 /* Gets the (unexpanded) path for the CSIDL with index folder.  If dwFlags has
1729  * SHGFP_TYPE_DEFAULT set, calls _SHGetDefaultValue.  Otherwise calls
1730  * _SHGetUserShellFolderPath for it, looking only in HKEY_LOCAL_MACHINE.
1731  * If this fails, falls back to _SHGetDefaultValue.
1732  */
1733 static HRESULT _SHGetAllUsersProfilePath(DWORD dwFlags, BYTE folder,
1734  LPWSTR pszPath)
1735 {
1736     HRESULT hr;
1737 
1738     TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
1739 
1740     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1741         return E_INVALIDARG;
1742     if (CSIDL_Data[folder].type != CSIDL_Type_AllUsers)
1743         return E_INVALIDARG;
1744     if (!pszPath)
1745         return E_INVALIDARG;
1746 
1747     if (dwFlags & SHGFP_TYPE_DEFAULT)
1748         hr = _SHGetDefaultValue(NULL, folder, pszPath);
1749     else
1750     {
1751         hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL,
1752          CSIDL_Data[folder].szValueName, pszPath);
1753         if (FAILED(hr))
1754             hr = _SHGetDefaultValue(NULL, folder, pszPath);
1755     }
1756     TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1757     return hr;
1758 }
1759 
1760 static HRESULT _SHOpenProfilesKey(PHKEY pKey)
1761 {
1762     LONG lRet;
1763     DWORD disp;
1764 
1765     lRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, ProfileListW, 0, NULL, 0,
1766      KEY_ALL_ACCESS, NULL, pKey, &disp);
1767     return HRESULT_FROM_WIN32(lRet);
1768 }
1769 
1770 /* Reads the value named szValueName from the key profilesKey (assumed to be
1771  * opened by _SHOpenProfilesKey) into szValue, which is assumed to be MAX_PATH
1772  * WCHARs in length.  If it doesn't exist, returns szDefault (and saves
1773  * szDefault to the registry).
1774  */
1775 static HRESULT _SHGetProfilesValue(HKEY profilesKey, LPCWSTR szValueName,
1776  LPWSTR szValue, LPCWSTR szDefault)
1777 {
1778     HRESULT hr;
1779     DWORD type, dwPathLen = MAX_PATH * sizeof(WCHAR);
1780     LONG lRet;
1781 
1782     TRACE("%p,%s,%p,%s\n", profilesKey, debugstr_w(szValueName), szValue,
1783      debugstr_w(szDefault));
1784     lRet = RegQueryValueExW(profilesKey, szValueName, NULL, &type,
1785      (LPBYTE)szValue, &dwPathLen);
1786     if (!lRet && (type == REG_SZ || type == REG_EXPAND_SZ) && dwPathLen
1787      && *szValue)
1788     {
1789         dwPathLen /= sizeof(WCHAR);
1790         szValue[dwPathLen] = '\0';
1791         hr = S_OK;
1792     }
1793     else
1794     {
1795         /* Missing or invalid value, set a default */
1796         lstrcpynW(szValue, szDefault, MAX_PATH);
1797         TRACE("Setting missing value %s to %s\n", debugstr_w(szValueName),
1798                                                   debugstr_w(szValue));
1799         lRet = RegSetValueExW(profilesKey, szValueName, 0, REG_EXPAND_SZ,
1800                               (LPBYTE)szValue,
1801                               (strlenW(szValue) + 1) * sizeof(WCHAR));
1802         if (lRet)
1803             hr = HRESULT_FROM_WIN32(lRet);
1804         else
1805             hr = S_OK;
1806     }
1807     TRACE("returning 0x%08x (output value is %s)\n", hr, debugstr_w(szValue));
1808     return hr;
1809 }
1810 
1811 /* Attempts to expand environment variables from szSrc into szDest, which is
1812  * assumed to be MAX_PATH characters in length.  Before referring to the
1813  * environment, handles a few variables directly, because the environment
1814  * variables may not be set when this is called (as during Wine's installation
1815  * when default values are being written to the registry).
1816  * The directly handled environment variables, and their source, are:
1817  * - ALLUSERSPROFILE, USERPROFILE: reads from the registry
1818  * - SystemDrive: uses GetSystemDirectoryW and uses the drive portion of its
1819  *   path
1820  * If one of the directly handled environment variables is expanded, only
1821  * expands a single variable, and only in the beginning of szSrc.
1822  */
1823 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest)
1824 {
1825     HRESULT hr;
1826     WCHAR szTemp[MAX_PATH], szProfilesPrefix[MAX_PATH] = { 0 };
1827     HKEY key = NULL;
1828 
1829     TRACE("%s, %p\n", debugstr_w(szSrc), szDest);
1830 
1831     if (!szSrc || !szDest) return E_INVALIDARG;
1832 
1833     /* short-circuit if there's nothing to expand */
1834     if (szSrc[0] != '%')
1835     {
1836         strcpyW(szDest, szSrc);
1837         hr = S_OK;
1838         goto end;
1839     }
1840     /* Get the profile prefix, we'll probably be needing it */
1841     hr = _SHOpenProfilesKey(&key);
1842     if (SUCCEEDED(hr))
1843     {
1844         WCHAR def_val[MAX_PATH];
1845 
1846         /* get the system drive */
1847         GetSystemDirectoryW(def_val, MAX_PATH);
1848         if (def_val[1] == ':') strcpyW( def_val + 3, szDefaultProfileDirW );
1849         else FIXME("non-drive system paths unsupported\n");
1850 
1851         hr = _SHGetProfilesValue(key, ProfilesDirectoryW, szProfilesPrefix, def_val );
1852     }
1853 
1854     *szDest = 0;
1855     strcpyW(szTemp, szSrc);
1856     while (SUCCEEDED(hr) && szTemp[0] == '%')
1857     {
1858         if (!strncmpiW(szTemp, AllUsersProfileW, strlenW(AllUsersProfileW)))
1859         {
1860             WCHAR szAllUsers[MAX_PATH];
1861 
1862             strcpyW(szDest, szProfilesPrefix);
1863             hr = _SHGetProfilesValue(key, AllUsersProfileValueW,
1864              szAllUsers, AllUsersW);
1865             PathAppendW(szDest, szAllUsers);
1866             PathAppendW(szDest, szTemp + strlenW(AllUsersProfileW));
1867         }
1868         else if (!strncmpiW(szTemp, UserProfileW, strlenW(UserProfileW)))
1869         {
1870             WCHAR userName[MAX_PATH];
1871             DWORD userLen = MAX_PATH;
1872 
1873             strcpyW(szDest, szProfilesPrefix);
1874             GetUserNameW(userName, &userLen);
1875             PathAppendW(szDest, userName);
1876             PathAppendW(szDest, szTemp + strlenW(UserProfileW));
1877         }
1878         else if (!strncmpiW(szTemp, SystemDriveW, strlenW(SystemDriveW)))
1879         {
1880             GetSystemDirectoryW(szDest, MAX_PATH);
1881             if (szDest[1] != ':')
1882             {
1883                 FIXME("non-drive system paths unsupported\n");
1884                 hr = E_FAIL;
1885             }
1886             else
1887             {
1888                 strcpyW(szDest + 3, szTemp + strlenW(SystemDriveW) + 1);
1889                 hr = S_OK;
1890             }
1891         }
1892         else
1893         {
1894             DWORD ret = ExpandEnvironmentStringsW(szSrc, szDest, MAX_PATH);
1895 
1896             if (ret > MAX_PATH)
1897                 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
1898             else if (ret == 0)
1899                 hr = HRESULT_FROM_WIN32(GetLastError());
1900             else
1901                 hr = S_OK;
1902         }
1903         if (SUCCEEDED(hr) && szDest[0] == '%')
1904             strcpyW(szTemp, szDest);
1905         else
1906         {
1907             /* terminate loop */
1908             szTemp[0] = '\0';
1909         }
1910     }
1911 end:
1912     if (key)
1913         RegCloseKey(key);
1914     TRACE("returning 0x%08x (input was %s, output is %s)\n", hr,
1915      debugstr_w(szSrc), debugstr_w(szDest));
1916     return hr;
1917 }
1918 
1919 /*************************************************************************
1920  * SHGetFolderPathW			[SHELL32.@]
1921  *
1922  * Convert nFolder to path.
1923  *
1924  * RETURNS
1925  *  Success: S_OK
1926  *  Failure: standard HRESULT error codes.
1927  *
1928  * NOTES
1929  * Most values can be overridden in either
1930  * HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
1931  * or in the same location in HKLM.
1932  * The "Shell Folders" registry key was used in NT4 and earlier systems.
1933  * Beginning with Windows 2000, the "User Shell Folders" key is used, so
1934  * changes made to it are made to the former key too.  This synchronization is
1935  * done on-demand: not until someone requests the value of one of these paths
1936  * (by calling one of the SHGet functions) is the value synchronized.
1937  * Furthermore, the HKCU paths take precedence over the HKLM paths.
1938  */
1939 HRESULT WINAPI SHGetFolderPathW(
1940 	HWND hwndOwner,    /* [I] owner window */
1941 	int nFolder,       /* [I] CSIDL identifying the folder */
1942 	HANDLE hToken,     /* [I] access token */
1943 	DWORD dwFlags,     /* [I] which path to return */
1944 	LPWSTR pszPath)    /* [O] converted path */
1945 {
1946     HRESULT hr =  SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, NULL, pszPath);
1947     if(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
1948         hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
1949     return hr;
1950 }
1951 
1952 HRESULT WINAPI SHGetFolderPathAndSubDirA(
1953 	HWND hwndOwner,    /* [I] owner window */
1954 	int nFolder,       /* [I] CSIDL identifying the folder */
1955 	HANDLE hToken,     /* [I] access token */
1956 	DWORD dwFlags,     /* [I] which path to return */
1957 	LPCSTR pszSubPath, /* [I] sub directory of the specified folder */
1958 	LPSTR pszPath)     /* [O] converted path */
1959 {
1960     int length;
1961     HRESULT hr = S_OK;
1962     LPWSTR pszSubPathW = NULL;
1963     LPWSTR pszPathW = NULL;
1964     TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
1965 
1966     if(pszPath) {
1967         pszPathW = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
1968         if(!pszPathW) {
1969             hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
1970             goto cleanup;
1971         }
1972     }
1973     TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
1974 
1975     /* SHGetFolderPathAndSubDirW does not distinguish if pszSubPath isn't
1976      * set (null), or an empty string.therefore call it without the parameter set
1977      * if pszSubPath is an empty string
1978      */
1979     if (pszSubPath && pszSubPath[0]) {
1980         length = MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, NULL, 0);
1981         pszSubPathW = HeapAlloc(GetProcessHeap(), 0, length * sizeof(WCHAR));
1982         if(!pszSubPathW) {
1983             hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
1984             goto cleanup;
1985         }
1986         MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, pszSubPathW, length);
1987     }
1988 
1989     hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, pszSubPathW, pszPathW);
1990 
1991     if (SUCCEEDED(hr) && pszPath)
1992         WideCharToMultiByte(CP_ACP, 0, pszPathW, -1, pszPath, MAX_PATH, NULL, NULL);
1993 
1994 cleanup:
1995     HeapFree(GetProcessHeap(), 0, pszPathW);
1996     HeapFree(GetProcessHeap(), 0, pszSubPathW);
1997     return hr;
1998 }
1999 
2000 /*************************************************************************
2001  * SHGetFolderPathAndSubDirW		[SHELL32.@]
2002  */
2003 HRESULT WINAPI SHGetFolderPathAndSubDirW(
2004 	HWND hwndOwner,    /* [I] owner window */
2005 	int nFolder,       /* [I] CSIDL identifying the folder */
2006 	HANDLE hToken,     /* [I] access token */
2007 	DWORD dwFlags,     /* [I] which path to return */
2008 	LPCWSTR pszSubPath,/* [I] sub directory of the specified folder */
2009 	LPWSTR pszPath)    /* [O] converted path */
2010 {
2011     HRESULT    hr;
2012     WCHAR      szBuildPath[MAX_PATH], szTemp[MAX_PATH];
2013     DWORD      folder = nFolder & CSIDL_FOLDER_MASK;
2014     CSIDL_Type type;
2015     int        ret;
2016 
2017     TRACE("%p,%p,nFolder=0x%04x,%s\n", hwndOwner,pszPath,nFolder,debugstr_w(pszSubPath));
2018 
2019     /* Windows always NULL-terminates the resulting path regardless of success
2020      * or failure, so do so first
2021      */
2022     if (pszPath)
2023         *pszPath = '\0';
2024 
2025     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
2026         return E_INVALIDARG;
2027     if ((SHGFP_TYPE_CURRENT != dwFlags) && (SHGFP_TYPE_DEFAULT != dwFlags))
2028         return E_INVALIDARG;
2029     szTemp[0] = 0;
2030     type = CSIDL_Data[folder].type;
2031     switch (type)
2032     {
2033         case CSIDL_Type_Disallowed:
2034             hr = E_INVALIDARG;
2035             break;
2036         case CSIDL_Type_NonExistent:
2037             hr = S_FALSE;
2038             break;
2039         case CSIDL_Type_WindowsPath:
2040             GetWindowsDirectoryW(szTemp, MAX_PATH);
2041             if (CSIDL_Data[folder].szDefaultPath &&
2042              !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
2043              *CSIDL_Data[folder].szDefaultPath)
2044             {
2045                 PathAddBackslashW(szTemp);
2046                 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
2047             }
2048             hr = S_OK;
2049             break;
2050         case CSIDL_Type_SystemPath:
2051             GetSystemDirectoryW(szTemp, MAX_PATH);
2052             if (CSIDL_Data[folder].szDefaultPath &&
2053              !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
2054              *CSIDL_Data[folder].szDefaultPath)
2055             {
2056                 PathAddBackslashW(szTemp);
2057                 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
2058             }
2059             hr = S_OK;
2060             break;
2061         case CSIDL_Type_SystemX86Path:
2062             if (!GetSystemWow64DirectoryW(szTemp, MAX_PATH)) GetSystemDirectoryW(szTemp, MAX_PATH);
2063             if (CSIDL_Data[folder].szDefaultPath &&
2064              !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
2065              *CSIDL_Data[folder].szDefaultPath)
2066             {
2067                 PathAddBackslashW(szTemp);
2068                 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
2069             }
2070             hr = S_OK;
2071             break;
2072         case CSIDL_Type_CurrVer:
2073             hr = _SHGetCurrentVersionPath(dwFlags, folder, szTemp);
2074             break;
2075         case CSIDL_Type_User:
2076             hr = _SHGetUserProfilePath(hToken, dwFlags, folder, szTemp);
2077             break;
2078         case CSIDL_Type_AllUsers:
2079             hr = _SHGetAllUsersProfilePath(dwFlags, folder, szTemp);
2080             break;
2081         default:
2082             FIXME("bogus type %d, please fix\n", type);
2083             hr = E_INVALIDARG;
2084             break;
2085     }
2086 
2087     /* Expand environment strings if necessary */
2088     if (*szTemp == '%')
2089         hr = _SHExpandEnvironmentStrings(szTemp, szBuildPath);
2090     else
2091         strcpyW(szBuildPath, szTemp);
2092 
2093     if (FAILED(hr)) goto end;
2094 
2095     if(pszSubPath) {
2096         /* make sure the new path does not exceed the buffer length
2097          * and remember to backslash and terminate it */
2098         if(MAX_PATH < (lstrlenW(szBuildPath) + lstrlenW(pszSubPath) + 2)) {
2099             hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
2100             goto end;
2101         }
2102         PathAppendW(szBuildPath, pszSubPath);
2103         PathRemoveBackslashW(szBuildPath);
2104     }
2105     /* Copy the path if it's available before we might return */
2106     if (SUCCEEDED(hr) && pszPath)
2107         strcpyW(pszPath, szBuildPath);
2108 
2109     /* if we don't care about existing directories we are ready */
2110     if(nFolder & CSIDL_FLAG_DONT_VERIFY) goto end;
2111 
2112     if (PathFileExistsW(szBuildPath)) goto end;
2113 
2114     /* not existing but we are not allowed to create it.  The return value
2115      * is verified against shell32 version 6.0.
2116      */
2117     if (!(nFolder & CSIDL_FLAG_CREATE))
2118     {
2119         hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
2120         goto end;
2121     }
2122 
2123     /* create directory/directories */
2124     ret = SHCreateDirectoryExW(hwndOwner, szBuildPath, NULL);
2125     if (ret && ret != ERROR_ALREADY_EXISTS)
2126     {
2127         ERR("Failed to create directory %s.\n", debugstr_w(szBuildPath));
2128         hr = E_FAIL;
2129         goto end;
2130     }
2131 
2132     TRACE("Created missing system directory %s\n", debugstr_w(szBuildPath));
2133 end:
2134     TRACE("returning 0x%08x (final path is %s)\n", hr, debugstr_w(szBuildPath));
2135     return hr;
2136 }
2137 
2138 /*************************************************************************
2139  * SHGetFolderPathA			[SHELL32.@]
2140  *
2141  * See SHGetFolderPathW.
2142  */
2143 HRESULT WINAPI SHGetFolderPathA(
2144 	HWND hwndOwner,
2145 	int nFolder,
2146 	HANDLE hToken,
2147 	DWORD dwFlags,
2148 	LPSTR pszPath)
2149 {
2150     WCHAR szTemp[MAX_PATH];
2151     HRESULT hr;
2152 
2153     TRACE("%p,%p,nFolder=0x%04x\n",hwndOwner,pszPath,nFolder);
2154 
2155     if (pszPath)
2156         *pszPath = '\0';
2157     hr = SHGetFolderPathW(hwndOwner, nFolder, hToken, dwFlags, szTemp);
2158     if (SUCCEEDED(hr) && pszPath)
2159         WideCharToMultiByte(CP_ACP, 0, szTemp, -1, pszPath, MAX_PATH, NULL,
2160          NULL);
2161 
2162     return hr;
2163 }
2164 
2165 /* For each folder in folders, if its value has not been set in the registry,
2166  * calls _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the
2167  * folder's type) to get the unexpanded value first.
2168  * Writes the unexpanded value to User Shell Folders, and queries it with
2169  * SHGetFolderPathW to force the creation of the directory if it doesn't
2170  * already exist.  SHGetFolderPathW also returns the expanded value, which
2171  * this then writes to Shell Folders.
2172  */
2173 static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken,
2174  LPCWSTR szUserShellFolderPath, LPCWSTR szShellFolderPath, const UINT folders[],
2175  UINT foldersLen)
2176 {
2177     const WCHAR *szValueName;
2178     WCHAR buffer[40];
2179     UINT i;
2180     WCHAR path[MAX_PATH];
2181     HRESULT hr = S_OK;
2182     HKEY hUserKey = NULL, hKey = NULL;
2183     DWORD dwType, dwPathLen;
2184     LONG ret;
2185 
2186     TRACE("%p,%p,%s,%p,%u\n", hRootKey, hToken,
2187      debugstr_w(szUserShellFolderPath), folders, foldersLen);
2188 
2189     ret = RegCreateKeyW(hRootKey, szUserShellFolderPath, &hUserKey);
2190     if (ret)
2191         hr = HRESULT_FROM_WIN32(ret);
2192     else
2193     {
2194         ret = RegCreateKeyW(hRootKey, szShellFolderPath, &hKey);
2195         if (ret)
2196             hr = HRESULT_FROM_WIN32(ret);
2197     }
2198     for (i = 0; SUCCEEDED(hr) && i < foldersLen; i++)
2199     {
2200         dwPathLen = MAX_PATH * sizeof(WCHAR);
2201 
2202         /* For CSIDL_Type_User we also use the GUID if no szValueName is provided */
2203         szValueName = CSIDL_Data[folders[i]].szValueName;
2204         if (!szValueName && CSIDL_Data[folders[i]].type == CSIDL_Type_User)
2205         {
2206             StringFromGUID2( CSIDL_Data[folders[i]].id, buffer, 39 );
2207             szValueName = &buffer[0];
2208         }
2209 
2210         if (RegQueryValueExW(hUserKey, szValueName, NULL,
2211          &dwType, (LPBYTE)path, &dwPathLen) || (dwType != REG_SZ &&
2212          dwType != REG_EXPAND_SZ))
2213         {
2214             *path = '\0';
2215             if (CSIDL_Data[folders[i]].type == CSIDL_Type_User)
2216                 _SHGetUserProfilePath(hToken, SHGFP_TYPE_DEFAULT, folders[i],
2217                  path);
2218             else if (CSIDL_Data[folders[i]].type == CSIDL_Type_AllUsers)
2219                 _SHGetAllUsersProfilePath(SHGFP_TYPE_DEFAULT, folders[i], path);
2220             else if (CSIDL_Data[folders[i]].type == CSIDL_Type_WindowsPath)
2221             {
2222                 GetWindowsDirectoryW(path, MAX_PATH);
2223                 if (CSIDL_Data[folders[i]].szDefaultPath &&
2224                     !IS_INTRESOURCE(CSIDL_Data[folders[i]].szDefaultPath))
2225                 {
2226                     PathAddBackslashW(path);
2227                     strcatW(path, CSIDL_Data[folders[i]].szDefaultPath);
2228                 }
2229             }
2230             else
2231                 hr = E_FAIL;
2232             if (*path)
2233             {
2234                 ret = RegSetValueExW(hUserKey, szValueName, 0, REG_EXPAND_SZ,
2235                  (LPBYTE)path, (strlenW(path) + 1) * sizeof(WCHAR));
2236                 if (ret)
2237                     hr = HRESULT_FROM_WIN32(ret);
2238                 else
2239                 {
2240                     hr = SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
2241                      hToken, SHGFP_TYPE_DEFAULT, path);
2242                     ret = RegSetValueExW(hKey, szValueName, 0, REG_SZ,
2243                      (LPBYTE)path, (strlenW(path) + 1) * sizeof(WCHAR));
2244                     if (ret)
2245                         hr = HRESULT_FROM_WIN32(ret);
2246                 }
2247             }
2248         }
2249     }
2250     if (hUserKey)
2251         RegCloseKey(hUserKey);
2252     if (hKey)
2253         RegCloseKey(hKey);
2254 
2255     TRACE("returning 0x%08x\n", hr);
2256     return hr;
2257 }
2258 
2259 static HRESULT _SHRegisterUserShellFolders(BOOL bDefault)
2260 {
2261     static const UINT folders[] = {
2262      CSIDL_PROGRAMS,
2263      CSIDL_PERSONAL,
2264      CSIDL_FAVORITES,
2265      CSIDL_APPDATA,
2266      CSIDL_STARTUP,
2267      CSIDL_RECENT,
2268      CSIDL_SENDTO,
2269      CSIDL_STARTMENU,
2270      CSIDL_MYMUSIC,
2271      CSIDL_MYVIDEO,
2272      CSIDL_DESKTOPDIRECTORY,
2273      CSIDL_NETHOOD,
2274      CSIDL_TEMPLATES,
2275      CSIDL_PRINTHOOD,
2276      CSIDL_LOCAL_APPDATA,
2277      CSIDL_INTERNET_CACHE,
2278      CSIDL_COOKIES,
2279      CSIDL_HISTORY,
2280      CSIDL_MYPICTURES,
2281      CSIDL_FONTS,
2282      CSIDL_ADMINTOOLS,
2283 /* Cannot use #if _WIN32_WINNT >= 0x0600 because _WIN32_WINNT == 0x0600 here. */
2284 #ifndef __REACTOS__
2285      CSIDL_CONTACTS,
2286      CSIDL_DOWNLOADS,
2287      CSIDL_LINKS,
2288      CSIDL_APPDATA_LOCALLOW,
2289      CSIDL_SAVED_GAMES,
2290      CSIDL_SEARCHES
2291 #endif
2292     };
2293     WCHAR userShellFolderPath[MAX_PATH], shellFolderPath[MAX_PATH];
2294     LPCWSTR pUserShellFolderPath, pShellFolderPath;
2295     HRESULT hr = S_OK;
2296     HKEY hRootKey;
2297     HANDLE hToken;
2298 
2299     TRACE("%s\n", bDefault ? "TRUE" : "FALSE");
2300     if (bDefault)
2301     {
2302         hToken = (HANDLE)-1;
2303         hRootKey = HKEY_USERS;
2304         strcpyW(userShellFolderPath, DefaultW);
2305         PathAddBackslashW(userShellFolderPath);
2306         strcatW(userShellFolderPath, szSHUserFolders);
2307         pUserShellFolderPath = userShellFolderPath;
2308         strcpyW(shellFolderPath, DefaultW);
2309         PathAddBackslashW(shellFolderPath);
2310         strcatW(shellFolderPath, szSHFolders);
2311         pShellFolderPath = shellFolderPath;
2312     }
2313     else
2314     {
2315         hToken = NULL;
2316         hRootKey = HKEY_CURRENT_USER;
2317         pUserShellFolderPath = szSHUserFolders;
2318         pShellFolderPath = szSHFolders;
2319     }
2320 
2321     hr = _SHRegisterFolders(hRootKey, hToken, pUserShellFolderPath,
2322      pShellFolderPath, folders, sizeof(folders) / sizeof(folders[0]));
2323     TRACE("returning 0x%08x\n", hr);
2324     return hr;
2325 }
2326 
2327 static HRESULT _SHRegisterCommonShellFolders(void)
2328 {
2329     static const UINT folders[] = {
2330      CSIDL_COMMON_STARTMENU,
2331      CSIDL_COMMON_PROGRAMS,
2332      CSIDL_COMMON_STARTUP,
2333      CSIDL_COMMON_DESKTOPDIRECTORY,
2334      CSIDL_COMMON_FAVORITES,
2335      CSIDL_COMMON_APPDATA,
2336      CSIDL_COMMON_TEMPLATES,
2337      CSIDL_COMMON_DOCUMENTS,
2338      CSIDL_COMMON_ADMINTOOLS,
2339      CSIDL_COMMON_MUSIC,
2340      CSIDL_COMMON_PICTURES,
2341      CSIDL_COMMON_VIDEO,
2342     };
2343     HRESULT hr;
2344 
2345     TRACE("\n");
2346     hr = _SHRegisterFolders(HKEY_LOCAL_MACHINE, NULL, szSHUserFolders,
2347      szSHFolders, folders, sizeof(folders) / sizeof(folders[0]));
2348     TRACE("returning 0x%08x\n", hr);
2349     return hr;
2350 }
2351 
2352 /* Register the default values in the registry, as some apps seem to depend
2353  * on their presence.  The set registered was taken from Windows XP.
2354  */
2355 HRESULT SHELL_RegisterShellFolders(void)
2356 {
2357     HRESULT hr;
2358 
2359     hr = _SHRegisterUserShellFolders(TRUE);
2360     if (SUCCEEDED(hr))
2361         hr = _SHRegisterUserShellFolders(FALSE);
2362     if (SUCCEEDED(hr))
2363         hr = _SHRegisterCommonShellFolders();
2364     return hr;
2365 }
2366 
2367 /*************************************************************************
2368  * SHGetSpecialFolderPathA [SHELL32.@]
2369  */
2370 BOOL WINAPI SHGetSpecialFolderPathA (
2371 	HWND hwndOwner,
2372 	LPSTR szPath,
2373 	int nFolder,
2374 	BOOL bCreate)
2375 {
2376     return SHGetFolderPathA(hwndOwner, nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0), NULL, 0,
2377                             szPath) == S_OK;
2378 }
2379 
2380 /*************************************************************************
2381  * SHGetSpecialFolderPathW
2382  */
2383 BOOL WINAPI SHGetSpecialFolderPathW (
2384 	HWND hwndOwner,
2385 	LPWSTR szPath,
2386 	int nFolder,
2387 	BOOL bCreate)
2388 {
2389     return SHGetFolderPathW(hwndOwner, nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0), NULL, 0,
2390                             szPath) == S_OK;
2391 }
2392 
2393 /*************************************************************************
2394  * SHGetFolderLocation [SHELL32.@]
2395  *
2396  * Gets the folder locations from the registry and creates a pidl.
2397  *
2398  * PARAMS
2399  *   hwndOwner  [I]
2400  *   nFolder    [I] CSIDL_xxxxx
2401  *   hToken     [I] token representing user, or NULL for current user, or -1 for
2402  *                  default user
2403  *   dwReserved [I] must be zero
2404  *   ppidl      [O] PIDL of a special folder
2405  *
2406  * RETURNS
2407  *  Success: S_OK
2408  *  Failure: Standard OLE-defined error result, S_FALSE or E_INVALIDARG
2409  *
2410  * NOTES
2411  *  Creates missing reg keys and directories.
2412  *  Mostly forwards to SHGetFolderPathW, but a few values of nFolder return
2413  *  virtual folders that are handled here.
2414  */
2415 HRESULT WINAPI SHGetFolderLocation(
2416 	HWND hwndOwner,
2417 	int nFolder,
2418 	HANDLE hToken,
2419 	DWORD dwReserved,
2420 	LPITEMIDLIST *ppidl)
2421 {
2422     HRESULT hr = E_INVALIDARG;
2423 
2424     TRACE("%p 0x%08x %p 0x%08x %p\n",
2425      hwndOwner, nFolder, hToken, dwReserved, ppidl);
2426 
2427     if (!ppidl)
2428         return E_INVALIDARG;
2429     if (dwReserved)
2430         return E_INVALIDARG;
2431 
2432     /* The virtual folders' locations are not user-dependent */
2433     *ppidl = NULL;
2434     switch (nFolder & CSIDL_FOLDER_MASK)
2435     {
2436         case CSIDL_DESKTOP:
2437             *ppidl = _ILCreateDesktop();
2438             break;
2439 
2440         case CSIDL_PERSONAL:
2441             *ppidl = _ILCreateMyDocuments();
2442             break;
2443 
2444         case CSIDL_INTERNET:
2445             *ppidl = _ILCreateIExplore();
2446             break;
2447 
2448         case CSIDL_CONTROLS:
2449             *ppidl = _ILCreateControlPanel();
2450             break;
2451 
2452         case CSIDL_PRINTERS:
2453             *ppidl = _ILCreatePrinters();
2454             break;
2455 
2456         case CSIDL_BITBUCKET:
2457             *ppidl = _ILCreateBitBucket();
2458             break;
2459 
2460         case CSIDL_DRIVES:
2461             *ppidl = _ILCreateMyComputer();
2462             break;
2463 
2464         case CSIDL_NETWORK:
2465             *ppidl = _ILCreateNetwork();
2466             break;
2467 
2468         default:
2469         {
2470             WCHAR szPath[MAX_PATH];
2471 
2472             hr = SHGetFolderPathW(hwndOwner, nFolder, hToken,
2473              SHGFP_TYPE_CURRENT, szPath);
2474             if (SUCCEEDED(hr))
2475             {
2476                 DWORD attributes=0;
2477 
2478                 TRACE("Value=%s\n", debugstr_w(szPath));
2479                 hr = SHILCreateFromPathW(szPath, ppidl, &attributes);
2480             }
2481             else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
2482             {
2483                 /* unlike SHGetFolderPath, SHGetFolderLocation in shell32
2484                  * version 6.0 returns E_FAIL for nonexistent paths
2485                  */
2486                 hr = E_FAIL;
2487             }
2488         }
2489     }
2490     if(*ppidl)
2491         hr = S_OK;
2492 
2493     TRACE("-- (new pidl %p)\n",*ppidl);
2494     return hr;
2495 }
2496 
2497 /*************************************************************************
2498  * SHGetSpecialFolderLocation		[SHELL32.@]
2499  *
2500  * NOTES
2501  *   In NT5, SHGetSpecialFolderLocation needs the <winntdir>/Recent
2502  *   directory.
2503  */
2504 HRESULT WINAPI SHGetSpecialFolderLocation(
2505 	HWND hwndOwner,
2506 	INT nFolder,
2507 	LPITEMIDLIST * ppidl)
2508 {
2509     HRESULT hr = E_INVALIDARG;
2510 
2511     TRACE("(%p,0x%x,%p)\n", hwndOwner,nFolder,ppidl);
2512 
2513     if (!ppidl)
2514         return E_INVALIDARG;
2515 
2516     hr = SHGetFolderLocation(hwndOwner, nFolder, NULL, 0, ppidl);
2517     return hr;
2518 }
2519