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