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