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