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