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