vbscript: Handle index read access to array properties.
[wine.git] / dlls / shell32 / shellpath.c
blob5dd9f4b6310190b08271332f25e9cfc02403e1d2
1 /*
2 * Path Functions
4 * Copyright 1998, 1999, 2000 Juergen Schmied
5 * Copyright 2004 Juan Lang
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * NOTES:
23 * Many of these functions are in SHLWAPI.DLL also
27 #define COBJMACROS
29 #include <stdio.h>
30 #include <stdarg.h>
31 #include <string.h>
32 #include <ctype.h>
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winternl.h"
37 #include "winnls.h"
38 #include "winreg.h"
39 #include "wingdi.h"
40 #include "winuser.h"
41 #include "winioctl.h"
42 #define WINE_MOUNTMGR_EXTENSIONS
43 #include "ddk/mountmgr.h"
45 #include "shlobj.h"
46 #include "shtypes.h"
47 #include "shresdef.h"
48 #include "shell32_main.h"
49 #include "pidl.h"
50 #include "shlwapi.h"
51 #include "sddl.h"
52 #include "knownfolders.h"
53 #include "initguid.h"
54 #include "shobjidl.h"
55 #include "wine/debug.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(shell);
59 static const BOOL is_win64 = sizeof(void *) > sizeof(int);
62 ########## Combining and Constructing paths ##########
65 /*************************************************************************
66 * PathAppend [SHELL32.36]
68 BOOL WINAPI PathAppendAW(
69 LPVOID lpszPath1,
70 LPCVOID lpszPath2)
72 if (SHELL_OsIsUnicode())
73 return PathAppendW(lpszPath1, lpszPath2);
74 return PathAppendA(lpszPath1, lpszPath2);
77 /*************************************************************************
78 * PathCombine [SHELL32.37]
80 LPVOID WINAPI PathCombineAW(
81 LPVOID szDest,
82 LPCVOID lpszDir,
83 LPCVOID lpszFile)
85 if (SHELL_OsIsUnicode())
86 return PathCombineW( szDest, lpszDir, lpszFile );
87 return PathCombineA( szDest, lpszDir, lpszFile );
90 /*************************************************************************
91 * PathAddBackslash [SHELL32.32]
93 LPVOID WINAPI PathAddBackslashAW(LPVOID lpszPath)
95 if(SHELL_OsIsUnicode())
96 return PathAddBackslashW(lpszPath);
97 return PathAddBackslashA(lpszPath);
100 /*************************************************************************
101 * PathBuildRoot [SHELL32.30]
103 LPVOID WINAPI PathBuildRootAW(LPVOID lpszPath, int drive)
105 if(SHELL_OsIsUnicode())
106 return PathBuildRootW(lpszPath, drive);
107 return PathBuildRootA(lpszPath, drive);
111 Extracting Component Parts
114 /*************************************************************************
115 * PathFindFileName [SHELL32.34]
117 LPVOID WINAPI PathFindFileNameAW(LPCVOID lpszPath)
119 if(SHELL_OsIsUnicode())
120 return PathFindFileNameW(lpszPath);
121 return PathFindFileNameA(lpszPath);
124 /*************************************************************************
125 * PathFindExtension [SHELL32.31]
127 LPVOID WINAPI PathFindExtensionAW(LPCVOID lpszPath)
129 if (SHELL_OsIsUnicode())
130 return PathFindExtensionW(lpszPath);
131 return PathFindExtensionA(lpszPath);
135 /*************************************************************************
136 * PathGetExtensionA [internal]
138 * NOTES
139 * exported by ordinal
140 * return value points to the first char after the dot
142 static LPSTR PathGetExtensionA(LPCSTR lpszPath)
144 TRACE("(%s)\n",lpszPath);
146 lpszPath = PathFindExtensionA(lpszPath);
147 return (LPSTR)(*lpszPath?(lpszPath+1):lpszPath);
150 /*************************************************************************
151 * PathGetExtensionW [internal]
153 static LPWSTR PathGetExtensionW(LPCWSTR lpszPath)
155 TRACE("(%s)\n",debugstr_w(lpszPath));
157 lpszPath = PathFindExtensionW(lpszPath);
158 return (LPWSTR)(*lpszPath?(lpszPath+1):lpszPath);
161 /*************************************************************************
162 * PathGetExtension [SHELL32.158]
164 LPVOID WINAPI PathGetExtensionAW(LPCVOID lpszPath,DWORD void1, DWORD void2)
166 if (SHELL_OsIsUnicode())
167 return PathGetExtensionW(lpszPath);
168 return PathGetExtensionA(lpszPath);
171 /*************************************************************************
172 * PathGetArgs [SHELL32.52]
174 LPVOID WINAPI PathGetArgsAW(LPVOID lpszPath)
176 if (SHELL_OsIsUnicode())
177 return PathGetArgsW(lpszPath);
178 return PathGetArgsA(lpszPath);
181 /*************************************************************************
182 * PathGetDriveNumber [SHELL32.57]
184 int WINAPI PathGetDriveNumberAW(LPVOID lpszPath)
186 if (SHELL_OsIsUnicode())
187 return PathGetDriveNumberW(lpszPath);
188 return PathGetDriveNumberA(lpszPath);
191 /*************************************************************************
192 * PathRemoveFileSpec [SHELL32.35]
194 BOOL WINAPI PathRemoveFileSpecAW(LPVOID lpszPath)
196 if (SHELL_OsIsUnicode())
197 return PathRemoveFileSpecW(lpszPath);
198 return PathRemoveFileSpecA(lpszPath);
201 /*************************************************************************
202 * PathStripPath [SHELL32.38]
204 void WINAPI PathStripPathAW(LPVOID lpszPath)
206 if (SHELL_OsIsUnicode())
207 PathStripPathW(lpszPath);
208 else
209 PathStripPathA(lpszPath);
212 /*************************************************************************
213 * PathStripToRoot [SHELL32.50]
215 BOOL WINAPI PathStripToRootAW(LPVOID lpszPath)
217 if (SHELL_OsIsUnicode())
218 return PathStripToRootW(lpszPath);
219 return PathStripToRootA(lpszPath);
222 /*************************************************************************
223 * PathRemoveArgs [SHELL32.251]
225 void WINAPI PathRemoveArgsAW(LPVOID lpszPath)
227 if (SHELL_OsIsUnicode())
228 PathRemoveArgsW(lpszPath);
229 else
230 PathRemoveArgsA(lpszPath);
233 /*************************************************************************
234 * PathRemoveExtension [SHELL32.250]
236 void WINAPI PathRemoveExtensionAW(LPVOID lpszPath)
238 if (SHELL_OsIsUnicode())
239 PathRemoveExtensionW(lpszPath);
240 else
241 PathRemoveExtensionA(lpszPath);
246 Path Manipulations
249 /*************************************************************************
250 * PathGetShortPathA [internal]
252 static void PathGetShortPathA(LPSTR pszPath)
254 CHAR path[MAX_PATH];
256 TRACE("%s\n", pszPath);
258 if (GetShortPathNameA(pszPath, path, MAX_PATH))
260 lstrcpyA(pszPath, path);
264 /*************************************************************************
265 * PathGetShortPathW [internal]
267 static void PathGetShortPathW(LPWSTR pszPath)
269 WCHAR path[MAX_PATH];
271 TRACE("%s\n", debugstr_w(pszPath));
273 if (GetShortPathNameW(pszPath, path, MAX_PATH))
275 lstrcpyW(pszPath, path);
279 /*************************************************************************
280 * PathGetShortPath [SHELL32.92]
282 VOID WINAPI PathGetShortPathAW(LPVOID pszPath)
284 if(SHELL_OsIsUnicode())
285 PathGetShortPathW(pszPath);
286 PathGetShortPathA(pszPath);
289 /*************************************************************************
290 * PathRemoveBlanks [SHELL32.33]
292 void WINAPI PathRemoveBlanksAW(LPVOID str)
294 if(SHELL_OsIsUnicode())
295 PathRemoveBlanksW(str);
296 else
297 PathRemoveBlanksA(str);
300 /*************************************************************************
301 * PathQuoteSpaces [SHELL32.55]
303 VOID WINAPI PathQuoteSpacesAW (LPVOID lpszPath)
305 if(SHELL_OsIsUnicode())
306 PathQuoteSpacesW(lpszPath);
307 else
308 PathQuoteSpacesA(lpszPath);
311 /*************************************************************************
312 * PathUnquoteSpaces [SHELL32.56]
314 VOID WINAPI PathUnquoteSpacesAW(LPVOID str)
316 if(SHELL_OsIsUnicode())
317 PathUnquoteSpacesW(str);
318 else
319 PathUnquoteSpacesA(str);
322 /*************************************************************************
323 * PathParseIconLocation [SHELL32.249]
325 int WINAPI PathParseIconLocationAW (LPVOID lpszPath)
327 if(SHELL_OsIsUnicode())
328 return PathParseIconLocationW(lpszPath);
329 return PathParseIconLocationA(lpszPath);
333 ########## Path Testing ##########
335 /*************************************************************************
336 * PathIsUNC [SHELL32.39]
338 BOOL WINAPI PathIsUNCAW (LPCVOID lpszPath)
340 if (SHELL_OsIsUnicode())
341 return PathIsUNCW( lpszPath );
342 return PathIsUNCA( lpszPath );
345 /*************************************************************************
346 * PathIsRelative [SHELL32.40]
348 BOOL WINAPI PathIsRelativeAW (LPCVOID lpszPath)
350 if (SHELL_OsIsUnicode())
351 return PathIsRelativeW( lpszPath );
352 return PathIsRelativeA( lpszPath );
355 /*************************************************************************
356 * PathIsRoot [SHELL32.29]
358 BOOL WINAPI PathIsRootAW(LPCVOID lpszPath)
360 if (SHELL_OsIsUnicode())
361 return PathIsRootW(lpszPath);
362 return PathIsRootA(lpszPath);
365 /*************************************************************************
366 * PathIsExeA [internal]
368 static BOOL PathIsExeA (LPCSTR lpszPath)
370 LPCSTR lpszExtension = PathGetExtensionA(lpszPath);
371 int i;
372 static const char * const lpszExtensions[] =
373 {"exe", "com", "pif", "cmd", "bat", "scf", "scr", NULL };
375 TRACE("path=%s\n",lpszPath);
377 for(i=0; lpszExtensions[i]; i++)
378 if (!lstrcmpiA(lpszExtension,lpszExtensions[i])) return TRUE;
380 return FALSE;
383 /*************************************************************************
384 * PathIsExeW [internal]
386 static BOOL PathIsExeW (LPCWSTR lpszPath)
388 LPCWSTR lpszExtension = PathGetExtensionW(lpszPath);
389 int i;
390 static const WCHAR lpszExtensions[][4] =
391 {L"exe", L"com", L"pif",L"cmd", L"bat", L"scf",L"scr",L"" };
393 TRACE("path=%s\n",debugstr_w(lpszPath));
395 for(i=0; lpszExtensions[i][0]; i++)
396 if (!wcsicmp(lpszExtension,lpszExtensions[i])) return TRUE;
398 return FALSE;
401 /*************************************************************************
402 * PathIsExe [SHELL32.43]
404 BOOL WINAPI PathIsExeAW (LPCVOID path)
406 if (SHELL_OsIsUnicode())
407 return PathIsExeW (path);
408 return PathIsExeA(path);
411 /*************************************************************************
412 * PathIsDirectory [SHELL32.159]
414 BOOL WINAPI PathIsDirectoryAW (LPCVOID lpszPath)
416 if (SHELL_OsIsUnicode())
417 return PathIsDirectoryW (lpszPath);
418 return PathIsDirectoryA (lpszPath);
421 /*************************************************************************
422 * PathFileExists [SHELL32.45]
424 BOOL WINAPI PathFileExistsAW (LPCVOID lpszPath)
426 if (SHELL_OsIsUnicode())
427 return PathFileExistsW (lpszPath);
428 return PathFileExistsA (lpszPath);
431 /*************************************************************************
432 * PathMatchSpec [SHELL32.46]
434 BOOL WINAPI PathMatchSpecAW(LPVOID name, LPVOID mask)
436 if (SHELL_OsIsUnicode())
437 return PathMatchSpecW( name, mask );
438 return PathMatchSpecA( name, mask );
441 /*************************************************************************
442 * PathIsSameRoot [SHELL32.650]
444 BOOL WINAPI PathIsSameRootAW(LPCVOID lpszPath1, LPCVOID lpszPath2)
446 if (SHELL_OsIsUnicode())
447 return PathIsSameRootW(lpszPath1, lpszPath2);
448 return PathIsSameRootA(lpszPath1, lpszPath2);
451 /*************************************************************************
452 * IsLFNDriveA [SHELL32.41]
454 BOOL WINAPI IsLFNDriveA(LPCSTR lpszPath)
456 DWORD fnlen;
458 if (!GetVolumeInformationA(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
459 return FALSE;
460 return fnlen > 12;
463 /*************************************************************************
464 * IsLFNDriveW [SHELL32.42]
466 BOOL WINAPI IsLFNDriveW(LPCWSTR lpszPath)
468 DWORD fnlen;
470 if (!GetVolumeInformationW(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
471 return FALSE;
472 return fnlen > 12;
475 /*************************************************************************
476 * IsLFNDrive [SHELL32.119]
478 BOOL WINAPI IsLFNDriveAW(LPCVOID lpszPath)
480 if (SHELL_OsIsUnicode())
481 return IsLFNDriveW(lpszPath);
482 return IsLFNDriveA(lpszPath);
486 ########## Creating Something Unique ##########
488 /*************************************************************************
489 * PathMakeUniqueNameA [internal]
491 static BOOL PathMakeUniqueNameA(
492 LPSTR lpszBuffer,
493 DWORD dwBuffSize,
494 LPCSTR lpszShortName,
495 LPCSTR lpszLongName,
496 LPCSTR lpszPathName)
498 FIXME("%p %lu %s %s %s stub\n",
499 lpszBuffer, dwBuffSize, debugstr_a(lpszShortName),
500 debugstr_a(lpszLongName), debugstr_a(lpszPathName));
501 return TRUE;
504 /*************************************************************************
505 * PathMakeUniqueNameW [internal]
507 static BOOL PathMakeUniqueNameW(
508 LPWSTR lpszBuffer,
509 DWORD dwBuffSize,
510 LPCWSTR lpszShortName,
511 LPCWSTR lpszLongName,
512 LPCWSTR lpszPathName)
514 FIXME("%p %lu %s %s %s stub\n",
515 lpszBuffer, dwBuffSize, debugstr_w(lpszShortName),
516 debugstr_w(lpszLongName), debugstr_w(lpszPathName));
517 return TRUE;
520 /*************************************************************************
521 * PathMakeUniqueName [SHELL32.47]
523 BOOL WINAPI PathMakeUniqueNameAW(
524 LPVOID lpszBuffer,
525 DWORD dwBuffSize,
526 LPCVOID lpszShortName,
527 LPCVOID lpszLongName,
528 LPCVOID lpszPathName)
530 if (SHELL_OsIsUnicode())
531 return PathMakeUniqueNameW(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
532 return PathMakeUniqueNameA(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
535 /*************************************************************************
536 * PathYetAnotherMakeUniqueName [SHELL32.75]
538 BOOL WINAPI PathYetAnotherMakeUniqueName(LPWSTR buffer, LPCWSTR path, LPCWSTR shortname, LPCWSTR longname)
540 WCHAR pathW[MAX_PATH], retW[MAX_PATH];
541 const WCHAR *file, *ext;
542 int i = 2;
544 TRACE("(%p, %s, %s, %s)\n", buffer, debugstr_w(path), debugstr_w(shortname), debugstr_w(longname));
546 file = longname ? longname : shortname;
547 PathCombineW(pathW, path, file);
548 lstrcpyW(retW, pathW);
549 PathRemoveExtensionW(pathW);
551 ext = PathFindExtensionW(file);
553 /* now try to make it unique */
554 while (PathFileExistsW(retW))
556 swprintf(retW, ARRAY_SIZE(retW), L"%s (%d)%s", pathW, i, ext);
557 i++;
560 lstrcpyW(buffer, retW);
561 TRACE("ret - %s\n", debugstr_w(buffer));
563 return TRUE;
567 ########## cleaning and resolving paths ##########
570 /*************************************************************************
571 * PathFindOnPath [SHELL32.145]
573 BOOL WINAPI PathFindOnPathAW(LPVOID sFile, LPCVOID *sOtherDirs)
575 if (SHELL_OsIsUnicode())
576 return PathFindOnPathW(sFile, (LPCWSTR *)sOtherDirs);
577 return PathFindOnPathA(sFile, (LPCSTR *)sOtherDirs);
580 /*************************************************************************
581 * PathCleanupSpec [SHELL32.171]
583 * lpszFile is changed in place.
585 int WINAPI PathCleanupSpec( LPCWSTR lpszPathW, LPWSTR lpszFileW )
587 int i = 0;
588 DWORD rc = 0;
589 int length = 0;
591 if (SHELL_OsIsUnicode())
593 LPWSTR p = lpszFileW;
595 TRACE("Cleanup %s\n",debugstr_w(lpszFileW));
597 if (lpszPathW)
598 length = lstrlenW(lpszPathW);
600 while (*p)
602 int gct = PathGetCharTypeW(*p);
603 if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
605 lpszFileW[i]='-';
606 rc |= PCS_REPLACEDCHAR;
608 else
609 lpszFileW[i]=*p;
610 i++;
611 p++;
612 if (length + i == MAX_PATH)
614 rc |= PCS_FATAL | PCS_PATHTOOLONG;
615 break;
618 lpszFileW[i]=0;
620 else
622 LPSTR lpszFileA = (LPSTR)lpszFileW;
623 LPCSTR lpszPathA = (LPCSTR)lpszPathW;
624 LPSTR p = lpszFileA;
626 TRACE("Cleanup %s\n",debugstr_a(lpszFileA));
628 if (lpszPathA)
629 length = strlen(lpszPathA);
631 while (*p)
633 int gct = PathGetCharTypeA(*p);
634 if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
636 lpszFileA[i]='-';
637 rc |= PCS_REPLACEDCHAR;
639 else
640 lpszFileA[i]=*p;
641 i++;
642 p++;
643 if (length + i == MAX_PATH)
645 rc |= PCS_FATAL | PCS_PATHTOOLONG;
646 break;
649 lpszFileA[i]=0;
651 return rc;
654 /*************************************************************************
655 * PathQualifyA [SHELL32]
657 static BOOL PathQualifyA(LPCSTR pszPath)
659 FIXME("%s\n",pszPath);
660 return FALSE;
663 /*************************************************************************
664 * PathQualifyW [SHELL32]
666 static BOOL PathQualifyW(LPCWSTR pszPath)
668 FIXME("%s\n",debugstr_w(pszPath));
669 return FALSE;
672 /*************************************************************************
673 * PathQualify [SHELL32.49]
675 BOOL WINAPI PathQualifyAW(LPCVOID pszPath)
677 if (SHELL_OsIsUnicode())
678 return PathQualifyW(pszPath);
679 return PathQualifyA(pszPath);
682 BOOL WINAPI PathFindOnPathExA(LPSTR,LPCSTR *,DWORD);
683 BOOL WINAPI PathFindOnPathExW(LPWSTR,LPCWSTR *,DWORD);
684 BOOL WINAPI PathFileExistsDefExtA(LPSTR,DWORD);
685 BOOL WINAPI PathFileExistsDefExtW(LPWSTR,DWORD);
687 static BOOL PathResolveA(char *path, const char **dirs, DWORD flags)
689 BOOL is_file_spec = PathIsFileSpecA(path);
690 DWORD dwWhich = flags & PRF_DONTFINDLNK ? 0xf : 0xff;
692 TRACE("(%s,%p,0x%08lx)\n", debugstr_a(path), dirs, flags);
694 if (flags & PRF_VERIFYEXISTS && !PathFileExistsA(path))
696 if (PathFindOnPathExA(path, dirs, dwWhich))
697 return TRUE;
698 if (PathFileExistsDefExtA(path, dwWhich))
699 return TRUE;
700 if (!is_file_spec) GetFullPathNameA(path, MAX_PATH, path, NULL);
701 SetLastError(ERROR_FILE_NOT_FOUND);
702 return FALSE;
705 if (is_file_spec)
707 SetLastError(ERROR_FILE_NOT_FOUND);
708 return FALSE;
711 GetFullPathNameA(path, MAX_PATH, path, NULL);
713 return TRUE;
716 static BOOL PathResolveW(WCHAR *path, const WCHAR **dirs, DWORD flags)
718 BOOL is_file_spec = PathIsFileSpecW(path);
719 DWORD dwWhich = flags & PRF_DONTFINDLNK ? 0xf : 0xff;
721 TRACE("(%s,%p,0x%08lx)\n", debugstr_w(path), dirs, flags);
723 if (flags & PRF_VERIFYEXISTS && !PathFileExistsW(path))
725 if (PathFindOnPathExW(path, dirs, dwWhich))
726 return TRUE;
727 if (PathFileExistsDefExtW(path, dwWhich))
728 return TRUE;
729 if (!is_file_spec) GetFullPathNameW(path, MAX_PATH, path, NULL);
730 SetLastError(ERROR_FILE_NOT_FOUND);
731 return FALSE;
734 if (is_file_spec)
736 SetLastError(ERROR_FILE_NOT_FOUND);
737 return FALSE;
740 GetFullPathNameW(path, MAX_PATH, path, NULL);
742 return TRUE;
745 /*************************************************************************
746 * PathResolve [SHELL32.51]
748 BOOL WINAPI PathResolveAW(void *path, const void **paths, DWORD flags)
750 if (SHELL_OsIsUnicode())
751 return PathResolveW(path, (const WCHAR **)paths, flags);
752 else
753 return PathResolveA(path, (const char **)paths, flags);
756 /*************************************************************************
757 * PathProcessCommandA
759 static LONG PathProcessCommandA (
760 LPCSTR lpszPath,
761 LPSTR lpszBuff,
762 DWORD dwBuffSize,
763 DWORD dwFlags)
765 FIXME("%s %p 0x%04lx 0x%04lx stub\n",
766 lpszPath, lpszBuff, dwBuffSize, dwFlags);
767 if(!lpszPath) return -1;
768 if(lpszBuff) strcpy(lpszBuff, lpszPath);
769 return strlen(lpszPath);
772 /*************************************************************************
773 * PathProcessCommandW
775 static LONG PathProcessCommandW (
776 LPCWSTR lpszPath,
777 LPWSTR lpszBuff,
778 DWORD dwBuffSize,
779 DWORD dwFlags)
781 FIXME("(%s, %p, 0x%04lx, 0x%04lx) stub\n",
782 debugstr_w(lpszPath), lpszBuff, dwBuffSize, dwFlags);
783 if(!lpszPath) return -1;
784 if(lpszBuff) lstrcpyW(lpszBuff, lpszPath);
785 return lstrlenW(lpszPath);
788 /*************************************************************************
789 * PathProcessCommand (SHELL32.653)
791 LONG WINAPI PathProcessCommandAW (
792 LPCVOID lpszPath,
793 LPVOID lpszBuff,
794 DWORD dwBuffSize,
795 DWORD dwFlags)
797 if (SHELL_OsIsUnicode())
798 return PathProcessCommandW(lpszPath, lpszBuff, dwBuffSize, dwFlags);
799 return PathProcessCommandA(lpszPath, lpszBuff, dwBuffSize, dwFlags);
803 ########## special ##########
806 /*************************************************************************
807 * PathSetDlgItemPath (SHELL32.48)
809 VOID WINAPI PathSetDlgItemPathAW(HWND hDlg, int id, LPCVOID pszPath)
811 if (SHELL_OsIsUnicode())
812 PathSetDlgItemPathW(hDlg, id, pszPath);
813 else
814 PathSetDlgItemPathA(hDlg, id, pszPath);
817 typedef enum _CSIDL_Type {
818 CSIDL_Type_User,
819 CSIDL_Type_AllUsers,
820 CSIDL_Type_CurrVer,
821 CSIDL_Type_Disallowed,
822 CSIDL_Type_NonExistent,
823 CSIDL_Type_WindowsPath,
824 CSIDL_Type_SystemPath,
825 CSIDL_Type_SystemX86Path,
826 CSIDL_Type_ProgramData,
827 } CSIDL_Type;
829 #define CSIDL_CONTACTS 0x0043
830 #define CSIDL_DOWNLOADS 0x0047
831 #define CSIDL_LINKS 0x004d
832 #define CSIDL_APPDATA_LOCALLOW 0x004e
833 #define CSIDL_SAVED_GAMES 0x0062
834 #define CSIDL_SEARCHES 0x0063
836 typedef struct
838 IApplicationDestinations IApplicationDestinations_iface;
839 LONG ref;
840 } IApplicationDestinationsImpl;
842 static inline IApplicationDestinationsImpl *impl_from_IApplicationDestinations( IApplicationDestinations *iface )
844 return CONTAINING_RECORD(iface, IApplicationDestinationsImpl, IApplicationDestinations_iface);
847 static HRESULT WINAPI ApplicationDestinations_QueryInterface(IApplicationDestinations *iface, REFIID riid,
848 LPVOID *ppv)
850 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
852 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppv);
854 if (ppv == NULL)
855 return E_POINTER;
857 if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IApplicationDestinations, riid))
859 *ppv = &This->IApplicationDestinations_iface;
860 IUnknown_AddRef((IUnknown*)*ppv);
862 TRACE("Returning IApplicationDestinations: %p\n", *ppv);
863 return S_OK;
866 *ppv = NULL;
867 FIXME("(%p)->(%s, %p) interface not supported.\n", This, debugstr_guid(riid), ppv);
869 return E_NOINTERFACE;
872 static ULONG WINAPI ApplicationDestinations_AddRef(IApplicationDestinations *iface)
874 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
875 ULONG ref = InterlockedIncrement(&This->ref);
877 TRACE("(%p), new refcount=%li\n", This, ref);
879 return ref;
882 static ULONG WINAPI ApplicationDestinations_Release(IApplicationDestinations *iface)
884 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
885 ULONG ref = InterlockedDecrement(&This->ref);
887 TRACE("(%p), new refcount=%li\n", This, ref);
889 if (ref == 0)
890 heap_free(This);
892 return ref;
895 static HRESULT WINAPI ApplicationDestinations_SetAppID(IApplicationDestinations *iface, const WCHAR *appid)
897 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
899 FIXME("(%p, %s) stub!\n", This, debugstr_w(appid));
901 return E_NOTIMPL;
904 static HRESULT WINAPI ApplicationDestinations_RemoveDestination(IApplicationDestinations *iface, IUnknown *punk)
906 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
908 FIXME("(%p, %p) stub!\n", This, punk);
910 return E_NOTIMPL;
913 static HRESULT WINAPI ApplicationDestinations_RemoveAllDestinations(IApplicationDestinations *iface)
915 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
917 FIXME("(%p) stub!\n", This);
919 return E_NOTIMPL;
922 static const IApplicationDestinationsVtbl ApplicationDestinationsVtbl =
924 ApplicationDestinations_QueryInterface,
925 ApplicationDestinations_AddRef,
926 ApplicationDestinations_Release,
927 ApplicationDestinations_SetAppID,
928 ApplicationDestinations_RemoveDestination,
929 ApplicationDestinations_RemoveAllDestinations
932 HRESULT WINAPI ApplicationDestinations_Constructor(IUnknown *outer, REFIID riid, LPVOID *ppv)
934 IApplicationDestinationsImpl *This;
935 HRESULT hr;
937 TRACE("(%p, %s, %p)\n", outer, debugstr_guid(riid), ppv);
939 if (outer)
940 return CLASS_E_NOAGGREGATION;
942 if (!(This = SHAlloc(sizeof(*This))))
943 return E_OUTOFMEMORY;
945 This->IApplicationDestinations_iface.lpVtbl = &ApplicationDestinationsVtbl;
946 This->ref = 0;
948 hr = IUnknown_QueryInterface(&This->IApplicationDestinations_iface, riid, ppv);
949 if (FAILED(hr))
950 SHFree(This);
952 return hr;
955 typedef struct
957 IApplicationDocumentLists IApplicationDocumentLists_iface;
958 LONG ref;
959 } IApplicationDocumentListsImpl;
961 static inline IApplicationDocumentListsImpl *impl_from_IApplicationDocumentLists( IApplicationDocumentLists *iface )
963 return CONTAINING_RECORD(iface, IApplicationDocumentListsImpl, IApplicationDocumentLists_iface);
966 static HRESULT WINAPI ApplicationDocumentLists_QueryInterface(IApplicationDocumentLists *iface,
967 REFIID riid, LPVOID *ppv)
969 IApplicationDocumentListsImpl *This = impl_from_IApplicationDocumentLists(iface);
971 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppv);
973 if (ppv == NULL)
974 return E_POINTER;
976 if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IApplicationDocumentLists, riid))
978 *ppv = &This->IApplicationDocumentLists_iface;
979 IUnknown_AddRef((IUnknown*)*ppv);
981 TRACE("Returning IApplicationDocumentLists: %p\n", *ppv);
982 return S_OK;
985 *ppv = NULL;
986 FIXME("(%p)->(%s, %p) interface not supported.\n", This, debugstr_guid(riid), ppv);
988 return E_NOINTERFACE;
991 static ULONG WINAPI ApplicationDocumentLists_AddRef(IApplicationDocumentLists *iface)
993 IApplicationDocumentListsImpl *This = impl_from_IApplicationDocumentLists(iface);
994 ULONG ref = InterlockedIncrement(&This->ref);
996 TRACE("(%p), new refcount=%li\n", This, ref);
998 return ref;
1001 static ULONG WINAPI ApplicationDocumentLists_Release(IApplicationDocumentLists *iface)
1003 IApplicationDocumentListsImpl *This = impl_from_IApplicationDocumentLists(iface);
1004 ULONG ref = InterlockedDecrement(&This->ref);
1006 TRACE("(%p), new refcount=%li\n", This, ref);
1008 if (ref == 0)
1009 heap_free(This);
1011 return ref;
1014 static HRESULT WINAPI ApplicationDocumentLists_SetAppID(IApplicationDocumentLists *iface,
1015 const WCHAR *appid)
1017 IApplicationDocumentListsImpl *This = impl_from_IApplicationDocumentLists(iface);
1019 FIXME("(%p, %s) stub!\n", This, debugstr_w(appid));
1021 return E_NOTIMPL;
1024 static HRESULT WINAPI ApplicationDocumentLists_GetList(IApplicationDocumentLists *iface,
1025 APPDOCLISTTYPE list_type, UINT item_count,
1026 REFIID riid, void **obj)
1028 IApplicationDocumentListsImpl *This = impl_from_IApplicationDocumentLists(iface);
1030 FIXME("(%p, %u, %u, %s, %p): stub\n", This, list_type, item_count, debugstr_guid(riid), obj);
1032 return E_NOTIMPL;
1035 static const IApplicationDocumentListsVtbl ApplicationDocumentListsVtbl =
1037 ApplicationDocumentLists_QueryInterface,
1038 ApplicationDocumentLists_AddRef,
1039 ApplicationDocumentLists_Release,
1040 ApplicationDocumentLists_SetAppID,
1041 ApplicationDocumentLists_GetList
1044 HRESULT WINAPI ApplicationDocumentLists_Constructor(IUnknown *outer, REFIID riid, LPVOID *ppv)
1046 IApplicationDocumentListsImpl *This;
1047 HRESULT hr;
1049 TRACE("(%p, %s, %p)\n", outer, debugstr_guid(riid), ppv);
1051 if (outer)
1052 return CLASS_E_NOAGGREGATION;
1054 if (!(This = SHAlloc(sizeof(*This))))
1055 return E_OUTOFMEMORY;
1057 This->IApplicationDocumentLists_iface.lpVtbl = &ApplicationDocumentListsVtbl;
1058 This->ref = 0;
1060 hr = IUnknown_QueryInterface(&This->IApplicationDocumentLists_iface, riid, ppv);
1061 if (FAILED(hr))
1062 SHFree(This);
1064 return hr;
1067 typedef struct
1069 const KNOWNFOLDERID *id;
1070 CSIDL_Type type;
1071 const WCHAR *value;
1072 const WCHAR *def_path; /* fallback string or resource ID */
1073 KF_CATEGORY category;
1074 const WCHAR *name;
1075 const KNOWNFOLDERID *parent;
1076 const WCHAR *path;
1077 const WCHAR *parsing;
1078 DWORD attributes;
1079 KF_DEFINITION_FLAGS flags;
1080 const FOLDERTYPEID *typeid;
1081 } CSIDL_DATA;
1083 static const CSIDL_DATA CSIDL_Data[] =
1085 { /* 0x00 - CSIDL_DESKTOP */
1086 .id = &FOLDERID_Desktop,
1087 .type = CSIDL_Type_User,
1088 .value = L"Desktop",
1089 .category = KF_CATEGORY_PERUSER,
1090 .name = L"Desktop",
1091 .path = L"Desktop",
1092 .attributes = FILE_ATTRIBUTE_READONLY,
1094 { /* 0x01 - CSIDL_INTERNET */
1095 .id = &FOLDERID_InternetFolder,
1096 .type = CSIDL_Type_Disallowed,
1097 .category = KF_CATEGORY_VIRTUAL,
1098 .name = L"InternetFolder",
1099 .parsing = L"::{871C5380-42A0-1069-A2EA-08002B30309D}",
1101 { /* 0x02 - CSIDL_PROGRAMS */
1102 .id = &FOLDERID_Programs,
1103 .type = CSIDL_Type_User,
1104 .value = L"Programs",
1105 .category = KF_CATEGORY_PERUSER,
1106 .name = L"Programs",
1107 .parent = &FOLDERID_StartMenu,
1108 .path = L"Programs",
1109 .attributes = FILE_ATTRIBUTE_READONLY,
1111 { /* 0x03 - CSIDL_CONTROLS (.CPL files) */
1112 .id = &FOLDERID_ControlPanelFolder,
1113 .type = CSIDL_Type_SystemPath,
1114 .category = KF_CATEGORY_VIRTUAL,
1115 .name = L"ControlPanelFolder",
1116 .path = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}",
1117 .parsing = L"::{26EE0668-A00A-44D7-9371-BEB064C98683}\\0",
1119 { /* 0x04 - CSIDL_PRINTERS */
1120 .id = &FOLDERID_PrintersFolder,
1121 .type = CSIDL_Type_SystemPath,
1122 .category = KF_CATEGORY_VIRTUAL,
1123 .name = L"PrintersFolder",
1124 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}",
1126 { /* 0x05 - CSIDL_PERSONAL */
1127 .id = &FOLDERID_Documents,
1128 .type = CSIDL_Type_User,
1129 .value = L"Personal",
1130 .category = KF_CATEGORY_PERUSER,
1131 .name = L"Personal",
1132 .parent = &FOLDERID_Profile,
1133 .path = L"Documents",
1134 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{FDD39AD0-238F-46AF-ADB4-6C85480369C7}",
1135 .attributes = FILE_ATTRIBUTE_READONLY,
1136 .flags = KFDF_ROAMABLE | KFDF_PRECREATE,
1138 { /* 0x06 - CSIDL_FAVORITES */
1139 .id = &FOLDERID_Favorites,
1140 .type = CSIDL_Type_User,
1141 .value = L"Favorites",
1142 .category = KF_CATEGORY_PERUSER,
1143 .name = L"Favorites",
1144 .path = L"Favorites",
1145 .attributes = FILE_ATTRIBUTE_READONLY,
1146 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1148 { /* 0x07 - CSIDL_STARTUP */
1149 .id = &FOLDERID_Startup,
1150 .type = CSIDL_Type_User,
1151 .value = L"StartUp",
1152 .category = KF_CATEGORY_PERUSER,
1153 .name = L"Startup",
1154 .parent = &FOLDERID_Programs,
1155 .path = L"StartUp",
1156 .attributes = FILE_ATTRIBUTE_READONLY,
1157 .flags = KFDF_PRECREATE,
1159 { /* 0x08 - CSIDL_RECENT */
1160 .id = &FOLDERID_Recent,
1161 .type = CSIDL_Type_User,
1162 .value = L"Recent",
1163 .category = KF_CATEGORY_PERUSER,
1164 .name = L"Recent",
1165 .parent = &FOLDERID_RoamingAppData,
1166 .path = L"Microsoft\\Windows\\Recent",
1167 .attributes = FILE_ATTRIBUTE_READONLY,
1168 .flags = KFDF_PRECREATE,
1170 { /* 0x09 - CSIDL_SENDTO */
1171 .id = &FOLDERID_SendTo,
1172 .type = CSIDL_Type_User,
1173 .value = L"SendTo",
1174 .category = KF_CATEGORY_PERUSER,
1175 .name = L"SendTo",
1176 .parent = &FOLDERID_RoamingAppData,
1177 .path = L"Microsoft\\Windows\\SendTo",
1178 .flags = KFDF_PRECREATE,
1180 { /* 0x0a - CSIDL_BITBUCKET - Recycle Bin */
1181 .id = &FOLDERID_RecycleBinFolder,
1182 .type = CSIDL_Type_Disallowed,
1183 .category = KF_CATEGORY_VIRTUAL,
1184 .name = L"RecycleBinFolder",
1185 .parsing = L"::{645FF040-5081-101B-9F08-00AA002F954E}",
1187 { /* 0x0b - CSIDL_STARTMENU */
1188 .id = &FOLDERID_StartMenu,
1189 .type = CSIDL_Type_User,
1190 .value = L"Start Menu",
1191 .category = KF_CATEGORY_PERUSER,
1192 .name = L"Start Menu",
1193 .parent = &FOLDERID_RoamingAppData,
1194 .path = L"Microsoft\\Windows\\Start Menu",
1195 .attributes = FILE_ATTRIBUTE_READONLY,
1196 .flags = KFDF_PRECREATE,
1198 { /* 0x0c - CSIDL_MYDOCUMENTS */
1199 .id = &GUID_NULL,
1200 .type = CSIDL_Type_Disallowed, /* matches WinXP--can't get its path */
1202 { /* 0x0d - CSIDL_MYMUSIC */
1203 .id = &FOLDERID_Music,
1204 .type = CSIDL_Type_User,
1205 .value = L"My Music",
1206 .category = KF_CATEGORY_PERUSER,
1207 .name = L"My Music",
1208 .parent = &FOLDERID_Profile,
1209 .path = L"Music",
1210 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{4BD8D571-6D19-48D3-BE97-422220080E43}",
1211 .attributes = FILE_ATTRIBUTE_READONLY,
1212 .flags = KFDF_ROAMABLE | KFDF_PRECREATE,
1214 { /* 0x0e - CSIDL_MYVIDEO */
1215 .id = &FOLDERID_Videos,
1216 .type = CSIDL_Type_User,
1217 .value = L"My Videos",
1218 .category = KF_CATEGORY_PERUSER,
1219 .name = L"My Video",
1220 .parent = &FOLDERID_Profile,
1221 .path = L"Videos",
1222 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{18989B1D-99B5-455B-841C-AB7C74E4DDFC}",
1223 .attributes = FILE_ATTRIBUTE_READONLY,
1224 .flags = KFDF_ROAMABLE | KFDF_PRECREATE,
1226 { /* 0x0f - unassigned */
1227 .id = &GUID_NULL,
1228 .type = CSIDL_Type_Disallowed,
1230 { /* 0x10 - CSIDL_DESKTOPDIRECTORY */
1231 .id = &FOLDERID_Desktop,
1232 .type = CSIDL_Type_User,
1233 .value = L"Desktop",
1234 .category = KF_CATEGORY_PERUSER,
1235 .name = L"Desktop",
1236 .parent = &FOLDERID_Profile,
1237 .path = L"Desktop",
1238 .attributes = FILE_ATTRIBUTE_READONLY,
1239 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1241 { /* 0x11 - CSIDL_DRIVES */
1242 .id = &FOLDERID_ComputerFolder,
1243 .type = CSIDL_Type_Disallowed,
1244 .category = KF_CATEGORY_VIRTUAL,
1245 .name = L"MyComputerFolder",
1246 .parsing = L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
1248 { /* 0x12 - CSIDL_NETWORK */
1249 .id = &FOLDERID_NetworkFolder,
1250 .type = CSIDL_Type_Disallowed,
1251 .category = KF_CATEGORY_VIRTUAL,
1252 .name = L"NetworkPlacesFolder",
1253 .parsing = L"::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}",
1255 { /* 0x13 - CSIDL_NETHOOD */
1256 .id = &FOLDERID_NetHood,
1257 .type = CSIDL_Type_User,
1258 .value = L"NetHood",
1259 .category = KF_CATEGORY_PERUSER,
1260 .name = L"NetHood",
1261 .parent = &FOLDERID_RoamingAppData,
1262 .path = L"Microsoft\\Windows\\Network Shortcuts",
1264 { /* 0x14 - CSIDL_FONTS */
1265 .id = &FOLDERID_Fonts,
1266 .type = CSIDL_Type_WindowsPath,
1267 .value = L"Fonts",
1268 .def_path = L"Fonts",
1269 .category = KF_CATEGORY_FIXED,
1270 .name = L"Fonts",
1271 .parent = &FOLDERID_Windows,
1272 .typeid = &FOLDERID_Windows
1274 { /* 0x15 - CSIDL_TEMPLATES */
1275 .id = &FOLDERID_Templates,
1276 .type = CSIDL_Type_User,
1277 .value = L"Templates",
1278 .category = KF_CATEGORY_PERUSER,
1279 .name = L"Templates",
1280 .parent = &FOLDERID_RoamingAppData,
1281 .path = L"Microsoft\\Windows\\Templates",
1283 { /* 0x16 - CSIDL_COMMON_STARTMENU */
1284 .id = &FOLDERID_CommonStartMenu,
1285 .type = CSIDL_Type_ProgramData,
1286 .value = L"Common Start Menu",
1287 .category = KF_CATEGORY_COMMON,
1288 .name = L"Common Start Menu",
1289 .parent = &FOLDERID_ProgramData,
1290 .path = L"Microsoft\\Windows\\Start Menu",
1291 .attributes = FILE_ATTRIBUTE_READONLY,
1293 { /* 0x17 - CSIDL_COMMON_PROGRAMS */
1294 .id = &FOLDERID_CommonPrograms,
1295 .type = CSIDL_Type_ProgramData,
1296 .value = L"Common Programs",
1297 .category = KF_CATEGORY_COMMON,
1298 .name = L"Common Programs",
1299 .parent = &FOLDERID_CommonStartMenu,
1300 .path = L"Programs",
1301 .attributes = FILE_ATTRIBUTE_READONLY,
1303 { /* 0x18 - CSIDL_COMMON_STARTUP */
1304 .id = &FOLDERID_CommonStartup,
1305 .type = CSIDL_Type_ProgramData,
1306 .value = L"Common StartUp",
1307 .category = KF_CATEGORY_COMMON,
1308 .name = L"Common Startup",
1309 .parent = &FOLDERID_CommonPrograms,
1310 .path = L"StartUp",
1311 .attributes = FILE_ATTRIBUTE_READONLY,
1312 .flags = KFDF_PRECREATE,
1314 { /* 0x19 - CSIDL_COMMON_DESKTOPDIRECTORY */
1315 .id = &FOLDERID_PublicDesktop,
1316 .type = CSIDL_Type_AllUsers,
1317 .value = L"Common Desktop",
1318 .category = KF_CATEGORY_COMMON,
1319 .name = L"Common Desktop",
1320 .parent = &FOLDERID_Public,
1321 .path = L"Desktop",
1322 .attributes = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN,
1323 .flags = KFDF_PRECREATE,
1325 { /* 0x1a - CSIDL_APPDATA */
1326 .id = &FOLDERID_RoamingAppData,
1327 .type = CSIDL_Type_User,
1328 .value = L"AppData",
1329 .category = KF_CATEGORY_PERUSER,
1330 .name = L"AppData",
1331 .parent = &FOLDERID_Profile,
1332 .path = L"AppData\\Roaming",
1334 { /* 0x1b - CSIDL_PRINTHOOD */
1335 .id = &FOLDERID_PrintHood,
1336 .type = CSIDL_Type_User,
1337 .value = L"PrintHood",
1338 .category = KF_CATEGORY_PERUSER,
1339 .name = L"PrintHood",
1340 .parent = &FOLDERID_RoamingAppData,
1341 .path = L"Microsoft\\Windows\\Printer Shortcuts",
1343 { /* 0x1c - CSIDL_LOCAL_APPDATA */
1344 .id = &FOLDERID_LocalAppData,
1345 .type = CSIDL_Type_User,
1346 .value = L"Local AppData",
1347 .category = KF_CATEGORY_PERUSER,
1348 .name = L"Local AppData",
1349 .parent = &FOLDERID_Profile,
1350 .path = L"AppData\\Local",
1351 .flags = KFDF_LOCAL_REDIRECT_ONLY | KFDF_PUBLISHEXPANDEDPATH,
1353 { /* 0x1d - CSIDL_ALTSTARTUP */
1354 .id = &GUID_NULL,
1355 .type = CSIDL_Type_NonExistent,
1357 { /* 0x1e - CSIDL_COMMON_ALTSTARTUP */
1358 .id = &GUID_NULL,
1359 .type = CSIDL_Type_NonExistent,
1361 { /* 0x1f - CSIDL_COMMON_FAVORITES */
1362 .id = &FOLDERID_Favorites,
1363 .type = CSIDL_Type_AllUsers,
1364 .value = L"Common Favorites",
1365 .category = KF_CATEGORY_PERUSER,
1366 .name = L"Favorites",
1367 .parent = &FOLDERID_Profile,
1368 .path = L"Favorites",
1369 .attributes = FILE_ATTRIBUTE_READONLY,
1370 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1372 { /* 0x20 - CSIDL_INTERNET_CACHE */
1373 .id = &FOLDERID_InternetCache,
1374 .type = CSIDL_Type_User,
1375 .value = L"Cache",
1376 .category = KF_CATEGORY_PERUSER,
1377 .name = L"Cache",
1378 .parent = &FOLDERID_LocalAppData,
1379 .path = L"Microsoft\\Windows\\INetCache",
1380 .flags = KFDF_LOCAL_REDIRECT_ONLY,
1382 { /* 0x21 - CSIDL_COOKIES */
1383 .id = &FOLDERID_Cookies,
1384 .type = CSIDL_Type_User,
1385 .value = L"Cookies",
1386 .category = KF_CATEGORY_PERUSER,
1387 .name = L"Cookies",
1388 .parent = &FOLDERID_LocalAppData,
1389 .path = L"Microsoft\\Windows\\INetCookies",
1391 { /* 0x22 - CSIDL_HISTORY */
1392 .id = &FOLDERID_History,
1393 .type = CSIDL_Type_User,
1394 .value = L"History",
1395 .category = KF_CATEGORY_PERUSER,
1396 .name = L"History",
1397 .parent = &FOLDERID_LocalAppData,
1398 .path = L"Microsoft\\Windows\\History",
1399 .flags = KFDF_LOCAL_REDIRECT_ONLY,
1401 { /* 0x23 - CSIDL_COMMON_APPDATA */
1402 .id = &FOLDERID_ProgramData,
1403 .type = CSIDL_Type_ProgramData,
1404 .value = L"Common AppData",
1405 .category = KF_CATEGORY_FIXED,
1406 .name = L"Common AppData",
1408 { /* 0x24 - CSIDL_WINDOWS */
1409 .id = &FOLDERID_Windows,
1410 .type = CSIDL_Type_WindowsPath,
1411 .category = KF_CATEGORY_FIXED,
1412 .name = L"Windows",
1414 { /* 0x25 - CSIDL_SYSTEM */
1415 .id = &FOLDERID_System,
1416 .type = CSIDL_Type_SystemPath,
1417 .category = KF_CATEGORY_FIXED,
1418 .name = L"System",
1420 { /* 0x26 - CSIDL_PROGRAM_FILES */
1421 .id = &FOLDERID_ProgramFiles,
1422 .type = CSIDL_Type_CurrVer,
1423 .value = L"ProgramFilesDir",
1424 .def_path = L"Program Files",
1425 .category = KF_CATEGORY_FIXED,
1426 .name = L"ProgramFiles",
1427 .attributes = FILE_ATTRIBUTE_READONLY,
1429 { /* 0x27 - CSIDL_MYPICTURES */
1430 .id = &FOLDERID_Pictures,
1431 .type = CSIDL_Type_User,
1432 .value = L"My Pictures",
1433 .category = KF_CATEGORY_PERUSER,
1434 .name = L"My Pictures",
1435 .parent = &FOLDERID_Profile,
1436 .path = L"Pictures",
1437 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{33E28130-4E1E-4676-835A-98395C3BC3BB}",
1438 .attributes = FILE_ATTRIBUTE_READONLY,
1439 .flags = KFDF_ROAMABLE | KFDF_PRECREATE,
1441 { /* 0x28 - CSIDL_PROFILE */
1442 .id = &FOLDERID_Profile,
1443 .type = CSIDL_Type_User,
1444 .category = KF_CATEGORY_FIXED,
1445 .name = L"Profile",
1447 { /* 0x29 - CSIDL_SYSTEMX86 */
1448 .id = &FOLDERID_SystemX86,
1449 .type = CSIDL_Type_SystemX86Path,
1450 .category = KF_CATEGORY_FIXED,
1451 .name = L"SystemX86",
1453 { /* 0x2a - CSIDL_PROGRAM_FILESX86 */
1454 .id = &FOLDERID_ProgramFilesX86,
1455 .type = CSIDL_Type_CurrVer,
1456 .value = L"ProgramFilesDir (x86)",
1457 .def_path = L"Program Files (x86)",
1458 .category = KF_CATEGORY_FIXED,
1459 .name = L"ProgramFilesX86",
1460 .attributes = FILE_ATTRIBUTE_READONLY,
1462 { /* 0x2b - CSIDL_PROGRAM_FILES_COMMON */
1463 .id = &FOLDERID_ProgramFilesCommon,
1464 .type = CSIDL_Type_CurrVer,
1465 .value = L"CommonFilesDir",
1466 .def_path = L"Program Files\\Common Files",
1467 .category = KF_CATEGORY_FIXED,
1468 .name = L"ProgramFilesCommon",
1470 { /* 0x2c - CSIDL_PROGRAM_FILES_COMMONX86 */
1471 .id = &FOLDERID_ProgramFilesCommonX86,
1472 .type = CSIDL_Type_CurrVer,
1473 .value = L"CommonFilesDir (x86)",
1474 .def_path = L"Program Files (x86)\\Common Files",
1475 .category = KF_CATEGORY_FIXED,
1476 .name = L"ProgramFilesCommonX86",
1478 { /* 0x2d - CSIDL_COMMON_TEMPLATES */
1479 .id = &FOLDERID_CommonTemplates,
1480 .type = CSIDL_Type_ProgramData,
1481 .value = L"Common Templates",
1482 .category = KF_CATEGORY_COMMON,
1483 .name = L"Common Templates",
1484 .parent = &FOLDERID_ProgramData,
1485 .path = L"Microsoft\\Windows\\Templates",
1487 { /* 0x2e - CSIDL_COMMON_DOCUMENTS */
1488 .id = &FOLDERID_PublicDocuments,
1489 .type = CSIDL_Type_AllUsers,
1490 .value = L"Common Documents",
1491 .category = KF_CATEGORY_COMMON,
1492 .name = L"Common Documents",
1493 .parent = &FOLDERID_Public,
1494 .path = L"Documents",
1495 .attributes = FILE_ATTRIBUTE_READONLY,
1496 .flags = KFDF_PRECREATE,
1498 { /* 0x2f - CSIDL_COMMON_ADMINTOOLS */
1499 .id = &FOLDERID_CommonAdminTools,
1500 .type = CSIDL_Type_ProgramData,
1501 .value = L"Common Administrative Tools",
1502 .category = KF_CATEGORY_COMMON,
1503 .name = L"Common Administrative Tools",
1504 .parent = &FOLDERID_CommonPrograms,
1505 .path = L"Administrative Tools",
1506 .attributes = FILE_ATTRIBUTE_READONLY,
1507 .flags = KFDF_PRECREATE,
1509 { /* 0x30 - CSIDL_ADMINTOOLS */
1510 .id = &FOLDERID_AdminTools,
1511 .type = CSIDL_Type_User,
1512 .value = L"Administrative Tools",
1513 .category = KF_CATEGORY_PERUSER,
1514 .name = L"Administrative Tools",
1515 .parent = &FOLDERID_Programs,
1516 .path = L"Administrative Tools",
1517 .attributes = FILE_ATTRIBUTE_READONLY,
1518 .flags = KFDF_PRECREATE,
1520 { /* 0x31 - CSIDL_CONNECTIONS */
1521 .id = &FOLDERID_ConnectionsFolder,
1522 .type = CSIDL_Type_Disallowed,
1523 .category = KF_CATEGORY_VIRTUAL,
1524 .name = L"ConnectionsFolder",
1525 .path = L"Administrative Tools",
1526 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}",
1528 { /* 0x32 - unassigned */
1529 .id = &GUID_NULL,
1530 .type = CSIDL_Type_Disallowed,
1532 { /* 0x33 - unassigned */
1533 .id = &GUID_NULL,
1534 .type = CSIDL_Type_Disallowed,
1536 { /* 0x34 - unassigned */
1537 .id = &GUID_NULL,
1538 .type = CSIDL_Type_Disallowed,
1540 { /* 0x35 - CSIDL_COMMON_MUSIC */
1541 .id = &FOLDERID_PublicMusic,
1542 .type = CSIDL_Type_AllUsers,
1543 .value = L"CommonMusic",
1544 .category = KF_CATEGORY_COMMON,
1545 .name = L"CommonMusic",
1546 .parent = &FOLDERID_Public,
1547 .path = L"Music",
1548 .attributes = FILE_ATTRIBUTE_READONLY,
1549 .flags = KFDF_PRECREATE,
1551 { /* 0x36 - CSIDL_COMMON_PICTURES */
1552 .id = &FOLDERID_PublicPictures,
1553 .type = CSIDL_Type_AllUsers,
1554 .value = L"CommonPictures",
1555 .category = KF_CATEGORY_COMMON,
1556 .name = L"CommonPictures",
1557 .parent = &FOLDERID_Public,
1558 .path = L"Pictures",
1559 .attributes = FILE_ATTRIBUTE_READONLY,
1560 .flags = KFDF_PRECREATE,
1562 { /* 0x37 - CSIDL_COMMON_VIDEO */
1563 .id = &FOLDERID_PublicVideos,
1564 .type = CSIDL_Type_AllUsers,
1565 .value = L"CommonVideo",
1566 .category = KF_CATEGORY_COMMON,
1567 .name = L"CommonVideo",
1568 .parent = &FOLDERID_Public,
1569 .path = L"Videos",
1570 .attributes = FILE_ATTRIBUTE_READONLY,
1571 .flags = KFDF_PRECREATE,
1573 { /* 0x38 - CSIDL_RESOURCES */
1574 .id = &FOLDERID_ResourceDir,
1575 .type = CSIDL_Type_WindowsPath,
1576 .def_path = L"Resources",
1577 .category = KF_CATEGORY_FIXED,
1578 .name = L"ResourceDir",
1580 { /* 0x39 - CSIDL_RESOURCES_LOCALIZED */
1581 .id = &FOLDERID_LocalizedResourcesDir,
1582 .type = CSIDL_Type_NonExistent,
1583 .category = KF_CATEGORY_FIXED,
1584 .name = L"LocalizedResourcesDir",
1586 { /* 0x3a - CSIDL_COMMON_OEM_LINKS */
1587 .id = &FOLDERID_CommonOEMLinks,
1588 .type = CSIDL_Type_ProgramData,
1589 .category = KF_CATEGORY_COMMON,
1590 .name = L"OEM Links",
1591 .parent = &FOLDERID_ProgramData,
1592 .path = L"OEM Links",
1594 { /* 0x3b - CSIDL_CDBURN_AREA */
1595 .id = &FOLDERID_CDBurning,
1596 .type = CSIDL_Type_User,
1597 .value = L"CD Burning",
1598 .category = KF_CATEGORY_PERUSER,
1599 .name = L"CD Burning",
1600 .parent = &FOLDERID_LocalAppData,
1601 .path = L"Microsoft\\Windows\\Burn\\Burn",
1602 .attributes = FILE_ATTRIBUTE_READONLY,
1603 .flags = KFDF_LOCAL_REDIRECT_ONLY,
1605 { /* 0x3c unassigned */
1606 .id = &GUID_NULL,
1607 .type = CSIDL_Type_Disallowed,
1609 { /* 0x3d - CSIDL_COMPUTERSNEARME */
1610 .id = &GUID_NULL,
1611 .type = CSIDL_Type_Disallowed, /* FIXME */
1613 { /* 0x3e - CSIDL_PROFILES */
1614 .id = &GUID_NULL,
1615 .type = CSIDL_Type_Disallowed, /* oddly, this matches WinXP */
1617 { /* 0x3f */
1618 .id = &FOLDERID_AddNewPrograms,
1619 .type = CSIDL_Type_Disallowed,
1620 .category = KF_CATEGORY_VIRTUAL,
1621 .name = L"AddNewProgramsFolder",
1622 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{15eae92e-f17a-4431-9f28-805e482dafd4}",
1624 { /* 0x40 */
1625 .id = &FOLDERID_AppUpdates,
1626 .type = CSIDL_Type_Disallowed,
1627 .category = KF_CATEGORY_VIRTUAL,
1628 .name = L"AppUpdatesFolder",
1629 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7b81be6a-ce2b-4676-a29e-eb907a5126c5}\\::{d450a8a1-9568-45c7-9c0e-b4f9fb4537bd}",
1631 { /* 0x41 */
1632 .id = &FOLDERID_ChangeRemovePrograms,
1633 .type = CSIDL_Type_Disallowed,
1634 .category = KF_CATEGORY_VIRTUAL,
1635 .name = L"ChangeRemoveProgramsFolder",
1636 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7b81be6a-ce2b-4676-a29e-eb907a5126c5}",
1638 { /* 0x42 */
1639 .id = &FOLDERID_ConflictFolder,
1640 .type = CSIDL_Type_Disallowed,
1641 .category = KF_CATEGORY_VIRTUAL,
1642 .name = L"ConflictFolder",
1643 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}\\::{E413D040-6788-4C22-957E-175D1C513A34},",
1645 { /* 0x43 - CSIDL_CONTACTS */
1646 .id = &FOLDERID_Contacts,
1647 .type = CSIDL_Type_User,
1648 .category = KF_CATEGORY_PERUSER,
1649 .name = L"Contacts",
1650 .parent = &FOLDERID_Profile,
1651 .path = L"Contacts",
1652 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{56784854-C6CB-462B-8169-88E350ACB882}",
1653 .attributes = FILE_ATTRIBUTE_READONLY,
1654 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1656 { /* 0x44 */
1657 .id = &FOLDERID_DeviceMetadataStore,
1658 .type = CSIDL_Type_Disallowed, /* FIXME */
1659 .category = KF_CATEGORY_COMMON,
1660 .name = L"Device Metadata Store",
1661 .parent = &FOLDERID_ProgramData,
1662 .path = L"Microsoft\\Windows\\DeviceMetadataStore",
1664 { /* 0x45 */
1665 .id = &GUID_NULL,
1666 .type = CSIDL_Type_Disallowed,
1668 { /* 0x46 */
1669 .id = &FOLDERID_DocumentsLibrary,
1670 .type = CSIDL_Type_Disallowed, /* FIXME */
1671 .category = KF_CATEGORY_PERUSER,
1672 .name = L"DocumentsLibrary",
1673 .parent = &FOLDERID_Libraries,
1674 .path = L"Documents.library-ms",
1675 .parsing = L"::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\\{7b0db17d-9cd2-4a93-9733-46cc89022e7c}",
1676 .flags = KFDF_PRECREATE | KFDF_STREAM,
1678 { /* 0x47 - CSIDL_DOWNLOADS */
1679 .id = &FOLDERID_Downloads,
1680 .type = CSIDL_Type_User,
1681 .category = KF_CATEGORY_PERUSER,
1682 .name = L"Downloads",
1683 .parent = &FOLDERID_Profile,
1684 .path = L"Downloads",
1685 .attributes = FILE_ATTRIBUTE_READONLY,
1686 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1688 { /* 0x48 */
1689 .id = &FOLDERID_Games,
1690 .type = CSIDL_Type_Disallowed,
1691 .category = KF_CATEGORY_VIRTUAL,
1692 .name = L"Games",
1693 .parsing = L"::{ED228FDF-9EA8-4870-83b1-96b02CFE0D52}",
1695 { /* 0x49 */
1696 .id = &FOLDERID_GameTasks,
1697 .type = CSIDL_Type_Disallowed, /* FIXME */
1698 .category = KF_CATEGORY_PERUSER,
1699 .name = L"GameTasks",
1700 .parent = &FOLDERID_LocalAppData,
1701 .path = L"Microsoft\\Windows\\GameExplorer",
1702 .flags = KFDF_LOCAL_REDIRECT_ONLY,
1704 { /* 0x4a */
1705 .id = &FOLDERID_HomeGroup,
1706 .type = CSIDL_Type_Disallowed,
1707 .category = KF_CATEGORY_VIRTUAL,
1708 .name = L"HomeGroupFolder",
1709 .parsing = L"::{B4FB3F98-C1EA-428d-A78A-D1F5659CBA93}",
1711 { /* 0x4b */
1712 .id = &FOLDERID_ImplicitAppShortcuts,
1713 .type = CSIDL_Type_Disallowed, /* FIXME */
1714 .category = KF_CATEGORY_PERUSER,
1715 .name = L"ImplicitAppShortcuts",
1716 .parent = &FOLDERID_UserPinned,
1717 .path = L"ImplicitAppShortcuts",
1718 .flags = KFDF_PRECREATE,
1720 { /* 0x4c */
1721 .id = &FOLDERID_Libraries,
1722 .type = CSIDL_Type_Disallowed, /* FIXME */
1723 .category = KF_CATEGORY_PERUSER,
1724 .name = L"Libraries",
1725 .parent = &FOLDERID_RoamingAppData,
1726 .path = L"Microsoft\\Windows\\Libraries",
1727 .flags = KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1729 { /* 0x4d - CSIDL_LINKS */
1730 .id = &FOLDERID_Links,
1731 .type = CSIDL_Type_User,
1732 .category = KF_CATEGORY_PERUSER,
1733 .name = L"Links",
1734 .parent = &FOLDERID_Profile,
1735 .path = L"Links",
1736 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{bfb9d5e0-c6a9-404c-b2b2-ae6db6af4968}",
1737 .attributes = FILE_ATTRIBUTE_READONLY,
1738 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1740 { /* 0x4e - CSIDL_APPDATA_LOCALLOW */
1741 .id = &FOLDERID_LocalAppDataLow,
1742 .type = CSIDL_Type_User,
1743 .category = KF_CATEGORY_PERUSER,
1744 .name = L"LocalAppDataLow",
1745 .parent = &FOLDERID_Profile,
1746 .path = L"AppData\\LocalLow",
1747 .attributes = FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
1748 .flags = KFDF_LOCAL_REDIRECT_ONLY | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1750 { /* 0x4f */
1751 .id = &FOLDERID_MusicLibrary,
1752 .type = CSIDL_Type_Disallowed, /* FIXME */
1753 .category = KF_CATEGORY_PERUSER,
1754 .name = L"MusicLibrary",
1755 .parent = &FOLDERID_Libraries,
1756 .path = L"Music.library-ms",
1757 .parsing = L"::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\\{2112AB0A-C86A-4ffe-A368-0DE96E47012E}",
1758 .flags = KFDF_PRECREATE | KFDF_STREAM,
1760 { /* 0x50 */
1761 .id = &FOLDERID_OriginalImages,
1762 .type = CSIDL_Type_Disallowed, /* FIXME */
1763 .category = KF_CATEGORY_PERUSER,
1764 .name = L"Original Images",
1765 .parent = &FOLDERID_LocalAppData,
1766 .path = L"Microsoft\\Windows Photo Gallery\\Original Images",
1768 { /* 0x51 */
1769 .id = &FOLDERID_PhotoAlbums,
1770 .type = CSIDL_Type_User,
1771 .category = KF_CATEGORY_PERUSER,
1772 .name = L"PhotoAlbums",
1773 .parent = &FOLDERID_Pictures,
1774 .path = L"Slide Shows",
1775 .attributes = FILE_ATTRIBUTE_READONLY,
1777 { /* 0x52 */
1778 .id = &FOLDERID_PicturesLibrary,
1779 .type = CSIDL_Type_Disallowed, /* FIXME */
1780 .category = KF_CATEGORY_PERUSER,
1781 .name = L"PicturesLibrary",
1782 .parent = &FOLDERID_Libraries,
1783 .path = L"Pictures.library-ms",
1784 .parsing = L"::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\\{A990AE9F-A03B-4e80-94BC-9912D7504104}",
1785 .flags = KFDF_PRECREATE | KFDF_STREAM,
1787 { /* 0x53 */
1788 .id = &FOLDERID_Playlists,
1789 .type = CSIDL_Type_User,
1790 .category = KF_CATEGORY_PERUSER,
1791 .name = L"Playlists",
1792 .parent = &FOLDERID_Music,
1793 .path = L"Playlists",
1794 .attributes = FILE_ATTRIBUTE_READONLY,
1796 { /* 0x54 */
1797 .id = &FOLDERID_ProgramFilesX64,
1798 #ifdef _WIN64
1799 .type = CSIDL_Type_CurrVer,
1800 .value = L"ProgramFilesDir",
1801 .def_path = L"Program Files",
1802 #else
1803 .type = CSIDL_Type_NonExistent,
1804 #endif
1805 .category = KF_CATEGORY_FIXED,
1806 .name = L"ProgramFilesX64",
1808 { /* 0x55 */
1809 .id = &FOLDERID_ProgramFilesCommonX64,
1810 #ifdef _WIN64
1811 .type = CSIDL_Type_CurrVer,
1812 .value = L"ProgramFilesCommonX64",
1813 .def_path = L"Program Files\\Common Files",
1814 #else
1815 .type = CSIDL_Type_NonExistent,
1816 #endif
1817 .category = KF_CATEGORY_FIXED,
1818 .name = L"ProgramFilesCommonX64",
1820 { /* 0x56 */
1821 .id = &FOLDERID_Public,
1822 .type = CSIDL_Type_AllUsers,
1823 .category = KF_CATEGORY_FIXED,
1824 .name = L"Public",
1825 .parsing = L"::{4336a54d-038b-4685-ab02-99bb52d3fb8b}",
1826 .attributes = FILE_ATTRIBUTE_READONLY,
1827 .flags = KFDF_PRECREATE,
1829 { /* 0x57 */
1830 .id = &FOLDERID_PublicDownloads,
1831 .type = CSIDL_Type_AllUsers,
1832 .category = KF_CATEGORY_COMMON,
1833 .name = L"CommonDownloads",
1834 .parent = &FOLDERID_Public,
1835 .path = L"Downloads",
1836 .attributes = FILE_ATTRIBUTE_READONLY,
1837 .flags = KFDF_PRECREATE,
1839 { /* 0x58 */
1840 .id = &FOLDERID_PublicGameTasks,
1841 .type = CSIDL_Type_ProgramData,
1842 .category = KF_CATEGORY_COMMON,
1843 .name = L"PublicGameTasks",
1844 .parent = &FOLDERID_ProgramData,
1845 .path = L"Microsoft\\Windows\\GameExplorer",
1846 .flags = KFDF_LOCAL_REDIRECT_ONLY,
1848 { /* 0x59 */
1849 .id = &FOLDERID_PublicLibraries,
1850 .type = CSIDL_Type_AllUsers,
1851 .category = KF_CATEGORY_COMMON,
1852 .name = L"PublicLibraries",
1853 .parent = &FOLDERID_Public,
1854 .path = L"Libraries",
1855 .attributes = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN,
1856 .flags = KFDF_PRECREATE,
1858 { /* 0x5a */
1859 .id = &FOLDERID_PublicRingtones,
1860 .type = CSIDL_Type_ProgramData,
1861 .category = KF_CATEGORY_COMMON,
1862 .name = L"CommonRingtones",
1863 .parent = &FOLDERID_ProgramData,
1864 .path = L"Microsoft\\Windows\\Ringtones",
1865 .flags = KFDF_PRECREATE,
1867 { /* 0x5b */
1868 .id = &FOLDERID_QuickLaunch,
1869 .type = CSIDL_Type_Disallowed, /* FIXME */
1870 .category = KF_CATEGORY_PERUSER,
1871 .name = L"Quick Launch",
1872 .parent = &FOLDERID_RoamingAppData,
1873 .path = L"Microsoft\\Internet Explorer\\Quick Launch",
1875 { /* 0x5c */
1876 .id = &FOLDERID_RecordedTVLibrary,
1877 .type = CSIDL_Type_Disallowed, /* FIXME */
1878 .category = KF_CATEGORY_COMMON,
1879 .name = L"RecordedTVLibrary",
1880 .parent = &FOLDERID_PublicLibraries,
1881 .path = L"RecordedTV.library-ms",
1882 .flags = KFDF_PRECREATE | KFDF_STREAM,
1884 { /* 0x5d */
1885 .id = &FOLDERID_Ringtones,
1886 .type = CSIDL_Type_Disallowed, /* FIXME */
1887 .category = KF_CATEGORY_PERUSER,
1888 .name = L"Ringtones",
1889 .parent = &FOLDERID_LocalAppData,
1890 .path = L"Microsoft\\Windows\\Ringtones",
1891 .flags = KFDF_PRECREATE,
1893 { /* 0x5e */
1894 .id = &FOLDERID_SampleMusic,
1895 .type = CSIDL_Type_AllUsers,
1896 .category = KF_CATEGORY_COMMON,
1897 .name = L"SampleMusic",
1898 .parent = &FOLDERID_PublicMusic,
1899 .path = L"Sample Music",
1900 .attributes = FILE_ATTRIBUTE_READONLY,
1901 .flags = KFDF_PRECREATE,
1903 { /* 0x5f */
1904 .id = &FOLDERID_SamplePictures,
1905 .type = CSIDL_Type_AllUsers,
1906 .category = KF_CATEGORY_COMMON,
1907 .name = L"SamplePictures",
1908 .parent = &FOLDERID_PublicPictures,
1909 .path = L"Sample Pictures",
1910 .attributes = FILE_ATTRIBUTE_READONLY,
1911 .flags = KFDF_PRECREATE,
1913 { /* 0x60 */
1914 .id = &FOLDERID_SamplePlaylists,
1915 .type = CSIDL_Type_AllUsers,
1916 .category = KF_CATEGORY_COMMON,
1917 .name = L"SamplePlaylists",
1918 .parent = &FOLDERID_PublicMusic,
1919 .path = L"Sample Playlists",
1920 .attributes = FILE_ATTRIBUTE_READONLY,
1921 .flags = KFDF_PRECREATE,
1923 { /* 0x61 */
1924 .id = &FOLDERID_SampleVideos,
1925 .type = CSIDL_Type_AllUsers,
1926 .category = KF_CATEGORY_COMMON,
1927 .name = L"SampleVideos",
1928 .parent = &FOLDERID_PublicVideos,
1929 .path = L"Sample Videos",
1930 .attributes = FILE_ATTRIBUTE_READONLY,
1931 .flags = KFDF_PRECREATE,
1933 { /* 0x62 - CSIDL_SAVED_GAMES */
1934 .id = &FOLDERID_SavedGames,
1935 .type = CSIDL_Type_User,
1936 .category = KF_CATEGORY_PERUSER,
1937 .name = L"SavedGames",
1938 .parent = &FOLDERID_Profile,
1939 .path = L"Saved Games",
1940 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{4C5C32FF-BB9D-43b0-B5B4-2D72E54EAAA4}",
1941 .attributes = FILE_ATTRIBUTE_READONLY,
1942 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1944 { /* 0x63 - CSIDL_SEARCHES */
1945 .id = &FOLDERID_SavedSearches,
1946 .type = CSIDL_Type_User,
1947 .category = KF_CATEGORY_PERUSER,
1948 .name = L"Searches",
1949 .parent = &FOLDERID_Profile,
1950 .path = L"Searches",
1951 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{7d1d3a04-debb-4115-95cf-2f29da2920da}",
1952 .attributes = FILE_ATTRIBUTE_READONLY,
1953 .flags = KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1955 { /* 0x64 */
1956 .id = &FOLDERID_SEARCH_CSC,
1957 .type = CSIDL_Type_Disallowed,
1958 .category = KF_CATEGORY_VIRTUAL,
1959 .name = L"CSCFolder",
1960 .parsing = L"shell:::{BD7A2E7B-21CB-41b2-A086-B309680C6B7E}\\*",
1962 { /* 0x65 */
1963 .id = &FOLDERID_SEARCH_MAPI,
1964 .type = CSIDL_Type_Disallowed,
1965 .category = KF_CATEGORY_VIRTUAL,
1966 .name = L"MAPIFolder",
1967 .parsing = L"shell:::{89D83576-6BD1-4C86-9454-BEB04E94C819}\\*",
1969 { /* 0x66 */
1970 .id = &FOLDERID_SearchHome,
1971 .type = CSIDL_Type_Disallowed,
1972 .category = KF_CATEGORY_VIRTUAL,
1973 .name = L"SearchHomeFolder",
1974 .parsing = L"::{9343812e-1c37-4a49-a12e-4b2d810d956b}",
1976 { /* 0x67 */
1977 .id = &FOLDERID_SidebarDefaultParts,
1978 .type = CSIDL_Type_Disallowed, /* FIXME */
1979 .category = KF_CATEGORY_COMMON,
1980 .name = L"Default Gadgets",
1981 .parent = &FOLDERID_ProgramFiles,
1982 .path = L"Windows Sidebar\\Gadgets",
1984 { /* 0x68 */
1985 .id = &FOLDERID_SidebarParts,
1986 .type = CSIDL_Type_Disallowed, /* FIXME */
1987 .category = KF_CATEGORY_PERUSER,
1988 .name = L"Gadgets",
1989 .parent = &FOLDERID_LocalAppData,
1990 .path = L"Microsoft\\Windows Sidebar\\Gadgets",
1992 { /* 0x69 */
1993 .id = &FOLDERID_SyncManagerFolder,
1994 .type = CSIDL_Type_Disallowed,
1995 .category = KF_CATEGORY_VIRTUAL,
1996 .name = L"SyncCenterFolder",
1997 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}",
1999 { /* 0x6a */
2000 .id = &FOLDERID_SyncResultsFolder,
2001 .type = CSIDL_Type_Disallowed,
2002 .category = KF_CATEGORY_VIRTUAL,
2003 .name = L"SyncResultsFolder",
2004 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}\\::{BC48B32F-5910-47F5-8570-5074A8A5636A},",
2006 { /* 0x6b */
2007 .id = &FOLDERID_SyncSetupFolder,
2008 .type = CSIDL_Type_Disallowed,
2009 .category = KF_CATEGORY_VIRTUAL,
2010 .name = L"SyncSetupFolder",
2011 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}\\::{F1390A9A-A3F4-4E5D-9C5F-98F3BD8D935C},",
2013 { /* 0x6c */
2014 .id = &FOLDERID_UserPinned,
2015 .type = CSIDL_Type_Disallowed, /* FIXME */
2016 .category = KF_CATEGORY_PERUSER,
2017 .name = L"User Pinned",
2018 .parent = &FOLDERID_QuickLaunch,
2019 .path = L"User Pinned",
2020 .attributes = FILE_ATTRIBUTE_HIDDEN,
2021 .flags = KFDF_PRECREATE,
2023 { /* 0x6d */
2024 .id = &FOLDERID_UserProfiles,
2025 .type = CSIDL_Type_CurrVer,
2026 .value = L"Users",
2027 .def_path = L"Users",
2028 .category = KF_CATEGORY_FIXED,
2029 .name = L"UserProfiles",
2030 .attributes = FILE_ATTRIBUTE_READONLY,
2031 .flags = KFDF_PRECREATE,
2033 { /* 0x6e */
2034 .id = &FOLDERID_UserProgramFiles,
2035 .type = CSIDL_Type_Disallowed, /* FIXME */
2036 .category = KF_CATEGORY_PERUSER,
2037 .name = L"UserProgramFiles",
2038 .parent = &FOLDERID_LocalAppData,
2039 .path = L"Programs",
2041 { /* 0x6f */
2042 .id = &FOLDERID_UserProgramFilesCommon,
2043 .type = CSIDL_Type_Disallowed, /* FIXME */
2044 .category = KF_CATEGORY_PERUSER,
2045 .name = L"UserProgramFilesCommon",
2046 .parent = &FOLDERID_UserProgramFiles,
2047 .path = L"Common",
2049 { /* 0x70 */
2050 .id = &FOLDERID_UsersFiles,
2051 .type = CSIDL_Type_Disallowed,
2052 .category = KF_CATEGORY_VIRTUAL,
2053 .name = L"UsersFilesFolder",
2054 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}",
2056 { /* 0x71 */
2057 .id = &FOLDERID_UsersLibraries,
2058 .type = CSIDL_Type_Disallowed,
2059 .category = KF_CATEGORY_VIRTUAL,
2060 .name = L"UsersLibrariesFolder",
2061 .parsing = L"::{031E4825-7B94-4dc3-B131-E946B44C8DD5}",
2063 { /* 0x72 */
2064 .id = &FOLDERID_VideosLibrary,
2065 .type = CSIDL_Type_Disallowed, /* FIXME */
2066 .category = KF_CATEGORY_PERUSER,
2067 .name = L"VideosLibrary",
2068 .path = L"Videos.library-ms",
2069 .parsing = L"::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\\{491E922F-5643-4af4-A7EB-4E7A138D8174}",
2073 static int csidl_from_id( const KNOWNFOLDERID *id )
2075 int i;
2076 for (i = 0; i < ARRAY_SIZE(CSIDL_Data); i++)
2077 if (IsEqualGUID( CSIDL_Data[i].id, id )) return i;
2078 return -1;
2081 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest);
2083 /* Gets the value named value from the registry key
2084 * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
2085 * (or from rootKey\userPrefix\... if userPrefix is not NULL) into path, which
2086 * is assumed to be MAX_PATH WCHARs in length.
2087 * If it exists, expands the value and writes the expanded value to
2088 * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
2089 * Returns successful error code if the value was retrieved from the registry,
2090 * and a failure otherwise.
2092 static HRESULT _SHGetUserShellFolderPath(HKEY rootKey, LPCWSTR userPrefix,
2093 LPCWSTR value, LPWSTR path)
2095 HRESULT hr;
2096 WCHAR shellFolderPath[MAX_PATH], userShellFolderPath[MAX_PATH];
2097 LPCWSTR pShellFolderPath, pUserShellFolderPath;
2098 HKEY userShellFolderKey, shellFolderKey;
2099 DWORD dwType, dwPathLen;
2101 TRACE("%p,%s,%s,%p\n",rootKey, debugstr_w(userPrefix), debugstr_w(value),
2102 path);
2104 if (userPrefix)
2106 lstrcpyW(shellFolderPath, userPrefix);
2107 PathAddBackslashW(shellFolderPath);
2108 lstrcatW(shellFolderPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
2109 pShellFolderPath = shellFolderPath;
2110 lstrcpyW(userShellFolderPath, userPrefix);
2111 PathAddBackslashW(userShellFolderPath);
2112 lstrcatW(userShellFolderPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders");
2113 pUserShellFolderPath = userShellFolderPath;
2115 else
2117 pUserShellFolderPath = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders";
2118 pShellFolderPath = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
2121 if (RegCreateKeyW(rootKey, pShellFolderPath, &shellFolderKey))
2123 TRACE("Failed to create %s\n", debugstr_w(pShellFolderPath));
2124 return E_FAIL;
2126 if (RegCreateKeyW(rootKey, pUserShellFolderPath, &userShellFolderKey))
2128 TRACE("Failed to create %s\n",
2129 debugstr_w(pUserShellFolderPath));
2130 RegCloseKey(shellFolderKey);
2131 return E_FAIL;
2134 dwPathLen = MAX_PATH * sizeof(WCHAR);
2135 if (!RegQueryValueExW(userShellFolderKey, value, NULL, &dwType,
2136 (LPBYTE)path, &dwPathLen) && (dwType == REG_EXPAND_SZ || dwType == REG_SZ))
2138 LONG ret;
2140 path[dwPathLen / sizeof(WCHAR)] = '\0';
2141 if (dwType == REG_EXPAND_SZ && path[0] == '%')
2143 WCHAR szTemp[MAX_PATH];
2145 _SHExpandEnvironmentStrings(path, szTemp);
2146 lstrcpynW(path, szTemp, MAX_PATH);
2148 ret = RegSetValueExW(shellFolderKey, value, 0, REG_SZ, (LPBYTE)path,
2149 (lstrlenW(path) + 1) * sizeof(WCHAR));
2150 if (ret != ERROR_SUCCESS)
2151 hr = HRESULT_FROM_WIN32(ret);
2152 else
2153 hr = S_OK;
2155 else
2156 hr = E_FAIL;
2157 RegCloseKey(shellFolderKey);
2158 RegCloseKey(userShellFolderKey);
2159 TRACE("returning 0x%08lx\n", hr);
2160 return hr;
2163 static void append_relative_path(BYTE folder, WCHAR *pszPath)
2165 if (CSIDL_Data[folder].path)
2167 PathAddBackslashW(pszPath);
2168 lstrcatW(pszPath, CSIDL_Data[folder].path);
2170 else if (CSIDL_Data[folder].def_path)
2172 PathAddBackslashW(pszPath);
2173 lstrcatW(pszPath, CSIDL_Data[folder].def_path);
2177 /* Gets a 'semi-expanded' default value of the CSIDL with index folder into
2178 * pszPath, based on the entries in CSIDL_Data. By semi-expanded, I mean:
2179 * - Depending on the entry's type, the path may begin with an (unexpanded)
2180 * environment variable name. The caller is responsible for expanding
2181 * environment strings if so desired.
2182 * The types that are prepended with environment variables are:
2183 * CSIDL_Type_User: %USERPROFILE%
2184 * CSIDL_Type_AllUsers: %ALLUSERSPROFILE%
2185 * CSIDL_Type_CurrVer: %SystemDrive%
2186 * (Others might make sense too, but as yet are unneeded.)
2188 static HRESULT _SHGetDefaultValue(BYTE folder, LPWSTR pszPath)
2190 HRESULT hr;
2192 TRACE("0x%02x,%p\n", folder, pszPath);
2194 if (folder >= ARRAY_SIZE(CSIDL_Data))
2195 return E_INVALIDARG;
2197 if (!pszPath)
2198 return E_INVALIDARG;
2200 if (!is_win64)
2202 BOOL is_wow64;
2204 switch (folder)
2206 case CSIDL_PROGRAM_FILES:
2207 case CSIDL_PROGRAM_FILESX86:
2208 IsWow64Process( GetCurrentProcess(), &is_wow64 );
2209 folder = is_wow64 ? CSIDL_PROGRAM_FILESX86 : CSIDL_PROGRAM_FILES;
2210 break;
2211 case CSIDL_PROGRAM_FILES_COMMON:
2212 case CSIDL_PROGRAM_FILES_COMMONX86:
2213 IsWow64Process( GetCurrentProcess(), &is_wow64 );
2214 folder = is_wow64 ? CSIDL_PROGRAM_FILES_COMMONX86 : CSIDL_PROGRAM_FILES_COMMON;
2215 break;
2219 if (!CSIDL_Data[folder].parent)
2221 /* hit the root, sub in env var */
2222 switch (CSIDL_Data[folder].type)
2224 case CSIDL_Type_User:
2225 lstrcpyW(pszPath, L"%USERPROFILE%");
2226 break;
2227 case CSIDL_Type_AllUsers:
2228 lstrcpyW(pszPath, L"%PUBLIC%");
2229 break;
2230 case CSIDL_Type_ProgramData:
2231 lstrcpyW(pszPath, L"%ProgramData%");
2232 break;
2233 case CSIDL_Type_CurrVer:
2234 lstrcpyW(pszPath, L"%SystemDrive%");
2235 break;
2236 default:
2237 ; /* no corresponding env. var, do nothing */
2239 hr = S_OK;
2240 }else{
2241 /* prepend with parent */
2242 hr = _SHGetDefaultValue(csidl_from_id(CSIDL_Data[folder].parent), pszPath);
2245 if (SUCCEEDED(hr))
2246 append_relative_path(folder, pszPath);
2248 TRACE("returning 0x%08lx\n", hr);
2249 return hr;
2252 /* Gets the (unexpanded) value of the folder with index folder into pszPath.
2253 * The folder's type is assumed to be CSIDL_Type_CurrVer. Its default value
2254 * can be overridden in the HKLM\\Software\\Microsoft\\Windows\\CurrentVersion key.
2255 * If dwFlags has SHGFP_TYPE_DEFAULT set or if the value isn't overridden in
2256 * the registry, uses _SHGetDefaultValue to get the value.
2258 static HRESULT _SHGetCurrentVersionPath(DWORD dwFlags, BYTE folder,
2259 LPWSTR pszPath)
2261 HRESULT hr;
2263 TRACE("0x%08lx,0x%02x,%p\n", dwFlags, folder, pszPath);
2265 if (folder >= ARRAY_SIZE(CSIDL_Data))
2266 return E_INVALIDARG;
2267 if (CSIDL_Data[folder].type != CSIDL_Type_CurrVer)
2268 return E_INVALIDARG;
2269 if (!pszPath)
2270 return E_INVALIDARG;
2272 if (dwFlags & SHGFP_TYPE_DEFAULT)
2273 hr = _SHGetDefaultValue(folder, pszPath);
2274 else
2276 HKEY hKey;
2278 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion", &hKey))
2279 hr = E_FAIL;
2280 else
2282 DWORD dwType, dwPathLen = MAX_PATH * sizeof(WCHAR);
2284 if (RegQueryValueExW(hKey, CSIDL_Data[folder].value, NULL,
2285 &dwType, (LPBYTE)pszPath, &dwPathLen) ||
2286 (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
2288 hr = _SHGetDefaultValue(folder, pszPath);
2289 dwType = REG_EXPAND_SZ;
2290 switch (folder)
2292 case CSIDL_PROGRAM_FILESX86:
2293 case CSIDL_PROGRAM_FILES_COMMONX86:
2294 /* these two should never be set on 32-bit setups */
2295 if (!is_win64)
2297 BOOL is_wow64;
2298 IsWow64Process( GetCurrentProcess(), &is_wow64 );
2299 if (!is_wow64) break;
2301 /* fall through */
2302 default:
2303 RegSetValueExW(hKey, CSIDL_Data[folder].value, 0, dwType,
2304 (LPBYTE)pszPath, (lstrlenW(pszPath)+1)*sizeof(WCHAR));
2307 else
2309 pszPath[dwPathLen / sizeof(WCHAR)] = '\0';
2310 hr = S_OK;
2312 RegCloseKey(hKey);
2315 TRACE("returning 0x%08lx (output path is %s)\n", hr, debugstr_w(pszPath));
2316 return hr;
2319 static LPWSTR _GetUserSidStringFromToken(HANDLE Token)
2321 char InfoBuffer[64];
2322 PTOKEN_USER UserInfo;
2323 DWORD InfoSize;
2324 LPWSTR SidStr;
2326 UserInfo = (PTOKEN_USER) InfoBuffer;
2327 if (! GetTokenInformation(Token, TokenUser, InfoBuffer, sizeof(InfoBuffer),
2328 &InfoSize))
2330 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2331 return NULL;
2332 UserInfo = heap_alloc(InfoSize);
2333 if (UserInfo == NULL)
2334 return NULL;
2335 if (! GetTokenInformation(Token, TokenUser, UserInfo, InfoSize,
2336 &InfoSize))
2338 heap_free(UserInfo);
2339 return NULL;
2343 if (! ConvertSidToStringSidW(UserInfo->User.Sid, &SidStr))
2344 SidStr = NULL;
2346 if (UserInfo != (PTOKEN_USER) InfoBuffer)
2347 heap_free(UserInfo);
2349 return SidStr;
2352 /* Gets the user's path (unexpanded) for the CSIDL with index folder:
2353 * If SHGFP_TYPE_DEFAULT is set, calls _SHGetDefaultValue for it. Otherwise
2354 * calls _SHGetUserShellFolderPath for it. Where it looks depends on hToken:
2355 * - if hToken is -1, looks in HKEY_USERS\.Default
2356 * - otherwise looks first in HKEY_CURRENT_USER, followed by HKEY_LOCAL_MACHINE
2357 * if HKEY_CURRENT_USER doesn't contain any entries. If both fail, finally
2358 * calls _SHGetDefaultValue for it.
2360 static HRESULT _SHGetUserProfilePath(HANDLE hToken, DWORD dwFlags, BYTE folder,
2361 LPWSTR pszPath)
2363 const WCHAR *szValueName;
2364 WCHAR buffer[40];
2365 HRESULT hr;
2367 TRACE("%p,0x%08lx,0x%02x,%p\n", hToken, dwFlags, folder, pszPath);
2369 if (folder >= ARRAY_SIZE(CSIDL_Data))
2370 return E_INVALIDARG;
2371 if (CSIDL_Data[folder].type != CSIDL_Type_User)
2372 return E_INVALIDARG;
2373 if (!pszPath)
2374 return E_INVALIDARG;
2376 if (dwFlags & SHGFP_TYPE_DEFAULT)
2378 if (hToken != NULL && hToken != (HANDLE)-1)
2380 FIXME("unsupported for user other than current or default\n");
2381 return E_FAIL;
2383 hr = _SHGetDefaultValue(folder, pszPath);
2385 else
2387 static const WCHAR DefaultW[] = L".Default";
2388 LPCWSTR userPrefix = NULL;
2389 HKEY hRootKey;
2391 if (hToken == (HANDLE)-1)
2393 hRootKey = HKEY_USERS;
2394 userPrefix = DefaultW;
2396 else if (hToken == NULL)
2397 hRootKey = HKEY_CURRENT_USER;
2398 else
2400 hRootKey = HKEY_USERS;
2401 userPrefix = _GetUserSidStringFromToken(hToken);
2402 if (userPrefix == NULL)
2404 hr = E_FAIL;
2405 goto error;
2409 /* For CSIDL_Type_User we also use the GUID if no szValueName is provided */
2410 szValueName = CSIDL_Data[folder].value;
2411 if (!szValueName)
2413 StringFromGUID2( CSIDL_Data[folder].id, buffer, 39 );
2414 szValueName = &buffer[0];
2417 hr = _SHGetUserShellFolderPath(hRootKey, userPrefix, szValueName, pszPath);
2418 if (FAILED(hr) && hRootKey != HKEY_LOCAL_MACHINE)
2419 hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL, szValueName, pszPath);
2420 if (FAILED(hr))
2421 hr = _SHGetDefaultValue(folder, pszPath);
2422 if (userPrefix != NULL && userPrefix != DefaultW)
2423 LocalFree((HLOCAL) userPrefix);
2425 error:
2426 TRACE("returning 0x%08lx (output path is %s)\n", hr, debugstr_w(pszPath));
2427 return hr;
2430 /* Gets the (unexpanded) path for the CSIDL with index folder. If dwFlags has
2431 * SHGFP_TYPE_DEFAULT set, calls _SHGetDefaultValue. Otherwise calls
2432 * _SHGetUserShellFolderPath for it, looking only in HKEY_LOCAL_MACHINE.
2433 * If this fails, falls back to _SHGetDefaultValue.
2435 static HRESULT _SHGetAllUsersProfilePath(DWORD dwFlags, BYTE folder,
2436 LPWSTR pszPath)
2438 HRESULT hr;
2440 TRACE("0x%08lx,0x%02x,%p\n", dwFlags, folder, pszPath);
2442 if (folder >= ARRAY_SIZE(CSIDL_Data))
2443 return E_INVALIDARG;
2444 if (CSIDL_Data[folder].type != CSIDL_Type_AllUsers && CSIDL_Data[folder].type != CSIDL_Type_ProgramData)
2445 return E_INVALIDARG;
2446 if (!pszPath)
2447 return E_INVALIDARG;
2449 if (dwFlags & SHGFP_TYPE_DEFAULT)
2450 hr = _SHGetDefaultValue(folder, pszPath);
2451 else
2453 hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL,
2454 CSIDL_Data[folder].value, pszPath);
2455 if (FAILED(hr))
2456 hr = _SHGetDefaultValue(folder, pszPath);
2458 TRACE("returning 0x%08lx (output path is %s)\n", hr, debugstr_w(pszPath));
2459 return hr;
2462 static HRESULT _SHOpenProfilesKey(PHKEY pKey)
2464 LONG lRet;
2465 DWORD disp;
2467 lRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList", 0, NULL, 0,
2468 KEY_ALL_ACCESS, NULL, pKey, &disp);
2469 return HRESULT_FROM_WIN32(lRet);
2472 /* Reads the value named szValueName from the key profilesKey (assumed to be
2473 * opened by _SHOpenProfilesKey) into szValue, which is assumed to be MAX_PATH
2474 * WCHARs in length. If it doesn't exist, returns szDefault (and saves
2475 * szDefault to the registry).
2477 static HRESULT _SHGetProfilesValue(HKEY profilesKey, LPCWSTR szValueName,
2478 LPWSTR szValue, LPCWSTR szDefault)
2480 HRESULT hr;
2481 DWORD type, dwPathLen = MAX_PATH * sizeof(WCHAR);
2482 LONG lRet;
2484 TRACE("%p,%s,%p,%s\n", profilesKey, debugstr_w(szValueName), szValue,
2485 debugstr_w(szDefault));
2486 lRet = RegQueryValueExW(profilesKey, szValueName, NULL, &type,
2487 (LPBYTE)szValue, &dwPathLen);
2488 if (!lRet && (type == REG_SZ || type == REG_EXPAND_SZ) && dwPathLen
2489 && *szValue)
2491 dwPathLen /= sizeof(WCHAR);
2492 szValue[dwPathLen] = '\0';
2493 hr = S_OK;
2495 else
2497 /* Missing or invalid value, set a default */
2498 lstrcpynW(szValue, szDefault, MAX_PATH);
2499 TRACE("Setting missing value %s to %s\n", debugstr_w(szValueName),
2500 debugstr_w(szValue));
2501 lRet = RegSetValueExW(profilesKey, szValueName, 0, REG_EXPAND_SZ,
2502 (LPBYTE)szValue,
2503 (lstrlenW(szValue) + 1) * sizeof(WCHAR));
2504 if (lRet)
2505 hr = HRESULT_FROM_WIN32(lRet);
2506 else
2507 hr = S_OK;
2509 TRACE("returning 0x%08lx (output value is %s)\n", hr, debugstr_w(szValue));
2510 return hr;
2513 /* Attempts to expand environment variables from szSrc into szDest, which is
2514 * assumed to be MAX_PATH characters in length. Before referring to the
2515 * environment, handles a few variables directly, because the environment
2516 * variables may not be set when this is called (as during Wine's installation
2517 * when default values are being written to the registry).
2518 * The directly handled environment variables, and their source, are:
2519 * - ALLUSERSPROFILE, USERPROFILE: reads from the registry
2520 * - SystemDrive: uses GetSystemDirectoryW and uses the drive portion of its
2521 * path
2522 * If one of the directly handled environment variables is expanded, only
2523 * expands a single variable, and only in the beginning of szSrc.
2525 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest)
2527 HRESULT hr;
2528 WCHAR szTemp[MAX_PATH], szProfilesPrefix[MAX_PATH] = { 0 };
2529 HKEY key = NULL;
2531 TRACE("%s, %p\n", debugstr_w(szSrc), szDest);
2533 if (!szSrc || !szDest) return E_INVALIDARG;
2535 /* short-circuit if there's nothing to expand */
2536 if (szSrc[0] != '%')
2538 lstrcpyW(szDest, szSrc);
2539 hr = S_OK;
2540 goto end;
2542 /* Get the profile prefix, we'll probably be needing it */
2543 hr = _SHOpenProfilesKey(&key);
2544 if (SUCCEEDED(hr))
2546 WCHAR def_val[MAX_PATH];
2548 /* get the system drive */
2549 GetSystemDirectoryW(def_val, MAX_PATH);
2550 lstrcpyW( def_val + 3, L"users" );
2552 hr = _SHGetProfilesValue(key, L"ProfilesDirectory", szProfilesPrefix, def_val );
2555 *szDest = 0;
2556 lstrcpyW(szTemp, szSrc);
2557 while (SUCCEEDED(hr) && szTemp[0] == '%')
2559 if (!wcsnicmp(szTemp, L"%ALLUSERSPROFILE%", lstrlenW(L"%ALLUSERSPROFILE%")))
2561 WCHAR szAllUsers[MAX_PATH], def_val[MAX_PATH];
2563 GetSystemDirectoryW(def_val, MAX_PATH);
2564 lstrcpyW( def_val + 3, L"users\\Public" );
2566 hr = _SHGetProfilesValue(key, L"Public", szAllUsers, def_val);
2567 PathAppendW(szDest, szAllUsers);
2568 PathAppendW(szDest, szTemp + lstrlenW(L"%ALLUSERSPROFILE%"));
2570 else if (!wcsnicmp(szTemp, L"%PUBLIC%", lstrlenW(L"%PUBLIC%")))
2572 WCHAR szAllUsers[MAX_PATH], def_val[MAX_PATH];
2574 GetSystemDirectoryW(def_val, MAX_PATH);
2575 lstrcpyW( def_val + 3, L"users\\Public" );
2577 hr = _SHGetProfilesValue(key, L"Public", szAllUsers, def_val);
2578 PathAppendW(szDest, szAllUsers);
2579 PathAppendW(szDest, szTemp + lstrlenW(L"%PUBLIC%"));
2581 else if (!wcsnicmp(szTemp, L"%ProgramData%", lstrlenW(L"%ProgramData%")))
2583 WCHAR szProgramData[MAX_PATH], def_val[MAX_PATH];
2584 HKEY shellFolderKey;
2585 DWORD dwType, dwPathLen = sizeof(def_val);
2586 BOOL in_registry = FALSE;
2588 if (!RegCreateKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", &shellFolderKey))
2590 if (!RegQueryValueExW(shellFolderKey, L"Common AppData", NULL, &dwType,
2591 (LPBYTE)def_val, &dwPathLen) && (dwType == REG_EXPAND_SZ || dwType == REG_SZ))
2592 in_registry = TRUE;
2594 RegCloseKey(shellFolderKey);
2597 if (!in_registry)
2599 GetSystemDirectoryW(def_val, MAX_PATH);
2600 lstrcpyW( def_val + 3, L"ProgramData" );
2603 hr = _SHGetProfilesValue(key, L"ProgramData", szProgramData, def_val);
2604 PathAppendW(szDest, szProgramData);
2605 PathAppendW(szDest, szTemp + lstrlenW(L"%ProgramData%"));
2607 else if (!wcsnicmp(szTemp, L"%USERPROFILE%", lstrlenW(L"%USERPROFILE%")))
2609 WCHAR userName[MAX_PATH];
2610 DWORD userLen = MAX_PATH;
2612 lstrcpyW(szDest, szProfilesPrefix);
2613 GetUserNameW(userName, &userLen);
2614 PathAppendW(szDest, userName);
2615 PathAppendW(szDest, szTemp + lstrlenW(L"%USERPROFILE%"));
2617 else if (!wcsnicmp(szTemp, L"%SystemDrive%", lstrlenW(L"%SystemDrive%")))
2619 GetSystemDirectoryW(szDest, MAX_PATH);
2620 lstrcpyW(szDest + 3, szTemp + lstrlenW(L"%SystemDrive%") + 1);
2622 else
2624 DWORD ret = ExpandEnvironmentStringsW(szTemp, szDest, MAX_PATH);
2626 if (ret > MAX_PATH)
2627 hr = E_NOT_SUFFICIENT_BUFFER;
2628 else if (ret == 0)
2629 hr = HRESULT_FROM_WIN32(GetLastError());
2630 else if (!wcscmp( szTemp, szDest )) break; /* nothing expanded */
2632 if (SUCCEEDED(hr)) lstrcpyW(szTemp, szDest);
2634 end:
2635 if (key)
2636 RegCloseKey(key);
2637 TRACE("returning 0x%08lx (input was %s, output is %s)\n", hr,
2638 debugstr_w(szSrc), debugstr_w(szDest));
2639 return hr;
2642 static char *xdg_config;
2643 static DWORD xdg_config_len;
2645 static BOOL WINAPI init_xdg_dirs( INIT_ONCE *once, void *param, void **context )
2647 const WCHAR *var, *fmt = L"\\??\\unix%s/user-dirs.dirs";
2648 char *p;
2649 WCHAR *name, *ptr;
2650 HANDLE file;
2651 DWORD len;
2653 if (!(var = _wgetenv( L"XDG_CONFIG_HOME" )) || var[0] != '/')
2655 if (!(var = _wgetenv( L"WINEHOMEDIR" ))) return TRUE;
2656 fmt = L"%s/.config/user-dirs.dirs";
2658 len = lstrlenW(var) + lstrlenW(fmt);
2659 name = heap_alloc( len * sizeof(WCHAR) );
2660 swprintf( name, len, fmt, var );
2661 name[1] = '\\'; /* change \??\ to \\?\ */
2662 for (ptr = name; *ptr; ptr++) if (*ptr == '/') *ptr = '\\';
2664 file = CreateFileW( name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
2665 heap_free( name );
2666 if (file != INVALID_HANDLE_VALUE)
2668 len = GetFileSize( file, NULL );
2669 if (!(xdg_config = heap_alloc( len + 1 ))) return TRUE;
2670 if (!ReadFile( file, xdg_config, len, &xdg_config_len, NULL ))
2672 heap_free( xdg_config );
2673 xdg_config = NULL;
2675 else
2677 for (p = xdg_config; p < xdg_config + xdg_config_len; p++) if (*p == '\n') *p = 0;
2678 *p = 0; /* append null to simplify string parsing */
2680 CloseHandle( file );
2682 return TRUE;
2685 static char *get_xdg_path( const char *var )
2687 static INIT_ONCE once;
2688 char *p, *ret = NULL;
2689 int i;
2691 InitOnceExecuteOnce( &once, init_xdg_dirs, NULL, NULL );
2692 if (!xdg_config) return NULL;
2694 for (p = xdg_config; p < xdg_config + xdg_config_len; p += strlen(p) + 1)
2696 while (*p == ' ' || *p == '\t') p++;
2697 if (strncmp( p, var, strlen(var) )) continue;
2698 p += strlen(var);
2699 while (*p == ' ' || *p == '\t') p++;
2700 if (*p != '=') continue;
2701 p++;
2702 while (*p == ' ' || *p == '\t') p++;
2703 if (*p != '"') continue;
2704 p++;
2705 if (*p != '/' && strncmp( p, "$HOME/", 6 )) continue;
2707 if (!(ret = heap_alloc( strlen(p) + 1 ))) break;
2708 for (i = 0; *p && *p != '"'; i++, p++)
2710 if (*p == '\\' && p[1]) p++;
2711 ret[i] = *p;
2713 ret[i] = 0;
2714 if (*p != '"')
2716 heap_free( ret );
2717 ret = NULL;
2719 break;
2721 return ret;
2724 static BOOL link_folder( HANDLE mgr, const UNICODE_STRING *path, const char *link )
2726 struct mountmgr_shell_folder *ioctl;
2727 DWORD len = sizeof(*ioctl) + path->Length + strlen(link) + 1;
2728 BOOL ret;
2730 if (!(ioctl = heap_alloc( len ))) return FALSE;
2731 ioctl->create_backup = FALSE;
2732 ioctl->folder_offset = sizeof(*ioctl);
2733 ioctl->folder_size = path->Length;
2734 memcpy( (char *)ioctl + ioctl->folder_offset, path->Buffer, ioctl->folder_size );
2735 ioctl->symlink_offset = ioctl->folder_offset + ioctl->folder_size;
2736 strcpy( (char *)ioctl + ioctl->symlink_offset, link );
2738 ret = DeviceIoControl( mgr, IOCTL_MOUNTMGR_DEFINE_SHELL_FOLDER, ioctl, len, NULL, 0, NULL, NULL );
2739 heap_free( ioctl );
2740 return ret;
2743 /******************************************************************************
2744 * create_link
2746 * Sets up a symbolic link for one of the 'My Whatever' shell folders to point
2747 * into the corresponding XDG directory.
2749 static void create_link( const WCHAR *path, const char *xdg_name, const char *default_name )
2751 UNICODE_STRING nt_name;
2752 char *target = NULL;
2753 HANDLE mgr;
2755 if ((mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,
2756 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
2757 0, 0 )) == INVALID_HANDLE_VALUE)
2759 FIXME( "failed to connect to mount manager\n" );
2760 return;
2763 nt_name.Buffer = NULL;
2764 if (!RtlDosPathNameToNtPathName_U( path, &nt_name, NULL, NULL )) goto done;
2766 if ((target = get_xdg_path( xdg_name )))
2768 if (link_folder( mgr, &nt_name, target )) goto done;
2770 if (link_folder( mgr, &nt_name, default_name )) goto done;
2772 /* fall back to HOME */
2773 link_folder( mgr, &nt_name, "$HOME" );
2775 done:
2776 RtlFreeUnicodeString( &nt_name );
2777 heap_free( target );
2778 CloseHandle( mgr );
2781 /******************************************************************************
2782 * _SHCreateSymbolicLink [Internal]
2784 * Sets up a symbolic link for one of the special shell folders to point into
2785 * the users home directory.
2787 * PARAMS
2788 * nFolder [I] CSIDL identifying the folder.
2790 static void _SHCreateSymbolicLink(int nFolder, const WCHAR *path)
2792 DWORD folder = nFolder & CSIDL_FOLDER_MASK;
2794 switch (folder) {
2795 case CSIDL_PERSONAL:
2796 create_link( path, "XDG_DOCUMENTS_DIR", "$HOME/Documents" );
2797 break;
2798 case CSIDL_DESKTOPDIRECTORY:
2799 create_link( path, "XDG_DESKTOP_DIR", "$HOME/Desktop" );
2800 break;
2801 case CSIDL_MYPICTURES:
2802 create_link( path, "XDG_PICTURES_DIR", "$HOME/Pictures" );
2803 break;
2804 case CSIDL_MYVIDEO:
2805 create_link( path, "XDG_VIDEOS_DIR", "$HOME/Movies" );
2806 break;
2807 case CSIDL_MYMUSIC:
2808 create_link( path, "XDG_MUSIC_DIR", "$HOME/Music" );
2809 break;
2810 case CSIDL_DOWNLOADS:
2811 create_link( path, "XDG_DOWNLOAD_DIR", "$HOME/Downloads" );
2812 break;
2813 case CSIDL_TEMPLATES:
2814 create_link( path, "XDG_TEMPLATES_DIR", "$HOME/Templates" );
2815 break;
2819 /******************************************************************************
2820 * SHGetFolderPathW [SHELL32.@]
2822 * Convert nFolder to path.
2824 * RETURNS
2825 * Success: S_OK
2826 * Failure: standard HRESULT error codes.
2828 * NOTES
2829 * Most values can be overridden in either
2830 * HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
2831 * or in the same location in HKLM.
2832 * The "Shell Folders" registry key was used in NT4 and earlier systems.
2833 * Beginning with Windows 2000, the "User Shell Folders" key is used, so
2834 * changes made to it are made to the former key too. This synchronization is
2835 * done on-demand: not until someone requests the value of one of these paths
2836 * (by calling one of the SHGet functions) is the value synchronized.
2837 * Furthermore, the HKCU paths take precedence over the HKLM paths.
2839 HRESULT WINAPI SHGetFolderPathW(
2840 HWND hwndOwner, /* [I] owner window */
2841 int nFolder, /* [I] CSIDL identifying the folder */
2842 HANDLE hToken, /* [I] access token */
2843 DWORD dwFlags, /* [I] which path to return */
2844 LPWSTR pszPath) /* [O] converted path */
2846 HRESULT hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, NULL, pszPath);
2847 if(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
2848 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
2849 return hr;
2852 HRESULT WINAPI SHGetFolderPathAndSubDirA(
2853 HWND hwndOwner, /* [I] owner window */
2854 int nFolder, /* [I] CSIDL identifying the folder */
2855 HANDLE hToken, /* [I] access token */
2856 DWORD dwFlags, /* [I] which path to return */
2857 LPCSTR pszSubPath, /* [I] sub directory of the specified folder */
2858 LPSTR pszPath) /* [O] converted path */
2860 int length;
2861 HRESULT hr = S_OK;
2862 LPWSTR pszSubPathW = NULL;
2863 LPWSTR pszPathW = NULL;
2865 TRACE("%p,%#x,%p,%#lx,%s,%p\n", hwndOwner, nFolder, hToken, dwFlags, debugstr_a(pszSubPath), pszPath);
2867 if(pszPath) {
2868 pszPathW = heap_alloc(MAX_PATH * sizeof(WCHAR));
2869 if(!pszPathW) {
2870 hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
2871 goto cleanup;
2874 TRACE("%08x,%08lx,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
2876 /* SHGetFolderPathAndSubDirW does not distinguish if pszSubPath isn't
2877 * set (null), or an empty string.therefore call it without the parameter set
2878 * if pszSubPath is an empty string
2880 if (pszSubPath && pszSubPath[0]) {
2881 length = MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, NULL, 0);
2882 pszSubPathW = heap_alloc(length * sizeof(WCHAR));
2883 if(!pszSubPathW) {
2884 hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
2885 goto cleanup;
2887 MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, pszSubPathW, length);
2890 hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, pszSubPathW, pszPathW);
2892 if (SUCCEEDED(hr) && pszPath)
2893 WideCharToMultiByte(CP_ACP, 0, pszPathW, -1, pszPath, MAX_PATH, NULL, NULL);
2895 cleanup:
2896 heap_free(pszPathW);
2897 heap_free(pszSubPathW);
2898 return hr;
2901 /*************************************************************************
2902 * SHGetFolderPathAndSubDirW [SHELL32.@]
2904 HRESULT WINAPI SHGetFolderPathAndSubDirW(
2905 HWND hwndOwner, /* [I] owner window */
2906 int nFolder, /* [I] CSIDL identifying the folder */
2907 HANDLE hToken, /* [I] access token */
2908 DWORD dwFlags, /* [I] which path to return */
2909 LPCWSTR pszSubPath,/* [I] sub directory of the specified folder */
2910 LPWSTR pszPath) /* [O] converted path */
2912 HRESULT hr;
2913 WCHAR szBuildPath[MAX_PATH], szTemp[MAX_PATH];
2914 DWORD folder = nFolder & CSIDL_FOLDER_MASK;
2915 CSIDL_Type type;
2916 int ret;
2918 TRACE("%p,%#x,%p,%#lx,%s,%p\n", hwndOwner, nFolder, hToken, dwFlags, debugstr_w(pszSubPath), pszPath);
2920 /* Windows always NULL-terminates the resulting path regardless of success
2921 * or failure, so do so first
2923 if (pszPath)
2924 *pszPath = '\0';
2926 if (folder >= ARRAY_SIZE(CSIDL_Data))
2927 return E_INVALIDARG;
2928 if ((SHGFP_TYPE_CURRENT != dwFlags) && (SHGFP_TYPE_DEFAULT != dwFlags))
2929 return E_INVALIDARG;
2930 szTemp[0] = 0;
2931 type = CSIDL_Data[folder].type;
2932 switch (type)
2934 case CSIDL_Type_Disallowed:
2935 hr = E_INVALIDARG;
2936 break;
2937 case CSIDL_Type_NonExistent:
2938 hr = S_FALSE;
2939 break;
2940 case CSIDL_Type_WindowsPath:
2941 GetWindowsDirectoryW(szTemp, MAX_PATH);
2942 append_relative_path(folder, szTemp);
2943 hr = S_OK;
2944 break;
2945 case CSIDL_Type_SystemPath:
2946 GetSystemDirectoryW(szTemp, MAX_PATH);
2947 append_relative_path(folder, szTemp);
2948 hr = S_OK;
2949 break;
2950 case CSIDL_Type_SystemX86Path:
2951 if (!GetSystemWow64DirectoryW(szTemp, MAX_PATH)) GetSystemDirectoryW(szTemp, MAX_PATH);
2952 append_relative_path(folder, szTemp);
2953 hr = S_OK;
2954 break;
2955 case CSIDL_Type_CurrVer:
2956 hr = _SHGetCurrentVersionPath(dwFlags, folder, szTemp);
2957 break;
2958 case CSIDL_Type_User:
2959 hr = _SHGetUserProfilePath(hToken, dwFlags, folder, szTemp);
2960 break;
2961 case CSIDL_Type_AllUsers:
2962 case CSIDL_Type_ProgramData:
2963 hr = _SHGetAllUsersProfilePath(dwFlags, folder, szTemp);
2964 break;
2965 default:
2966 FIXME("bogus type %d, please fix\n", type);
2967 hr = E_INVALIDARG;
2968 break;
2971 /* Expand environment strings if necessary */
2972 if (*szTemp == '%')
2973 hr = _SHExpandEnvironmentStrings(szTemp, szBuildPath);
2974 else
2975 lstrcpyW(szBuildPath, szTemp);
2977 if (FAILED(hr)) goto end;
2979 if(pszSubPath) {
2980 /* make sure the new path does not exceed the buffer length
2981 * and remember to backslash and terminate it */
2982 if(MAX_PATH < (lstrlenW(szBuildPath) + lstrlenW(pszSubPath) + 2)) {
2983 hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
2984 goto end;
2986 PathAppendW(szBuildPath, pszSubPath);
2987 PathRemoveBackslashW(szBuildPath);
2989 /* Copy the path if it's available before we might return */
2990 if (SUCCEEDED(hr) && pszPath)
2991 lstrcpyW(pszPath, szBuildPath);
2993 /* if we don't care about existing directories we are ready */
2994 if(nFolder & CSIDL_FLAG_DONT_VERIFY) goto end;
2996 if (PathFileExistsW(szBuildPath)) goto end;
2998 /* not existing but we are not allowed to create it. The return value
2999 * is verified against shell32 version 6.0.
3001 if (!(nFolder & CSIDL_FLAG_CREATE))
3003 hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
3004 goto end;
3007 /* create symbolic links rather than directories for specific
3008 * user shell folders */
3009 _SHCreateSymbolicLink(folder, szBuildPath);
3011 /* create directory/directories */
3012 ret = SHCreateDirectoryExW(hwndOwner, szBuildPath, NULL);
3013 if (ret && ret != ERROR_ALREADY_EXISTS)
3015 ERR("Failed to create directory %s.\n", debugstr_w(szBuildPath));
3016 hr = E_FAIL;
3017 goto end;
3020 TRACE("Created missing system directory %s\n", debugstr_w(szBuildPath));
3021 end:
3022 TRACE("returning 0x%08lx (final path is %s)\n", hr, debugstr_w(szBuildPath));
3023 return hr;
3026 /*************************************************************************
3027 * SHGetFolderPathA [SHELL32.@]
3029 * See SHGetFolderPathW.
3031 HRESULT WINAPI SHGetFolderPathA(
3032 HWND hwndOwner,
3033 int nFolder,
3034 HANDLE hToken,
3035 DWORD dwFlags,
3036 LPSTR pszPath)
3038 WCHAR szTemp[MAX_PATH];
3039 HRESULT hr;
3041 TRACE("%p,%d,%p,%#lx,%p\n", hwndOwner, nFolder, hToken, dwFlags, pszPath);
3043 if (pszPath)
3044 *pszPath = '\0';
3045 hr = SHGetFolderPathW(hwndOwner, nFolder, hToken, dwFlags, szTemp);
3046 if (SUCCEEDED(hr) && pszPath)
3047 WideCharToMultiByte(CP_ACP, 0, szTemp, -1, pszPath, MAX_PATH, NULL,
3048 NULL);
3050 return hr;
3053 /* For each folder in folders, if its value has not been set in the registry,
3054 * calls _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the
3055 * folder's type) to get the unexpanded value first.
3056 * Writes the unexpanded value to User Shell Folders, and queries it with
3057 * SHGetFolderPathW to force the creation of the directory if it doesn't
3058 * already exist. SHGetFolderPathW also returns the expanded value, which
3059 * this then writes to Shell Folders.
3061 static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken,
3062 LPCWSTR szUserShellFolderPath, LPCWSTR szShellFolderPath, const UINT folders[],
3063 UINT foldersLen)
3065 const WCHAR *szValueName;
3066 WCHAR buffer[40];
3067 UINT i;
3068 WCHAR path[MAX_PATH];
3069 HRESULT hr = S_OK;
3070 HKEY hUserKey = NULL, hKey = NULL;
3071 DWORD dwType, dwPathLen;
3072 LONG ret;
3074 TRACE("%p,%p,%s,%p,%u\n", hRootKey, hToken,
3075 debugstr_w(szUserShellFolderPath), folders, foldersLen);
3077 ret = RegCreateKeyW(hRootKey, szUserShellFolderPath, &hUserKey);
3078 if (ret)
3079 hr = HRESULT_FROM_WIN32(ret);
3080 else
3082 ret = RegCreateKeyW(hRootKey, szShellFolderPath, &hKey);
3083 if (ret)
3084 hr = HRESULT_FROM_WIN32(ret);
3086 for (i = 0; SUCCEEDED(hr) && i < foldersLen; i++)
3088 dwPathLen = MAX_PATH * sizeof(WCHAR);
3090 /* For CSIDL_Type_User we also use the GUID if no szValueName is provided */
3091 szValueName = CSIDL_Data[folders[i]].value;
3092 if (!szValueName && CSIDL_Data[folders[i]].type == CSIDL_Type_User)
3094 StringFromGUID2( CSIDL_Data[folders[i]].id, buffer, 39 );
3095 szValueName = &buffer[0];
3098 if (RegQueryValueExW(hUserKey, szValueName, NULL,
3099 &dwType, (LPBYTE)path, &dwPathLen) || (dwType != REG_SZ &&
3100 dwType != REG_EXPAND_SZ))
3102 *path = '\0';
3103 if (CSIDL_Data[folders[i]].type == CSIDL_Type_User)
3104 _SHGetUserProfilePath(hToken, SHGFP_TYPE_DEFAULT, folders[i],
3105 path);
3106 else if (CSIDL_Data[folders[i]].type == CSIDL_Type_AllUsers ||
3107 CSIDL_Data[folders[i]].type == CSIDL_Type_ProgramData)
3108 _SHGetAllUsersProfilePath(SHGFP_TYPE_DEFAULT, folders[i], path);
3109 else if (CSIDL_Data[folders[i]].type == CSIDL_Type_WindowsPath)
3111 GetWindowsDirectoryW(path, MAX_PATH);
3112 append_relative_path(folders[i], path);
3114 else
3115 hr = E_FAIL;
3116 if (*path)
3118 ret = RegSetValueExW(hUserKey, szValueName, 0, REG_EXPAND_SZ,
3119 (LPBYTE)path, (lstrlenW(path) + 1) * sizeof(WCHAR));
3120 if (ret)
3121 hr = HRESULT_FROM_WIN32(ret);
3122 else
3124 hr = SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
3125 hToken, SHGFP_TYPE_DEFAULT, path);
3126 ret = RegSetValueExW(hKey, szValueName, 0, REG_SZ,
3127 (LPBYTE)path, (lstrlenW(path) + 1) * sizeof(WCHAR));
3128 if (ret)
3129 hr = HRESULT_FROM_WIN32(ret);
3133 else
3135 /* create the default dir, which may be different from the path
3136 * stored in the registry. */
3137 SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
3138 hToken, SHGFP_TYPE_DEFAULT, path);
3141 if (hUserKey)
3142 RegCloseKey(hUserKey);
3143 if (hKey)
3144 RegCloseKey(hKey);
3146 TRACE("returning 0x%08lx\n", hr);
3147 return hr;
3150 static HRESULT _SHRegisterUserShellFolders(BOOL bDefault)
3152 static const UINT folders[] = {
3153 CSIDL_PROGRAMS,
3154 CSIDL_PERSONAL,
3155 CSIDL_FAVORITES,
3156 CSIDL_APPDATA,
3157 CSIDL_STARTUP,
3158 CSIDL_RECENT,
3159 CSIDL_SENDTO,
3160 CSIDL_STARTMENU,
3161 CSIDL_MYMUSIC,
3162 CSIDL_MYVIDEO,
3163 CSIDL_DESKTOPDIRECTORY,
3164 CSIDL_NETHOOD,
3165 CSIDL_TEMPLATES,
3166 CSIDL_PRINTHOOD,
3167 CSIDL_LOCAL_APPDATA,
3168 CSIDL_INTERNET_CACHE,
3169 CSIDL_COOKIES,
3170 CSIDL_HISTORY,
3171 CSIDL_MYPICTURES,
3172 CSIDL_FONTS,
3173 CSIDL_ADMINTOOLS,
3174 CSIDL_CONTACTS,
3175 CSIDL_DOWNLOADS,
3176 CSIDL_LINKS,
3177 CSIDL_APPDATA_LOCALLOW,
3178 CSIDL_SAVED_GAMES,
3179 CSIDL_SEARCHES
3181 WCHAR userShellFolderPath[MAX_PATH], shellFolderPath[MAX_PATH];
3182 LPCWSTR pUserShellFolderPath, pShellFolderPath;
3183 HRESULT hr = S_OK;
3184 HKEY hRootKey;
3185 HANDLE hToken;
3187 TRACE("%s\n", bDefault ? "TRUE" : "FALSE");
3188 if (bDefault)
3190 hToken = (HANDLE)-1;
3191 hRootKey = HKEY_USERS;
3192 lstrcpyW(userShellFolderPath, L".Default");
3193 PathAddBackslashW(userShellFolderPath);
3194 lstrcatW(userShellFolderPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders");
3195 pUserShellFolderPath = userShellFolderPath;
3196 lstrcpyW(shellFolderPath, L".Default");
3197 PathAddBackslashW(shellFolderPath);
3198 lstrcatW(shellFolderPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
3199 pShellFolderPath = shellFolderPath;
3201 else
3203 hToken = NULL;
3204 hRootKey = HKEY_CURRENT_USER;
3205 pUserShellFolderPath = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders";
3206 pShellFolderPath = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
3209 hr = _SHRegisterFolders(hRootKey, hToken, pUserShellFolderPath,
3210 pShellFolderPath, folders, ARRAY_SIZE(folders));
3211 TRACE("returning 0x%08lx\n", hr);
3212 return hr;
3215 static HRESULT _SHRegisterCommonShellFolders(void)
3217 static const UINT folders[] = {
3218 CSIDL_COMMON_STARTMENU,
3219 CSIDL_COMMON_PROGRAMS,
3220 CSIDL_COMMON_STARTUP,
3221 CSIDL_COMMON_DESKTOPDIRECTORY,
3222 CSIDL_COMMON_FAVORITES,
3223 CSIDL_COMMON_APPDATA,
3224 CSIDL_COMMON_TEMPLATES,
3225 CSIDL_COMMON_DOCUMENTS,
3226 CSIDL_COMMON_ADMINTOOLS,
3227 CSIDL_COMMON_MUSIC,
3228 CSIDL_COMMON_PICTURES,
3229 CSIDL_COMMON_VIDEO,
3231 HRESULT hr;
3233 TRACE("\n");
3234 hr = _SHRegisterFolders(HKEY_LOCAL_MACHINE, NULL,
3235 L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders",
3236 L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
3237 folders, ARRAY_SIZE(folders));
3238 TRACE("returning 0x%08lx\n", hr);
3239 return hr;
3242 /******************************************************************************
3243 * create_extra_folders [Internal]
3245 * Create some extra folders that don't have a standard CSIDL definition.
3247 static HRESULT create_extra_folders(void)
3249 WCHAR path[MAX_PATH+5];
3250 HRESULT hr;
3251 HKEY hkey;
3252 DWORD type, size, ret;
3254 ret = RegCreateKeyW( HKEY_CURRENT_USER, L"Environment", &hkey );
3255 if (ret) return HRESULT_FROM_WIN32( ret );
3257 /* FIXME: should be under AppData, but we don't want spaces in the temp path */
3258 hr = SHGetFolderPathAndSubDirW( 0, CSIDL_PROFILE | CSIDL_FLAG_CREATE, NULL,
3259 SHGFP_TYPE_DEFAULT, L"Temp", path );
3260 if (SUCCEEDED(hr))
3262 size = sizeof(path);
3263 if (RegQueryValueExW( hkey, L"TEMP", NULL, &type, (LPBYTE)path, &size ))
3264 RegSetValueExW( hkey, L"TEMP", 0, REG_SZ, (LPBYTE)path, (lstrlenW(path) + 1) * sizeof(WCHAR) );
3265 size = sizeof(path);
3266 if (RegQueryValueExW( hkey, L"TMP", NULL, &type, (LPBYTE)path, &size ))
3267 RegSetValueExW( hkey, L"TMP", 0, REG_SZ, (LPBYTE)path, (lstrlenW(path) + 1) * sizeof(WCHAR) );
3269 RegCloseKey( hkey );
3271 if (SUCCEEDED(hr))
3273 hr = SHGetFolderPathAndSubDirW( 0, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, NULL,
3274 SHGFP_TYPE_DEFAULT, L"Microsoft", path );
3276 if (SUCCEEDED(hr))
3278 hr = SHGetFolderPathAndSubDirW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL,
3279 SHGFP_TYPE_DEFAULT, L"Microsoft\\Windows\\Themes", path);
3281 return hr;
3285 /******************************************************************************
3286 * set_folder_attributes
3288 * Set the various folder attributes registry keys.
3290 static HRESULT set_folder_attributes(void)
3292 static const struct
3294 const CLSID *clsid;
3295 BOOL wfparsing : 1;
3296 BOOL wfdisplay : 1;
3297 BOOL hideasdel : 1;
3298 DWORD attr;
3299 DWORD call_for_attr;
3300 } folders[] =
3302 { &CLSID_UnixFolder, TRUE, FALSE, FALSE },
3303 { &CLSID_UnixDosFolder, TRUE, FALSE, FALSE,
3304 SFGAO_FILESYSANCESTOR|SFGAO_FOLDER|SFGAO_HASSUBFOLDER, SFGAO_FILESYSTEM },
3305 { &CLSID_FolderShortcut, FALSE, FALSE, FALSE,
3306 SFGAO_FILESYSTEM|SFGAO_FOLDER|SFGAO_LINK,
3307 SFGAO_HASSUBFOLDER|SFGAO_FILESYSTEM|SFGAO_FOLDER|SFGAO_FILESYSANCESTOR },
3308 { &CLSID_MyDocuments, TRUE, FALSE, FALSE,
3309 SFGAO_FILESYSANCESTOR|SFGAO_FOLDER|SFGAO_HASSUBFOLDER, SFGAO_FILESYSTEM },
3310 { &CLSID_RecycleBin, FALSE, FALSE, FALSE,
3311 SFGAO_FOLDER|SFGAO_DROPTARGET|SFGAO_HASPROPSHEET },
3312 { &CLSID_ControlPanel, FALSE, TRUE, TRUE,
3313 SFGAO_FOLDER|SFGAO_HASSUBFOLDER }
3316 unsigned int i;
3317 WCHAR buffer[39 + ARRAY_SIZE(L"CLSID\\") + ARRAY_SIZE(L"\\ShellFolder")];
3318 LONG res;
3319 HKEY hkey;
3321 for (i = 0; i < ARRAY_SIZE(folders); i++)
3323 lstrcpyW( buffer, L"CLSID\\" );
3324 StringFromGUID2( folders[i].clsid, buffer + lstrlenW(buffer), 39 );
3325 lstrcatW( buffer, L"\\ShellFolder" );
3326 res = RegCreateKeyExW( HKEY_CLASSES_ROOT, buffer, 0, NULL, 0,
3327 KEY_READ | KEY_WRITE, NULL, &hkey, NULL);
3328 if (res) return HRESULT_FROM_WIN32( res );
3329 if (folders[i].wfparsing)
3330 res = RegSetValueExW( hkey, L"WantsFORPARSING", 0, REG_SZ, (const BYTE *)L"", sizeof(WCHAR) );
3331 if (folders[i].wfdisplay)
3332 res = RegSetValueExW( hkey, L"WantsFORDISPLAY", 0, REG_SZ, (const BYTE *)L"", sizeof(WCHAR) );
3333 if (folders[i].hideasdel)
3334 res = RegSetValueExW( hkey, L"HideAsDeletePerUser", 0, REG_SZ, (const BYTE *)L"", sizeof(WCHAR) );
3335 if (folders[i].attr)
3336 res = RegSetValueExW( hkey, L"Attributes", 0, REG_DWORD,
3337 (const BYTE *)&folders[i].attr, sizeof(DWORD));
3338 if (folders[i].call_for_attr)
3339 res = RegSetValueExW( hkey, L"CallForAttributes", 0, REG_DWORD,
3340 (const BYTE *)&folders[i].call_for_attr, sizeof(DWORD));
3341 RegCloseKey( hkey );
3343 return S_OK;
3346 /*************************************************************************
3347 * SHGetSpecialFolderPathA [SHELL32.@]
3349 BOOL WINAPI SHGetSpecialFolderPathA (
3350 HWND hwndOwner,
3351 LPSTR szPath,
3352 int nFolder,
3353 BOOL bCreate)
3355 return SHGetFolderPathA(hwndOwner, nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0), NULL, 0,
3356 szPath) == S_OK;
3359 /*************************************************************************
3360 * SHGetSpecialFolderPathW
3362 BOOL WINAPI SHGetSpecialFolderPathW (
3363 HWND hwndOwner,
3364 LPWSTR szPath,
3365 int nFolder,
3366 BOOL bCreate)
3368 return SHGetFolderPathW(hwndOwner, nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0), NULL, 0,
3369 szPath) == S_OK;
3372 /*************************************************************************
3373 * SHGetSpecialFolderPath (SHELL32.175)
3375 BOOL WINAPI SHGetSpecialFolderPathAW (
3376 HWND hwndOwner,
3377 LPVOID szPath,
3378 int nFolder,
3379 BOOL bCreate)
3382 if (SHELL_OsIsUnicode())
3383 return SHGetSpecialFolderPathW (hwndOwner, szPath, nFolder, bCreate);
3384 return SHGetSpecialFolderPathA (hwndOwner, szPath, nFolder, bCreate);
3387 /*************************************************************************
3388 * SHGetFolderLocation [SHELL32.@]
3390 * Gets the folder locations from the registry and creates a pidl.
3392 * PARAMS
3393 * hwndOwner [I]
3394 * nFolder [I] CSIDL_xxxxx
3395 * hToken [I] token representing user, or NULL for current user, or -1 for
3396 * default user
3397 * dwReserved [I] must be zero
3398 * ppidl [O] PIDL of a special folder
3400 * RETURNS
3401 * Success: S_OK
3402 * Failure: Standard OLE-defined error result, S_FALSE or E_INVALIDARG
3404 * NOTES
3405 * Creates missing reg keys and directories.
3406 * Mostly forwards to SHGetFolderPathW, but a few values of nFolder return
3407 * virtual folders that are handled here.
3409 HRESULT WINAPI SHGetFolderLocation(
3410 HWND hwndOwner,
3411 int nFolder,
3412 HANDLE hToken,
3413 DWORD dwReserved,
3414 LPITEMIDLIST *ppidl)
3416 HRESULT hr = E_INVALIDARG;
3418 TRACE("%p 0x%08x %p 0x%08lx %p\n",
3419 hwndOwner, nFolder, hToken, dwReserved, ppidl);
3421 if (!ppidl)
3422 return E_INVALIDARG;
3423 if (dwReserved)
3424 return E_INVALIDARG;
3426 /* The virtual folders' locations are not user-dependent */
3427 *ppidl = NULL;
3428 switch (nFolder & CSIDL_FOLDER_MASK)
3430 case CSIDL_DESKTOP:
3431 *ppidl = _ILCreateDesktop();
3432 break;
3434 case CSIDL_PERSONAL:
3435 *ppidl = _ILCreateMyDocuments();
3436 break;
3438 case CSIDL_INTERNET:
3439 *ppidl = _ILCreateIExplore();
3440 break;
3442 case CSIDL_CONTROLS:
3443 *ppidl = _ILCreateControlPanel();
3444 break;
3446 case CSIDL_PRINTERS:
3447 *ppidl = _ILCreatePrinters();
3448 break;
3450 case CSIDL_BITBUCKET:
3451 *ppidl = _ILCreateBitBucket();
3452 break;
3454 case CSIDL_DRIVES:
3455 *ppidl = _ILCreateMyComputer();
3456 break;
3458 case CSIDL_NETWORK:
3459 *ppidl = _ILCreateNetwork();
3460 break;
3462 default:
3464 WCHAR szPath[MAX_PATH];
3466 hr = SHGetFolderPathW(hwndOwner, nFolder, hToken,
3467 SHGFP_TYPE_CURRENT, szPath);
3468 if (SUCCEEDED(hr))
3470 DWORD attributes=0;
3472 TRACE("Value=%s\n", debugstr_w(szPath));
3473 hr = SHILCreateFromPathW(szPath, ppidl, &attributes);
3475 else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
3477 /* unlike SHGetFolderPath, SHGetFolderLocation in shell32
3478 * version 6.0 returns E_FAIL for nonexistent paths
3480 hr = E_FAIL;
3484 if(*ppidl)
3485 hr = S_OK;
3487 TRACE("-- (new pidl %p)\n",*ppidl);
3488 return hr;
3491 /*************************************************************************
3492 * SHGetSpecialFolderLocation [SHELL32.@]
3494 * NOTES
3495 * In NT5, SHGetSpecialFolderLocation needs the <winntdir>/Recent
3496 * directory.
3498 HRESULT WINAPI SHGetSpecialFolderLocation(
3499 HWND hwndOwner,
3500 INT nFolder,
3501 LPITEMIDLIST * ppidl)
3503 HRESULT hr = E_INVALIDARG;
3505 TRACE("(%p,0x%x,%p)\n", hwndOwner,nFolder,ppidl);
3507 if (!ppidl)
3508 return E_INVALIDARG;
3510 hr = SHGetFolderLocation(hwndOwner, nFolder, NULL, 0, ppidl);
3511 return hr;
3514 /*************************************************************************
3515 * SHGetKnownFolderPath [SHELL32.@]
3517 HRESULT WINAPI SHGetKnownFolderPath(REFKNOWNFOLDERID rfid, DWORD flags, HANDLE token, WCHAR **ret_path)
3519 WCHAR pathW[MAX_PATH];
3520 HRESULT hr;
3521 int folder = csidl_from_id(rfid), shgfp_flags;
3523 TRACE("%s, 0x%08lx, %p, %p\n", debugstr_guid(rfid), flags, token, ret_path);
3525 *ret_path = NULL;
3527 if (folder < 0)
3528 return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
3530 if (flags & ~(KF_FLAG_CREATE|KF_FLAG_SIMPLE_IDLIST|KF_FLAG_DONT_UNEXPAND|
3531 KF_FLAG_DONT_VERIFY|KF_FLAG_NO_ALIAS|KF_FLAG_INIT|KF_FLAG_DEFAULT_PATH|KF_FLAG_NOT_PARENT_RELATIVE))
3533 FIXME("flags 0x%08lx not supported\n", flags);
3534 return E_INVALIDARG;
3537 if ((flags & (KF_FLAG_DEFAULT_PATH | KF_FLAG_NOT_PARENT_RELATIVE)) == KF_FLAG_NOT_PARENT_RELATIVE)
3539 WARN("Invalid flags mask %#lx.\n", flags);
3540 return E_INVALIDARG;
3543 if (flags & KF_FLAG_NOT_PARENT_RELATIVE)
3545 FIXME("Ignoring KF_FLAG_NOT_PARENT_RELATIVE.\n");
3546 flags &= ~KF_FLAG_NOT_PARENT_RELATIVE;
3549 folder |= flags & CSIDL_FLAG_MASK;
3550 shgfp_flags = flags & KF_FLAG_DEFAULT_PATH ? SHGFP_TYPE_DEFAULT : SHGFP_TYPE_CURRENT;
3552 hr = SHGetFolderPathAndSubDirW( 0, folder, token, shgfp_flags, NULL, pathW );
3553 if (FAILED( hr ))
3555 TRACE("Failed to get folder path, %#lx.\n", hr);
3556 return hr;
3559 TRACE("Final path is %s, %#lx\n", debugstr_w(pathW), hr);
3561 *ret_path = CoTaskMemAlloc((lstrlenW(pathW) + 1) * sizeof(WCHAR));
3562 if (!*ret_path)
3563 return E_OUTOFMEMORY;
3564 lstrcpyW(*ret_path, pathW);
3566 return hr;
3569 /*************************************************************************
3570 * SHGetFolderPathEx [SHELL32.@]
3572 HRESULT WINAPI SHGetFolderPathEx(REFKNOWNFOLDERID rfid, DWORD flags, HANDLE token, LPWSTR path, DWORD len)
3574 HRESULT hr;
3575 WCHAR *buffer;
3577 TRACE("%s, 0x%08lx, %p, %p, %lu\n", debugstr_guid(rfid), flags, token, path, len);
3579 if (!path || !len) return E_INVALIDARG;
3581 hr = SHGetKnownFolderPath( rfid, flags, token, &buffer );
3582 if (SUCCEEDED( hr ))
3584 if (lstrlenW( buffer ) + 1 > len)
3586 CoTaskMemFree( buffer );
3587 return HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER );
3589 lstrcpyW( path, buffer );
3590 CoTaskMemFree( buffer );
3592 return hr;
3596 * Internal function to convert known folder identifier to path of registry key
3597 * associated with known folder.
3599 * Parameters:
3600 * rfid [I] pointer to known folder identifier (may be NULL)
3601 * lpStringGuid [I] string with known folder identifier (used when rfid is NULL)
3602 * lpPath [O] place to store string address. String should be
3603 * later freed using HeapFree(GetProcessHeap(),0, ... )
3605 static HRESULT get_known_folder_registry_path(
3606 REFKNOWNFOLDERID rfid,
3607 LPWSTR lpStringGuid,
3608 LPWSTR *lpPath)
3610 HRESULT hr = S_OK;
3611 int length;
3612 WCHAR sGuid[50];
3614 TRACE("(%s, %s, %p)\n", debugstr_guid(rfid), debugstr_w(lpStringGuid), lpPath);
3616 if(rfid)
3617 StringFromGUID2(rfid, sGuid, ARRAY_SIZE(sGuid));
3618 else
3619 lstrcpyW(sGuid, lpStringGuid);
3621 length = lstrlenW(L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FolderDescriptions")+51;
3622 *lpPath = heap_alloc(length*sizeof(WCHAR));
3623 if(!(*lpPath))
3624 hr = E_OUTOFMEMORY;
3626 if(SUCCEEDED(hr))
3628 lstrcpyW(*lpPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FolderDescriptions\\");
3629 lstrcatW(*lpPath, sGuid);
3632 return hr;
3635 static HRESULT get_known_folder_wstr(const WCHAR *regpath, const WCHAR *value, WCHAR **out)
3637 DWORD size = 0;
3638 HRESULT hr;
3640 size = 0;
3641 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, regpath, value, RRF_RT_REG_SZ, NULL, NULL, &size));
3642 if(FAILED(hr))
3643 return hr;
3645 *out = CoTaskMemAlloc(size);
3646 if(!*out)
3647 return E_OUTOFMEMORY;
3649 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, regpath, value, RRF_RT_REG_SZ, NULL, *out, &size));
3650 if(FAILED(hr)){
3651 CoTaskMemFree(*out);
3652 *out = NULL;
3655 return hr;
3658 static HRESULT get_known_folder_dword(const WCHAR *registryPath, const WCHAR *value, DWORD *out)
3660 DWORD dwSize = sizeof(DWORD);
3661 DWORD dwType;
3662 return HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, registryPath, value, RRF_RT_DWORD, &dwType, out, &dwSize));
3666 * Internal function to get place where folder redirection information are stored.
3668 * Parameters:
3669 * rfid [I] pointer to known folder identifier (may be NULL)
3670 * rootKey [O] root key where the redirection information are stored
3671 * It can be HKLM for COMMON folders, and HKCU for PERUSER folders.
3673 static HRESULT get_known_folder_redirection_place(
3674 REFKNOWNFOLDERID rfid,
3675 HKEY *rootKey)
3677 HRESULT hr;
3678 LPWSTR lpRegistryPath = NULL;
3679 DWORD category;
3681 /* first, get known folder's category */
3682 hr = get_known_folder_registry_path(rfid, NULL, &lpRegistryPath);
3684 if(SUCCEEDED(hr))
3685 hr = get_known_folder_dword(lpRegistryPath, L"Category", &category);
3687 if(SUCCEEDED(hr))
3689 if(category == KF_CATEGORY_COMMON)
3691 *rootKey = HKEY_LOCAL_MACHINE;
3692 hr = S_OK;
3694 else if(category == KF_CATEGORY_PERUSER)
3696 *rootKey = HKEY_CURRENT_USER;
3697 hr = S_OK;
3699 else
3700 hr = E_FAIL;
3703 heap_free(lpRegistryPath);
3704 return hr;
3707 static HRESULT get_known_folder_path_by_id(REFKNOWNFOLDERID folderId, LPWSTR lpRegistryPath, DWORD dwFlags, LPWSTR *ppszPath);
3709 static HRESULT redirect_known_folder(
3710 REFKNOWNFOLDERID rfid,
3711 HWND hwnd,
3712 KF_REDIRECT_FLAGS flags,
3713 LPCWSTR pszTargetPath,
3714 UINT cFolders,
3715 KNOWNFOLDERID const *pExclusion,
3716 LPWSTR *ppszError)
3718 HRESULT hr;
3719 HKEY rootKey = HKEY_LOCAL_MACHINE, hKey;
3720 WCHAR sGuid[39];
3721 LPWSTR lpRegistryPath = NULL, lpSrcPath = NULL;
3722 TRACE("(%s, %p, 0x%08x, %s, %d, %p, %p)\n", debugstr_guid(rfid), hwnd, flags, debugstr_w(pszTargetPath), cFolders, pExclusion, ppszError);
3724 if (ppszError) *ppszError = NULL;
3726 hr = get_known_folder_registry_path(rfid, NULL, &lpRegistryPath);
3728 if(SUCCEEDED(hr))
3729 hr = get_known_folder_path_by_id(rfid, lpRegistryPath, 0, &lpSrcPath);
3731 heap_free(lpRegistryPath);
3733 /* get path to redirection storage */
3734 if(SUCCEEDED(hr))
3735 hr = get_known_folder_redirection_place(rfid, &rootKey);
3737 /* write redirection information */
3738 if(SUCCEEDED(hr))
3739 hr = HRESULT_FROM_WIN32(RegCreateKeyExW(rootKey, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL));
3741 if(SUCCEEDED(hr))
3743 StringFromGUID2(rfid, sGuid, ARRAY_SIZE(sGuid));
3745 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sGuid, 0, REG_SZ, (LPBYTE)pszTargetPath, (lstrlenW(pszTargetPath)+1)*sizeof(WCHAR)));
3747 RegCloseKey(hKey);
3750 /* make sure destination path exists */
3751 SHCreateDirectory(NULL, pszTargetPath);
3753 /* copy content if required */
3754 if(SUCCEEDED(hr) && (flags & KF_REDIRECT_COPY_CONTENTS) )
3756 WCHAR srcPath[MAX_PATH+1], dstPath[MAX_PATH+1];
3757 SHFILEOPSTRUCTW fileOp;
3759 ZeroMemory(srcPath, sizeof(srcPath));
3760 lstrcpyW(srcPath, lpSrcPath);
3761 lstrcatW(srcPath, L"\\*");
3763 ZeroMemory(dstPath, sizeof(dstPath));
3764 lstrcpyW(dstPath, pszTargetPath);
3766 ZeroMemory(&fileOp, sizeof(fileOp));
3768 if(flags & KF_REDIRECT_DEL_SOURCE_CONTENTS)
3769 fileOp.wFunc = FO_MOVE;
3770 else
3771 fileOp.wFunc = FO_COPY;
3773 fileOp.pFrom = srcPath;
3774 fileOp.pTo = dstPath;
3775 fileOp.fFlags = FOF_NO_UI;
3777 hr = (SHFileOperationW(&fileOp)==0 ? S_OK : E_FAIL);
3779 if(flags & KF_REDIRECT_DEL_SOURCE_CONTENTS)
3781 ZeroMemory(srcPath, sizeof(srcPath));
3782 lstrcpyW(srcPath, lpSrcPath);
3784 ZeroMemory(&fileOp, sizeof(fileOp));
3785 fileOp.wFunc = FO_DELETE;
3786 fileOp.pFrom = srcPath;
3787 fileOp.fFlags = FOF_NO_UI;
3789 hr = (SHFileOperationW(&fileOp)==0 ? S_OK : E_FAIL);
3793 CoTaskMemFree(lpSrcPath);
3795 return hr;
3799 struct knownfolder
3801 IKnownFolder IKnownFolder_iface;
3802 LONG refs;
3803 KNOWNFOLDERID id;
3804 LPWSTR registryPath;
3807 static inline struct knownfolder *impl_from_IKnownFolder( IKnownFolder *iface )
3809 return CONTAINING_RECORD( iface, struct knownfolder, IKnownFolder_iface );
3812 static ULONG WINAPI knownfolder_AddRef(
3813 IKnownFolder *iface )
3815 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
3816 return InterlockedIncrement( &knownfolder->refs );
3819 static ULONG WINAPI knownfolder_Release(
3820 IKnownFolder *iface )
3822 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
3823 LONG refs = InterlockedDecrement( &knownfolder->refs );
3824 if (!refs)
3826 TRACE("destroying %p\n", knownfolder);
3827 heap_free( knownfolder->registryPath );
3828 heap_free( knownfolder );
3830 return refs;
3833 static HRESULT WINAPI knownfolder_QueryInterface(
3834 IKnownFolder *iface,
3835 REFIID riid,
3836 void **ppv )
3838 struct knownfolder *This = impl_from_IKnownFolder( iface );
3840 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppv );
3842 *ppv = NULL;
3843 if ( IsEqualGUID( riid, &IID_IKnownFolder ) ||
3844 IsEqualGUID( riid, &IID_IUnknown ) )
3846 *ppv = iface;
3848 else if ( IsEqualGUID( riid, &IID_IMarshal ) )
3850 TRACE("IID_IMarshal returning NULL.\n");
3851 return E_NOINTERFACE;
3853 else
3855 FIXME("interface %s not implemented\n", debugstr_guid(riid));
3856 return E_NOINTERFACE;
3858 IKnownFolder_AddRef( iface );
3859 return S_OK;
3862 static HRESULT knownfolder_set_id(
3863 struct knownfolder *knownfolder,
3864 const KNOWNFOLDERID *kfid)
3866 HKEY hKey;
3867 HRESULT hr;
3869 TRACE("%s\n", debugstr_guid(kfid));
3871 knownfolder->id = *kfid;
3873 /* check is it registry-registered folder */
3874 hr = get_known_folder_registry_path(kfid, NULL, &knownfolder->registryPath);
3875 if(SUCCEEDED(hr))
3876 hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, knownfolder->registryPath, 0, 0, &hKey));
3878 if(SUCCEEDED(hr))
3880 hr = S_OK;
3881 RegCloseKey(hKey);
3883 else
3885 /* This known folder is not registered. To mark it, we set registryPath to NULL */
3886 heap_free(knownfolder->registryPath);
3887 knownfolder->registryPath = NULL;
3888 hr = S_OK;
3891 return hr;
3894 static HRESULT WINAPI knownfolder_GetId(
3895 IKnownFolder *iface,
3896 KNOWNFOLDERID *pkfid)
3898 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
3900 TRACE("%p\n", pkfid);
3902 *pkfid = knownfolder->id;
3903 return S_OK;
3906 static HRESULT WINAPI knownfolder_GetCategory(
3907 IKnownFolder *iface,
3908 KF_CATEGORY *pCategory)
3910 struct knownfolder *knownfolder = impl_from_IKnownFolder(iface);
3911 HRESULT hr = S_OK;
3913 TRACE("%p, %p\n", knownfolder, pCategory);
3915 /* we cannot get a category for a folder which is not registered */
3916 if(!knownfolder->registryPath)
3917 hr = E_FAIL;
3919 if(SUCCEEDED(hr))
3920 hr = get_known_folder_dword(knownfolder->registryPath, L"Category", (DWORD *)pCategory);
3922 return hr;
3925 static HRESULT WINAPI knownfolder_GetShellItem(
3926 IKnownFolder *iface,
3927 DWORD flags,
3928 REFIID riid,
3929 void **ppv)
3931 struct knownfolder *knownfolder = impl_from_IKnownFolder(iface);
3932 TRACE("(%p, 0x%08lx, %s, %p)\n", knownfolder, flags, debugstr_guid(riid), ppv);
3933 return SHGetKnownFolderItem(&knownfolder->id, flags, NULL, riid, ppv);
3936 static HRESULT get_known_folder_path(
3937 LPWSTR sFolderId,
3938 LPWSTR registryPath,
3939 LPWSTR *ppszPath)
3941 HRESULT hr;
3942 DWORD dwSize, dwType;
3943 WCHAR path[MAX_PATH] = {0};
3944 WCHAR parentGuid[39];
3945 DWORD category;
3946 LPWSTR parentRegistryPath, parentPath;
3947 HKEY hRedirectionRootKey = NULL;
3949 TRACE("(%s, %p)\n", debugstr_w(registryPath), ppszPath);
3950 *ppszPath = NULL;
3952 /* check if folder has parent */
3953 dwSize = sizeof(parentGuid);
3954 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, registryPath, L"ParentFolder", RRF_RT_REG_SZ, &dwType, parentGuid, &dwSize));
3955 if(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) hr = S_FALSE;
3957 if(hr == S_OK)
3959 /* get parent's known folder path (recursive) */
3960 hr = get_known_folder_registry_path(NULL, parentGuid, &parentRegistryPath);
3961 if(FAILED(hr)) return hr;
3963 hr = get_known_folder_path(parentGuid, parentRegistryPath, &parentPath);
3964 if(FAILED(hr)) {
3965 heap_free(parentRegistryPath);
3966 return hr;
3969 lstrcatW(path, parentPath);
3970 lstrcatW(path, L"\\");
3972 heap_free(parentRegistryPath);
3973 heap_free(parentPath);
3976 /* check, if folder was redirected */
3977 if(SUCCEEDED(hr))
3978 hr = get_known_folder_dword(registryPath, L"Category", &category);
3980 if(SUCCEEDED(hr))
3982 if(category == KF_CATEGORY_COMMON)
3983 hRedirectionRootKey = HKEY_LOCAL_MACHINE;
3984 else if(category == KF_CATEGORY_PERUSER)
3985 hRedirectionRootKey = HKEY_CURRENT_USER;
3987 if(hRedirectionRootKey)
3989 hr = HRESULT_FROM_WIN32(RegGetValueW(hRedirectionRootKey, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders", sFolderId, RRF_RT_REG_SZ, NULL, NULL, &dwSize));
3991 if(SUCCEEDED(hr))
3993 *ppszPath = CoTaskMemAlloc(dwSize+(lstrlenW(path)+1)*sizeof(WCHAR));
3994 if(!*ppszPath) hr = E_OUTOFMEMORY;
3997 if(SUCCEEDED(hr))
3999 lstrcpyW(*ppszPath, path);
4000 hr = HRESULT_FROM_WIN32(RegGetValueW(hRedirectionRootKey, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders", sFolderId, RRF_RT_REG_SZ, NULL, *ppszPath + lstrlenW(path), &dwSize));
4004 if(!*ppszPath)
4006 /* no redirection, use previous way - read the relative path from folder definition */
4007 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, registryPath, L"RelativePath", RRF_RT_REG_SZ, &dwType, NULL, &dwSize));
4009 if(SUCCEEDED(hr))
4011 *ppszPath = CoTaskMemAlloc(dwSize+(lstrlenW(path)+1)*sizeof(WCHAR));
4012 if(!*ppszPath) hr = E_OUTOFMEMORY;
4015 if(SUCCEEDED(hr))
4017 lstrcpyW(*ppszPath, path);
4018 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, registryPath, L"RelativePath", RRF_RT_REG_SZ, &dwType, *ppszPath + lstrlenW(path), &dwSize));
4023 TRACE("returning path: %s\n", debugstr_w(*ppszPath));
4024 return hr;
4027 static HRESULT get_known_folder_path_by_id(
4028 REFKNOWNFOLDERID folderId,
4029 LPWSTR lpRegistryPath,
4030 DWORD dwFlags,
4031 LPWSTR *ppszPath)
4033 HRESULT hr = E_FAIL;
4034 WCHAR sGuid[39];
4035 DWORD dwAttributes;
4037 TRACE("(%s, %s, 0x%08lx, %p)\n", debugstr_guid(folderId), debugstr_w(lpRegistryPath), dwFlags, ppszPath);
4039 /* if this is registry-registered known folder, get path from registry */
4040 if(lpRegistryPath)
4042 StringFromGUID2(folderId, sGuid, ARRAY_SIZE(sGuid));
4044 hr = get_known_folder_path(sGuid, lpRegistryPath, ppszPath);
4046 /* in other case, use older way */
4048 if(FAILED(hr))
4049 hr = SHGetKnownFolderPath( folderId, dwFlags, NULL, ppszPath );
4051 if (FAILED(hr)) return hr;
4053 /* check if known folder really exists */
4054 dwAttributes = GetFileAttributesW(*ppszPath);
4055 if(dwAttributes == INVALID_FILE_ATTRIBUTES || !(dwAttributes & FILE_ATTRIBUTE_DIRECTORY) )
4057 TRACE("directory %s not found\n", debugstr_w(*ppszPath));
4058 CoTaskMemFree(*ppszPath);
4059 *ppszPath = NULL;
4060 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
4063 return hr;
4066 static HRESULT WINAPI knownfolder_GetPath(
4067 IKnownFolder *iface,
4068 DWORD dwFlags,
4069 LPWSTR *ppszPath)
4071 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
4072 TRACE("(%p, 0x%08lx, %p)\n", knownfolder, dwFlags, ppszPath);
4074 return get_known_folder_path_by_id(&knownfolder->id, knownfolder->registryPath, dwFlags, ppszPath);
4077 static HRESULT WINAPI knownfolder_SetPath(
4078 IKnownFolder *iface,
4079 DWORD dwFlags,
4080 LPCWSTR pszPath)
4082 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
4083 HRESULT hr = S_OK;
4085 TRACE("(%p, 0x%08lx, %s)\n", knownfolder, dwFlags, debugstr_w(pszPath));
4087 /* check if the known folder is registered */
4088 if(!knownfolder->registryPath)
4089 hr = E_FAIL;
4091 if(SUCCEEDED(hr))
4092 hr = redirect_known_folder(&knownfolder->id, NULL, 0, pszPath, 0, NULL, NULL);
4094 return hr;
4097 static HRESULT WINAPI knownfolder_GetIDList(
4098 IKnownFolder *iface,
4099 DWORD flags,
4100 PIDLIST_ABSOLUTE *ppidl)
4102 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
4103 TRACE("(%p, 0x%08lx, %p)\n", knownfolder, flags, ppidl);
4104 return SHGetKnownFolderIDList(&knownfolder->id, flags, NULL, ppidl);
4107 static HRESULT WINAPI knownfolder_GetFolderType(
4108 IKnownFolder *iface,
4109 FOLDERTYPEID *pftid)
4111 FIXME("%p\n", pftid);
4112 return E_NOTIMPL;
4115 static HRESULT WINAPI knownfolder_GetRedirectionCapabilities(
4116 IKnownFolder *iface,
4117 KF_REDIRECTION_CAPABILITIES *pCapabilities)
4119 FIXME("%p stub\n", pCapabilities);
4120 if(!pCapabilities) return E_INVALIDARG;
4121 *pCapabilities = KF_REDIRECTION_CAPABILITIES_DENY_ALL;
4122 return S_OK;
4125 static HRESULT WINAPI knownfolder_GetFolderDefinition(
4126 IKnownFolder *iface,
4127 KNOWNFOLDER_DEFINITION *pKFD)
4129 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
4130 HRESULT hr;
4131 DWORD dwSize;
4132 WCHAR parentGuid[39];
4133 TRACE("(%p, %p)\n", knownfolder, pKFD);
4135 if(!pKFD) return E_INVALIDARG;
4137 ZeroMemory(pKFD, sizeof(*pKFD));
4139 /* required fields */
4140 hr = get_known_folder_dword(knownfolder->registryPath, L"Category", (DWORD *)&pKFD->category);
4141 if(FAILED(hr))
4142 return hr;
4144 hr = get_known_folder_wstr(knownfolder->registryPath, L"Name", &pKFD->pszName);
4145 if(FAILED(hr))
4146 return hr;
4148 /* optional fields */
4149 dwSize = sizeof(parentGuid);
4150 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, knownfolder->registryPath, L"ParentFolder",
4151 RRF_RT_REG_SZ, NULL, parentGuid, &dwSize));
4152 if(SUCCEEDED(hr))
4154 hr = IIDFromString(parentGuid, &pKFD->fidParent);
4155 if(FAILED(hr))
4156 return hr;
4159 get_known_folder_dword(knownfolder->registryPath, L"Attributes", &pKFD->dwAttributes);
4161 get_known_folder_wstr(knownfolder->registryPath, L"RelativePath", &pKFD->pszRelativePath);
4163 get_known_folder_wstr(knownfolder->registryPath, L"ParsingName", &pKFD->pszParsingName);
4165 return S_OK;
4168 static const struct IKnownFolderVtbl knownfolder_vtbl =
4170 knownfolder_QueryInterface,
4171 knownfolder_AddRef,
4172 knownfolder_Release,
4173 knownfolder_GetId,
4174 knownfolder_GetCategory,
4175 knownfolder_GetShellItem,
4176 knownfolder_GetPath,
4177 knownfolder_SetPath,
4178 knownfolder_GetIDList,
4179 knownfolder_GetFolderType,
4180 knownfolder_GetRedirectionCapabilities,
4181 knownfolder_GetFolderDefinition
4184 static HRESULT knownfolder_create( struct knownfolder **knownfolder )
4186 struct knownfolder *kf;
4188 kf = heap_alloc( sizeof(*kf) );
4189 if (!kf) return E_OUTOFMEMORY;
4191 kf->IKnownFolder_iface.lpVtbl = &knownfolder_vtbl;
4192 kf->refs = 1;
4193 memset( &kf->id, 0, sizeof(kf->id) );
4194 kf->registryPath = NULL;
4196 *knownfolder = kf;
4198 TRACE("returning iface %p\n", &kf->IKnownFolder_iface);
4199 return S_OK;
4202 struct foldermanager
4204 IKnownFolderManager IKnownFolderManager_iface;
4205 LONG refs;
4206 UINT num_ids;
4207 KNOWNFOLDERID *ids;
4210 static inline struct foldermanager *impl_from_IKnownFolderManager( IKnownFolderManager *iface )
4212 return CONTAINING_RECORD( iface, struct foldermanager, IKnownFolderManager_iface );
4215 static ULONG WINAPI foldermanager_AddRef(
4216 IKnownFolderManager *iface )
4218 struct foldermanager *foldermanager = impl_from_IKnownFolderManager( iface );
4219 return InterlockedIncrement( &foldermanager->refs );
4222 static ULONG WINAPI foldermanager_Release(
4223 IKnownFolderManager *iface )
4225 struct foldermanager *foldermanager = impl_from_IKnownFolderManager( iface );
4226 LONG refs = InterlockedDecrement( &foldermanager->refs );
4227 if (!refs)
4229 TRACE("destroying %p\n", foldermanager);
4230 heap_free( foldermanager->ids );
4231 heap_free( foldermanager );
4233 return refs;
4236 static HRESULT WINAPI foldermanager_QueryInterface(
4237 IKnownFolderManager *iface,
4238 REFIID riid,
4239 void **ppv )
4241 struct foldermanager *This = impl_from_IKnownFolderManager( iface );
4243 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppv );
4245 *ppv = NULL;
4246 if ( IsEqualGUID( riid, &IID_IKnownFolderManager ) ||
4247 IsEqualGUID( riid, &IID_IUnknown ) )
4249 *ppv = iface;
4251 else if ( IsEqualGUID( riid, &IID_IMarshal ) )
4253 TRACE("IID_IMarshal returning NULL.\n");
4254 return E_NOINTERFACE;
4256 else
4258 FIXME("interface %s not implemented\n", debugstr_guid(riid));
4259 return E_NOINTERFACE;
4261 IKnownFolderManager_AddRef( iface );
4262 return S_OK;
4265 static HRESULT WINAPI foldermanager_FolderIdFromCsidl(
4266 IKnownFolderManager *iface,
4267 int nCsidl,
4268 KNOWNFOLDERID *pfid)
4270 TRACE("%d, %p\n", nCsidl, pfid);
4272 if (nCsidl >= ARRAY_SIZE(CSIDL_Data))
4273 return E_INVALIDARG;
4274 *pfid = *CSIDL_Data[nCsidl].id;
4275 return S_OK;
4278 static HRESULT WINAPI foldermanager_FolderIdToCsidl(
4279 IKnownFolderManager *iface,
4280 REFKNOWNFOLDERID rfid,
4281 int *pnCsidl)
4283 int csidl;
4285 TRACE("%s, %p\n", debugstr_guid(rfid), pnCsidl);
4287 csidl = csidl_from_id( rfid );
4288 if (csidl == -1) return E_INVALIDARG;
4289 *pnCsidl = csidl;
4290 return S_OK;
4293 static HRESULT WINAPI foldermanager_GetFolderIds(
4294 IKnownFolderManager *iface,
4295 KNOWNFOLDERID **ppKFId,
4296 UINT *pCount)
4298 struct foldermanager *fm = impl_from_IKnownFolderManager( iface );
4300 TRACE("%p, %p\n", ppKFId, pCount);
4302 *ppKFId = CoTaskMemAlloc(fm->num_ids * sizeof(KNOWNFOLDERID));
4303 memcpy(*ppKFId, fm->ids, fm->num_ids * sizeof(KNOWNFOLDERID));
4304 *pCount = fm->num_ids;
4305 return S_OK;
4308 static BOOL is_knownfolder( struct foldermanager *fm, const KNOWNFOLDERID *id )
4310 UINT i;
4311 HRESULT hr;
4312 LPWSTR registryPath = NULL;
4313 HKEY hKey;
4315 /* TODO: move all entries from "CSIDL_Data" static array to registry known folder descriptions */
4316 for (i = 0; i < fm->num_ids; i++)
4317 if (IsEqualGUID( &fm->ids[i], id )) return TRUE;
4319 hr = get_known_folder_registry_path(id, NULL, &registryPath);
4320 if(SUCCEEDED(hr))
4322 hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, registryPath, 0, 0, &hKey));
4323 heap_free(registryPath);
4326 if(SUCCEEDED(hr))
4328 hr = S_OK;
4329 RegCloseKey(hKey);
4332 return hr == S_OK;
4335 static HRESULT WINAPI foldermanager_GetFolder(
4336 IKnownFolderManager *iface,
4337 REFKNOWNFOLDERID rfid,
4338 IKnownFolder **ppkf)
4340 struct foldermanager *fm = impl_from_IKnownFolderManager( iface );
4341 struct knownfolder *kf;
4342 HRESULT hr;
4344 TRACE("%s, %p\n", debugstr_guid(rfid), ppkf);
4346 if (!is_knownfolder( fm, rfid ))
4348 WARN("unknown folder\n");
4349 return E_INVALIDARG;
4351 hr = knownfolder_create( &kf );
4352 if (SUCCEEDED( hr ))
4354 hr = knownfolder_set_id( kf, rfid );
4355 *ppkf = &kf->IKnownFolder_iface;
4357 else
4358 *ppkf = NULL;
4360 return hr;
4363 static HRESULT WINAPI foldermanager_GetFolderByName(
4364 IKnownFolderManager *iface,
4365 LPCWSTR pszCanonicalName,
4366 IKnownFolder **ppkf)
4368 struct foldermanager *fm = impl_from_IKnownFolderManager( iface );
4369 struct knownfolder *kf;
4370 BOOL found = FALSE;
4371 HRESULT hr;
4372 UINT i;
4374 TRACE( "%s, %p\n", debugstr_w(pszCanonicalName), ppkf );
4376 for (i = 0; i < fm->num_ids; i++)
4378 WCHAR *path, *name;
4379 hr = get_known_folder_registry_path( &fm->ids[i], NULL, &path );
4380 if (FAILED( hr )) return hr;
4382 hr = get_known_folder_wstr( path, L"Name", &name );
4383 heap_free( path );
4384 if (FAILED( hr )) return hr;
4386 found = !wcsicmp( pszCanonicalName, name );
4387 CoTaskMemFree( name );
4388 if (found) break;
4391 if (found)
4393 hr = knownfolder_create( &kf );
4394 if (FAILED( hr )) return hr;
4396 hr = knownfolder_set_id( kf, &fm->ids[i] );
4397 if (FAILED( hr ))
4399 IKnownFolder_Release( &kf->IKnownFolder_iface );
4400 return hr;
4402 *ppkf = &kf->IKnownFolder_iface;
4404 else
4406 hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
4407 *ppkf = NULL;
4410 return hr;
4413 static HRESULT register_folder(const KNOWNFOLDERID *rfid, const KNOWNFOLDER_DEFINITION *pKFD)
4415 HRESULT hr;
4416 HKEY hKey = NULL;
4417 DWORD dwDisp;
4418 LPWSTR registryPath = NULL;
4420 hr = get_known_folder_registry_path(rfid, NULL, &registryPath);
4421 TRACE("registry path: %s\n", debugstr_w(registryPath));
4423 if(SUCCEEDED(hr))
4424 hr = HRESULT_FROM_WIN32(RegCreateKeyExW(HKEY_LOCAL_MACHINE, registryPath, 0, NULL, 0, KEY_WRITE, 0, &hKey, &dwDisp));
4426 if(SUCCEEDED(hr))
4428 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"Category", 0, REG_DWORD, (LPBYTE)&pKFD->category, sizeof(pKFD->category)));
4430 if(SUCCEEDED(hr) && pKFD->dwAttributes != 0)
4431 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"Attributes", 0, REG_DWORD, (LPBYTE)&pKFD->dwAttributes, sizeof(pKFD->dwAttributes)));
4433 if(SUCCEEDED(hr))
4434 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"Name", 0, REG_SZ, (LPBYTE)pKFD->pszName, (lstrlenW(pKFD->pszName)+1)*sizeof(WCHAR) ));
4436 if(SUCCEEDED(hr) && pKFD->pszParsingName)
4437 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"ParsingName", 0, REG_SZ, (LPBYTE)pKFD->pszParsingName, (lstrlenW(pKFD->pszParsingName)+1)*sizeof(WCHAR) ));
4439 if(SUCCEEDED(hr) && !IsEqualGUID(&pKFD->fidParent, &GUID_NULL))
4441 WCHAR sParentGuid[39];
4442 StringFromGUID2(&pKFD->fidParent, sParentGuid, ARRAY_SIZE(sParentGuid));
4444 /* this known folder has parent folder */
4445 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"ParentFolder", 0, REG_SZ, (LPBYTE)sParentGuid, sizeof(sParentGuid)));
4448 if(SUCCEEDED(hr) && pKFD->category != KF_CATEGORY_VIRTUAL && pKFD->pszRelativePath)
4449 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"RelativePath", 0, REG_SZ, (LPBYTE)pKFD->pszRelativePath, (lstrlenW(pKFD->pszRelativePath)+1)*sizeof(WCHAR) ));
4451 RegCloseKey(hKey);
4453 if(FAILED(hr))
4454 SHDeleteKeyW(HKEY_LOCAL_MACHINE, registryPath);
4457 heap_free(registryPath);
4458 return hr;
4461 static HRESULT WINAPI foldermanager_RegisterFolder(
4462 IKnownFolderManager *iface,
4463 REFKNOWNFOLDERID rfid,
4464 KNOWNFOLDER_DEFINITION const *pKFD)
4466 TRACE("(%p, %s, %p)\n", iface, debugstr_guid(rfid), pKFD);
4467 return register_folder(rfid, pKFD);
4470 static HRESULT WINAPI foldermanager_UnregisterFolder(
4471 IKnownFolderManager *iface,
4472 REFKNOWNFOLDERID rfid)
4474 HRESULT hr;
4475 LPWSTR registryPath = NULL;
4476 TRACE("(%p, %s)\n", iface, debugstr_guid(rfid));
4478 hr = get_known_folder_registry_path(rfid, NULL, &registryPath);
4480 if(SUCCEEDED(hr))
4481 hr = HRESULT_FROM_WIN32(SHDeleteKeyW(HKEY_LOCAL_MACHINE, registryPath));
4483 heap_free(registryPath);
4484 return hr;
4487 static HRESULT WINAPI foldermanager_FindFolderFromPath(
4488 IKnownFolderManager *iface,
4489 LPCWSTR pszPath,
4490 FFFP_MODE mode,
4491 IKnownFolder **ppkf)
4493 FIXME("%s, 0x%08x, %p\n", debugstr_w(pszPath), mode, ppkf);
4494 return E_NOTIMPL;
4497 static HRESULT WINAPI foldermanager_FindFolderFromIDList(
4498 IKnownFolderManager *iface,
4499 PCIDLIST_ABSOLUTE pidl,
4500 IKnownFolder **ppkf)
4502 FIXME("%p, %p\n", pidl, ppkf);
4503 return E_NOTIMPL;
4506 static HRESULT WINAPI foldermanager_Redirect(
4507 IKnownFolderManager *iface,
4508 REFKNOWNFOLDERID rfid,
4509 HWND hwnd,
4510 KF_REDIRECT_FLAGS flags,
4511 LPCWSTR pszTargetPath,
4512 UINT cFolders,
4513 KNOWNFOLDERID const *pExclusion,
4514 LPWSTR *ppszError)
4516 return redirect_known_folder(rfid, hwnd, flags, pszTargetPath, cFolders, pExclusion, ppszError);
4519 static const struct IKnownFolderManagerVtbl foldermanager_vtbl =
4521 foldermanager_QueryInterface,
4522 foldermanager_AddRef,
4523 foldermanager_Release,
4524 foldermanager_FolderIdFromCsidl,
4525 foldermanager_FolderIdToCsidl,
4526 foldermanager_GetFolderIds,
4527 foldermanager_GetFolder,
4528 foldermanager_GetFolderByName,
4529 foldermanager_RegisterFolder,
4530 foldermanager_UnregisterFolder,
4531 foldermanager_FindFolderFromPath,
4532 foldermanager_FindFolderFromIDList,
4533 foldermanager_Redirect
4536 static HRESULT foldermanager_create( void **ppv )
4538 UINT i, j;
4539 struct foldermanager *fm;
4541 fm = heap_alloc( sizeof(*fm) );
4542 if (!fm) return E_OUTOFMEMORY;
4544 fm->IKnownFolderManager_iface.lpVtbl = &foldermanager_vtbl;
4545 fm->refs = 1;
4546 fm->num_ids = 0;
4548 for (i = 0; i < ARRAY_SIZE(CSIDL_Data); i++)
4550 if (!IsEqualGUID( CSIDL_Data[i].id, &GUID_NULL )) fm->num_ids++;
4552 fm->ids = heap_alloc( fm->num_ids * sizeof(KNOWNFOLDERID) );
4553 if (!fm->ids)
4555 heap_free( fm );
4556 return E_OUTOFMEMORY;
4558 for (i = j = 0; i < ARRAY_SIZE(CSIDL_Data); i++)
4560 if (!IsEqualGUID( CSIDL_Data[i].id, &GUID_NULL ))
4562 fm->ids[j] = *CSIDL_Data[i].id;
4563 j++;
4566 TRACE("found %u known folders\n", fm->num_ids);
4567 *ppv = &fm->IKnownFolderManager_iface;
4569 TRACE("returning iface %p\n", *ppv);
4570 return S_OK;
4573 HRESULT WINAPI KnownFolderManager_Constructor( IUnknown *punk, REFIID riid, void **ppv )
4575 TRACE("%p, %s, %p\n", punk, debugstr_guid(riid), ppv);
4577 if (!ppv)
4578 return E_POINTER;
4579 if (punk)
4580 return CLASS_E_NOAGGREGATION;
4582 return foldermanager_create( ppv );
4585 HRESULT WINAPI SHGetKnownFolderIDList(REFKNOWNFOLDERID rfid, DWORD flags, HANDLE token, PIDLIST_ABSOLUTE *pidl)
4587 TRACE("%s, 0x%08lx, %p, %p\n", debugstr_guid(rfid), flags, token, pidl);
4589 if (!pidl)
4590 return E_INVALIDARG;
4592 if (flags)
4593 FIXME("unsupported flags: 0x%08lx\n", flags);
4595 if (token)
4596 FIXME("user token is not used.\n");
4598 *pidl = NULL;
4599 if (IsEqualIID(rfid, &FOLDERID_Desktop))
4600 *pidl = _ILCreateDesktop();
4601 else if (IsEqualIID(rfid, &FOLDERID_RecycleBinFolder))
4602 *pidl = _ILCreateBitBucket();
4603 else if (IsEqualIID(rfid, &FOLDERID_ComputerFolder))
4604 *pidl = _ILCreateMyComputer();
4605 else if (IsEqualIID(rfid, &FOLDERID_PrintersFolder))
4606 *pidl = _ILCreatePrinters();
4607 else if (IsEqualIID(rfid, &FOLDERID_ControlPanelFolder))
4608 *pidl = _ILCreateControlPanel();
4609 else if (IsEqualIID(rfid, &FOLDERID_NetworkFolder))
4610 *pidl = _ILCreateNetwork();
4611 else if (IsEqualIID(rfid, &FOLDERID_Documents))
4612 *pidl = _ILCreateMyDocuments();
4613 else
4615 DWORD attributes = 0;
4616 WCHAR *pathW;
4617 HRESULT hr;
4619 hr = SHGetKnownFolderPath(rfid, flags, token, &pathW);
4620 if (FAILED(hr))
4621 return hr;
4623 hr = SHILCreateFromPathW(pathW, pidl, &attributes);
4624 CoTaskMemFree(pathW);
4625 return hr;
4628 return *pidl ? S_OK : E_FAIL;
4631 HRESULT WINAPI SHGetKnownFolderItem(REFKNOWNFOLDERID rfid, KNOWN_FOLDER_FLAG flags, HANDLE hToken,
4632 REFIID riid, void **ppv)
4634 PIDLIST_ABSOLUTE pidl;
4635 HRESULT hr;
4637 TRACE("%s, 0x%08x, %p, %s, %p\n", debugstr_guid(rfid), flags, hToken, debugstr_guid(riid), ppv);
4639 hr = SHGetKnownFolderIDList(rfid, flags, hToken, &pidl);
4640 if (FAILED(hr))
4642 *ppv = NULL;
4643 return hr;
4646 hr = SHCreateItemFromIDList(pidl, riid, ppv);
4647 CoTaskMemFree(pidl);
4648 return hr;
4651 static void register_system_knownfolders(void)
4653 int i;
4655 for (i = 0; i < ARRAY_SIZE(CSIDL_Data); ++i)
4657 const CSIDL_DATA *folder = &CSIDL_Data[i];
4658 if(folder->name){
4659 KNOWNFOLDER_DEFINITION kfd;
4661 /* register_folder won't modify kfd, so cast away const instead of
4662 * reallocating */
4663 kfd.category = folder->category;
4664 kfd.pszName = (WCHAR *)folder->name;
4665 kfd.pszDescription = NULL;
4666 kfd.fidParent = folder->parent ? *folder->parent : GUID_NULL;
4667 kfd.pszRelativePath = (WCHAR *)folder->path;
4668 kfd.pszParsingName = (WCHAR *)folder->parsing;
4669 kfd.pszTooltip = NULL;
4670 kfd.pszLocalizedName = NULL;
4671 kfd.pszIcon = NULL;
4672 kfd.pszSecurity = NULL;
4673 kfd.dwAttributes = folder->attributes;
4674 kfd.kfdFlags = folder->flags;
4675 kfd.ftidType = folder->typeid ? *folder->typeid : GUID_NULL;
4676 register_folder(folder->id, &kfd);
4681 HRESULT SHELL_RegisterShellFolders(void)
4683 HRESULT hr;
4685 hr = _SHRegisterUserShellFolders(TRUE);
4686 if (SUCCEEDED(hr))
4687 hr = _SHRegisterUserShellFolders(FALSE);
4688 if (SUCCEEDED(hr))
4689 hr = _SHRegisterCommonShellFolders();
4690 if (SUCCEEDED(hr))
4691 hr = create_extra_folders();
4692 if (SUCCEEDED(hr))
4693 hr = set_folder_attributes();
4694 if (SUCCEEDED(hr))
4695 register_system_knownfolders();
4696 return hr;