mf/tests: Clobber the alignment and bytes per second, to test if the DMO fixes it.
[wine.git] / dlls / shell32 / shellpath.c
blob7fbded5743bc13bcaddbc6faddd258a31775b762
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 : 0xbf;
692 TRACE("(%s,%p,0x%08lx)\n", debugstr_a(path), dirs, flags);
694 if (flags & PRF_VERIFYEXISTS)
696 if (PathFindOnPathExA(path, dirs, dwWhich))
698 if (!PathIsFileSpecA(path)) GetFullPathNameA(path, MAX_PATH, path, NULL);
699 return TRUE;
701 if (!is_file_spec)
703 GetFullPathNameA(path, MAX_PATH, path, NULL);
704 if (PathFileExistsDefExtA(path, dwWhich))
705 return TRUE;
707 SetLastError(ERROR_FILE_NOT_FOUND);
708 return FALSE;
711 if (is_file_spec)
713 SetLastError(ERROR_FILE_NOT_FOUND);
714 return FALSE;
717 GetFullPathNameA(path, MAX_PATH, path, NULL);
719 return TRUE;
722 static BOOL PathResolveW(WCHAR *path, const WCHAR **dirs, DWORD flags)
724 BOOL is_file_spec = PathIsFileSpecW(path);
725 DWORD dwWhich = flags & PRF_DONTFINDLNK ? 0xf : 0xbf;
727 TRACE("(%s,%p,0x%08lx)\n", debugstr_w(path), dirs, flags);
729 if (flags & PRF_VERIFYEXISTS)
731 if (PathFindOnPathExW(path, dirs, dwWhich))
733 if (!PathIsFileSpecW(path)) GetFullPathNameW(path, MAX_PATH, path, NULL);
734 return TRUE;
736 if (!is_file_spec)
738 GetFullPathNameW(path, MAX_PATH, path, NULL);
739 if (PathFileExistsDefExtW(path, dwWhich))
740 return TRUE;
742 SetLastError(ERROR_FILE_NOT_FOUND);
743 return FALSE;
746 if (is_file_spec)
748 SetLastError(ERROR_FILE_NOT_FOUND);
749 return FALSE;
752 GetFullPathNameW(path, MAX_PATH, path, NULL);
754 return TRUE;
757 /*************************************************************************
758 * PathResolve [SHELL32.51]
760 BOOL WINAPI PathResolveAW(void *path, const void **paths, DWORD flags)
762 if (SHELL_OsIsUnicode())
763 return PathResolveW(path, (const WCHAR **)paths, flags);
764 else
765 return PathResolveA(path, (const char **)paths, flags);
768 /*************************************************************************
769 * PathProcessCommandA
771 static LONG PathProcessCommandA (
772 LPCSTR lpszPath,
773 LPSTR lpszBuff,
774 DWORD dwBuffSize,
775 DWORD dwFlags)
777 FIXME("%s %p 0x%04lx 0x%04lx stub\n",
778 lpszPath, lpszBuff, dwBuffSize, dwFlags);
779 if(!lpszPath) return -1;
780 if(lpszBuff) strcpy(lpszBuff, lpszPath);
781 return strlen(lpszPath);
784 /*************************************************************************
785 * PathProcessCommandW
787 static LONG PathProcessCommandW (
788 LPCWSTR lpszPath,
789 LPWSTR lpszBuff,
790 DWORD dwBuffSize,
791 DWORD dwFlags)
793 FIXME("(%s, %p, 0x%04lx, 0x%04lx) stub\n",
794 debugstr_w(lpszPath), lpszBuff, dwBuffSize, dwFlags);
795 if(!lpszPath) return -1;
796 if(lpszBuff) lstrcpyW(lpszBuff, lpszPath);
797 return lstrlenW(lpszPath);
800 /*************************************************************************
801 * PathProcessCommand (SHELL32.653)
803 LONG WINAPI PathProcessCommandAW (
804 LPCVOID lpszPath,
805 LPVOID lpszBuff,
806 DWORD dwBuffSize,
807 DWORD dwFlags)
809 if (SHELL_OsIsUnicode())
810 return PathProcessCommandW(lpszPath, lpszBuff, dwBuffSize, dwFlags);
811 return PathProcessCommandA(lpszPath, lpszBuff, dwBuffSize, dwFlags);
815 ########## special ##########
818 /*************************************************************************
819 * PathSetDlgItemPath (SHELL32.48)
821 VOID WINAPI PathSetDlgItemPathAW(HWND hDlg, int id, LPCVOID pszPath)
823 if (SHELL_OsIsUnicode())
824 PathSetDlgItemPathW(hDlg, id, pszPath);
825 else
826 PathSetDlgItemPathA(hDlg, id, pszPath);
829 typedef enum _CSIDL_Type {
830 CSIDL_Type_User,
831 CSIDL_Type_AllUsers,
832 CSIDL_Type_CurrVer,
833 CSIDL_Type_Disallowed,
834 CSIDL_Type_NonExistent,
835 CSIDL_Type_WindowsPath,
836 CSIDL_Type_SystemPath,
837 CSIDL_Type_SystemX86Path,
838 CSIDL_Type_ProgramData,
839 } CSIDL_Type;
841 #define CSIDL_CONTACTS 0x0043
842 #define CSIDL_DOWNLOADS 0x0047
843 #define CSIDL_LINKS 0x004d
844 #define CSIDL_APPDATA_LOCALLOW 0x004e
845 #define CSIDL_SAVED_GAMES 0x0062
846 #define CSIDL_SEARCHES 0x0063
848 typedef struct
850 IApplicationDestinations IApplicationDestinations_iface;
851 LONG ref;
852 } IApplicationDestinationsImpl;
854 static inline IApplicationDestinationsImpl *impl_from_IApplicationDestinations( IApplicationDestinations *iface )
856 return CONTAINING_RECORD(iface, IApplicationDestinationsImpl, IApplicationDestinations_iface);
859 static HRESULT WINAPI ApplicationDestinations_QueryInterface(IApplicationDestinations *iface, REFIID riid,
860 LPVOID *ppv)
862 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
864 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppv);
866 if (ppv == NULL)
867 return E_POINTER;
869 if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IApplicationDestinations, riid))
871 *ppv = &This->IApplicationDestinations_iface;
872 IUnknown_AddRef((IUnknown*)*ppv);
874 TRACE("Returning IApplicationDestinations: %p\n", *ppv);
875 return S_OK;
878 *ppv = NULL;
879 FIXME("(%p)->(%s, %p) interface not supported.\n", This, debugstr_guid(riid), ppv);
881 return E_NOINTERFACE;
884 static ULONG WINAPI ApplicationDestinations_AddRef(IApplicationDestinations *iface)
886 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
887 ULONG ref = InterlockedIncrement(&This->ref);
889 TRACE("(%p), new refcount=%li\n", This, ref);
891 return ref;
894 static ULONG WINAPI ApplicationDestinations_Release(IApplicationDestinations *iface)
896 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
897 ULONG ref = InterlockedDecrement(&This->ref);
899 TRACE("(%p), new refcount=%li\n", This, ref);
901 if (ref == 0)
902 free(This);
904 return ref;
907 static HRESULT WINAPI ApplicationDestinations_SetAppID(IApplicationDestinations *iface, const WCHAR *appid)
909 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
911 FIXME("(%p, %s) stub!\n", This, debugstr_w(appid));
913 return E_NOTIMPL;
916 static HRESULT WINAPI ApplicationDestinations_RemoveDestination(IApplicationDestinations *iface, IUnknown *punk)
918 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
920 FIXME("(%p, %p) stub!\n", This, punk);
922 return E_NOTIMPL;
925 static HRESULT WINAPI ApplicationDestinations_RemoveAllDestinations(IApplicationDestinations *iface)
927 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
929 FIXME("(%p) stub!\n", This);
931 return E_NOTIMPL;
934 static const IApplicationDestinationsVtbl ApplicationDestinationsVtbl =
936 ApplicationDestinations_QueryInterface,
937 ApplicationDestinations_AddRef,
938 ApplicationDestinations_Release,
939 ApplicationDestinations_SetAppID,
940 ApplicationDestinations_RemoveDestination,
941 ApplicationDestinations_RemoveAllDestinations
944 HRESULT WINAPI ApplicationDestinations_Constructor(IUnknown *outer, REFIID riid, LPVOID *ppv)
946 IApplicationDestinationsImpl *This;
947 HRESULT hr;
949 TRACE("(%p, %s, %p)\n", outer, debugstr_guid(riid), ppv);
951 if (outer)
952 return CLASS_E_NOAGGREGATION;
954 if (!(This = SHAlloc(sizeof(*This))))
955 return E_OUTOFMEMORY;
957 This->IApplicationDestinations_iface.lpVtbl = &ApplicationDestinationsVtbl;
958 This->ref = 0;
960 hr = IUnknown_QueryInterface(&This->IApplicationDestinations_iface, riid, ppv);
961 if (FAILED(hr))
962 SHFree(This);
964 return hr;
967 typedef struct
969 IApplicationDocumentLists IApplicationDocumentLists_iface;
970 LONG ref;
971 } IApplicationDocumentListsImpl;
973 static inline IApplicationDocumentListsImpl *impl_from_IApplicationDocumentLists( IApplicationDocumentLists *iface )
975 return CONTAINING_RECORD(iface, IApplicationDocumentListsImpl, IApplicationDocumentLists_iface);
978 static HRESULT WINAPI ApplicationDocumentLists_QueryInterface(IApplicationDocumentLists *iface,
979 REFIID riid, LPVOID *ppv)
981 IApplicationDocumentListsImpl *This = impl_from_IApplicationDocumentLists(iface);
983 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppv);
985 if (ppv == NULL)
986 return E_POINTER;
988 if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IApplicationDocumentLists, riid))
990 *ppv = &This->IApplicationDocumentLists_iface;
991 IUnknown_AddRef((IUnknown*)*ppv);
993 TRACE("Returning IApplicationDocumentLists: %p\n", *ppv);
994 return S_OK;
997 *ppv = NULL;
998 FIXME("(%p)->(%s, %p) interface not supported.\n", This, debugstr_guid(riid), ppv);
1000 return E_NOINTERFACE;
1003 static ULONG WINAPI ApplicationDocumentLists_AddRef(IApplicationDocumentLists *iface)
1005 IApplicationDocumentListsImpl *This = impl_from_IApplicationDocumentLists(iface);
1006 ULONG ref = InterlockedIncrement(&This->ref);
1008 TRACE("(%p), new refcount=%li\n", This, ref);
1010 return ref;
1013 static ULONG WINAPI ApplicationDocumentLists_Release(IApplicationDocumentLists *iface)
1015 IApplicationDocumentListsImpl *This = impl_from_IApplicationDocumentLists(iface);
1016 ULONG ref = InterlockedDecrement(&This->ref);
1018 TRACE("(%p), new refcount=%li\n", This, ref);
1020 if (ref == 0)
1021 free(This);
1023 return ref;
1026 static HRESULT WINAPI ApplicationDocumentLists_SetAppID(IApplicationDocumentLists *iface,
1027 const WCHAR *appid)
1029 IApplicationDocumentListsImpl *This = impl_from_IApplicationDocumentLists(iface);
1031 FIXME("(%p, %s) stub!\n", This, debugstr_w(appid));
1033 return E_NOTIMPL;
1036 static HRESULT WINAPI ApplicationDocumentLists_GetList(IApplicationDocumentLists *iface,
1037 APPDOCLISTTYPE list_type, UINT item_count,
1038 REFIID riid, void **obj)
1040 IApplicationDocumentListsImpl *This = impl_from_IApplicationDocumentLists(iface);
1042 FIXME("(%p, %u, %u, %s, %p): stub\n", This, list_type, item_count, debugstr_guid(riid), obj);
1044 return E_NOTIMPL;
1047 static const IApplicationDocumentListsVtbl ApplicationDocumentListsVtbl =
1049 ApplicationDocumentLists_QueryInterface,
1050 ApplicationDocumentLists_AddRef,
1051 ApplicationDocumentLists_Release,
1052 ApplicationDocumentLists_SetAppID,
1053 ApplicationDocumentLists_GetList
1056 HRESULT WINAPI ApplicationDocumentLists_Constructor(IUnknown *outer, REFIID riid, LPVOID *ppv)
1058 IApplicationDocumentListsImpl *This;
1059 HRESULT hr;
1061 TRACE("(%p, %s, %p)\n", outer, debugstr_guid(riid), ppv);
1063 if (outer)
1064 return CLASS_E_NOAGGREGATION;
1066 if (!(This = SHAlloc(sizeof(*This))))
1067 return E_OUTOFMEMORY;
1069 This->IApplicationDocumentLists_iface.lpVtbl = &ApplicationDocumentListsVtbl;
1070 This->ref = 0;
1072 hr = IUnknown_QueryInterface(&This->IApplicationDocumentLists_iface, riid, ppv);
1073 if (FAILED(hr))
1074 SHFree(This);
1076 return hr;
1079 typedef struct
1081 const KNOWNFOLDERID *id;
1082 CSIDL_Type type;
1083 const WCHAR *value;
1084 const WCHAR *def_path; /* fallback string or resource ID */
1085 KF_CATEGORY category;
1086 const WCHAR *name;
1087 const KNOWNFOLDERID *parent;
1088 const WCHAR *path;
1089 const WCHAR *parsing;
1090 DWORD attributes;
1091 KF_DEFINITION_FLAGS flags;
1092 const FOLDERTYPEID *typeid;
1093 } CSIDL_DATA;
1095 static const CSIDL_DATA CSIDL_Data[] =
1097 { /* 0x00 - CSIDL_DESKTOP */
1098 .id = &FOLDERID_Desktop,
1099 .type = CSIDL_Type_User,
1100 .value = L"Desktop",
1101 .category = KF_CATEGORY_PERUSER,
1102 .name = L"Desktop",
1103 .path = L"Desktop",
1104 .attributes = FILE_ATTRIBUTE_READONLY,
1106 { /* 0x01 - CSIDL_INTERNET */
1107 .id = &FOLDERID_InternetFolder,
1108 .type = CSIDL_Type_Disallowed,
1109 .category = KF_CATEGORY_VIRTUAL,
1110 .name = L"InternetFolder",
1111 .parsing = L"::{871C5380-42A0-1069-A2EA-08002B30309D}",
1113 { /* 0x02 - CSIDL_PROGRAMS */
1114 .id = &FOLDERID_Programs,
1115 .type = CSIDL_Type_User,
1116 .value = L"Programs",
1117 .category = KF_CATEGORY_PERUSER,
1118 .name = L"Programs",
1119 .parent = &FOLDERID_StartMenu,
1120 .path = L"Programs",
1121 .attributes = FILE_ATTRIBUTE_READONLY,
1123 { /* 0x03 - CSIDL_CONTROLS (.CPL files) */
1124 .id = &FOLDERID_ControlPanelFolder,
1125 .type = CSIDL_Type_SystemPath,
1126 .category = KF_CATEGORY_VIRTUAL,
1127 .name = L"ControlPanelFolder",
1128 .path = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}",
1129 .parsing = L"::{26EE0668-A00A-44D7-9371-BEB064C98683}\\0",
1131 { /* 0x04 - CSIDL_PRINTERS */
1132 .id = &FOLDERID_PrintersFolder,
1133 .type = CSIDL_Type_SystemPath,
1134 .category = KF_CATEGORY_VIRTUAL,
1135 .name = L"PrintersFolder",
1136 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}",
1138 { /* 0x05 - CSIDL_PERSONAL */
1139 .id = &FOLDERID_Documents,
1140 .type = CSIDL_Type_User,
1141 .value = L"Personal",
1142 .category = KF_CATEGORY_PERUSER,
1143 .name = L"Personal",
1144 .parent = &FOLDERID_Profile,
1145 .path = L"Documents",
1146 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{FDD39AD0-238F-46AF-ADB4-6C85480369C7}",
1147 .attributes = FILE_ATTRIBUTE_READONLY,
1148 .flags = KFDF_ROAMABLE | KFDF_PRECREATE,
1150 { /* 0x06 - CSIDL_FAVORITES */
1151 .id = &FOLDERID_Favorites,
1152 .type = CSIDL_Type_User,
1153 .value = L"Favorites",
1154 .category = KF_CATEGORY_PERUSER,
1155 .name = L"Favorites",
1156 .path = L"Favorites",
1157 .attributes = FILE_ATTRIBUTE_READONLY,
1158 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1160 { /* 0x07 - CSIDL_STARTUP */
1161 .id = &FOLDERID_Startup,
1162 .type = CSIDL_Type_User,
1163 .value = L"StartUp",
1164 .category = KF_CATEGORY_PERUSER,
1165 .name = L"Startup",
1166 .parent = &FOLDERID_Programs,
1167 .path = L"StartUp",
1168 .attributes = FILE_ATTRIBUTE_READONLY,
1169 .flags = KFDF_PRECREATE,
1171 { /* 0x08 - CSIDL_RECENT */
1172 .id = &FOLDERID_Recent,
1173 .type = CSIDL_Type_User,
1174 .value = L"Recent",
1175 .category = KF_CATEGORY_PERUSER,
1176 .name = L"Recent",
1177 .parent = &FOLDERID_RoamingAppData,
1178 .path = L"Microsoft\\Windows\\Recent",
1179 .attributes = FILE_ATTRIBUTE_READONLY,
1180 .flags = KFDF_PRECREATE,
1182 { /* 0x09 - CSIDL_SENDTO */
1183 .id = &FOLDERID_SendTo,
1184 .type = CSIDL_Type_User,
1185 .value = L"SendTo",
1186 .category = KF_CATEGORY_PERUSER,
1187 .name = L"SendTo",
1188 .parent = &FOLDERID_RoamingAppData,
1189 .path = L"Microsoft\\Windows\\SendTo",
1190 .flags = KFDF_PRECREATE,
1192 { /* 0x0a - CSIDL_BITBUCKET - Recycle Bin */
1193 .id = &FOLDERID_RecycleBinFolder,
1194 .type = CSIDL_Type_Disallowed,
1195 .category = KF_CATEGORY_VIRTUAL,
1196 .name = L"RecycleBinFolder",
1197 .parsing = L"::{645FF040-5081-101B-9F08-00AA002F954E}",
1199 { /* 0x0b - CSIDL_STARTMENU */
1200 .id = &FOLDERID_StartMenu,
1201 .type = CSIDL_Type_User,
1202 .value = L"Start Menu",
1203 .category = KF_CATEGORY_PERUSER,
1204 .name = L"Start Menu",
1205 .parent = &FOLDERID_RoamingAppData,
1206 .path = L"Microsoft\\Windows\\Start Menu",
1207 .attributes = FILE_ATTRIBUTE_READONLY,
1208 .flags = KFDF_PRECREATE,
1210 { /* 0x0c - CSIDL_MYDOCUMENTS */
1211 .id = &GUID_NULL,
1212 .type = CSIDL_Type_Disallowed, /* matches WinXP--can't get its path */
1214 { /* 0x0d - CSIDL_MYMUSIC */
1215 .id = &FOLDERID_Music,
1216 .type = CSIDL_Type_User,
1217 .value = L"My Music",
1218 .category = KF_CATEGORY_PERUSER,
1219 .name = L"My Music",
1220 .parent = &FOLDERID_Profile,
1221 .path = L"Music",
1222 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{4BD8D571-6D19-48D3-BE97-422220080E43}",
1223 .attributes = FILE_ATTRIBUTE_READONLY,
1224 .flags = KFDF_ROAMABLE | KFDF_PRECREATE,
1226 { /* 0x0e - CSIDL_MYVIDEO */
1227 .id = &FOLDERID_Videos,
1228 .type = CSIDL_Type_User,
1229 .value = L"My Videos",
1230 .category = KF_CATEGORY_PERUSER,
1231 .name = L"My Video",
1232 .parent = &FOLDERID_Profile,
1233 .path = L"Videos",
1234 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{18989B1D-99B5-455B-841C-AB7C74E4DDFC}",
1235 .attributes = FILE_ATTRIBUTE_READONLY,
1236 .flags = KFDF_ROAMABLE | KFDF_PRECREATE,
1238 { /* 0x0f - unassigned */
1239 .id = &GUID_NULL,
1240 .type = CSIDL_Type_Disallowed,
1242 { /* 0x10 - CSIDL_DESKTOPDIRECTORY */
1243 .id = &FOLDERID_Desktop,
1244 .type = CSIDL_Type_User,
1245 .value = L"Desktop",
1246 .category = KF_CATEGORY_PERUSER,
1247 .name = L"Desktop",
1248 .parent = &FOLDERID_Profile,
1249 .path = L"Desktop",
1250 .attributes = FILE_ATTRIBUTE_READONLY,
1251 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1253 { /* 0x11 - CSIDL_DRIVES */
1254 .id = &FOLDERID_ComputerFolder,
1255 .type = CSIDL_Type_Disallowed,
1256 .category = KF_CATEGORY_VIRTUAL,
1257 .name = L"MyComputerFolder",
1258 .parsing = L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
1260 { /* 0x12 - CSIDL_NETWORK */
1261 .id = &FOLDERID_NetworkFolder,
1262 .type = CSIDL_Type_Disallowed,
1263 .category = KF_CATEGORY_VIRTUAL,
1264 .name = L"NetworkPlacesFolder",
1265 .parsing = L"::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}",
1267 { /* 0x13 - CSIDL_NETHOOD */
1268 .id = &FOLDERID_NetHood,
1269 .type = CSIDL_Type_User,
1270 .value = L"NetHood",
1271 .category = KF_CATEGORY_PERUSER,
1272 .name = L"NetHood",
1273 .parent = &FOLDERID_RoamingAppData,
1274 .path = L"Microsoft\\Windows\\Network Shortcuts",
1276 { /* 0x14 - CSIDL_FONTS */
1277 .id = &FOLDERID_Fonts,
1278 .type = CSIDL_Type_WindowsPath,
1279 .value = L"Fonts",
1280 .def_path = L"Fonts",
1281 .category = KF_CATEGORY_FIXED,
1282 .name = L"Fonts",
1283 .parent = &FOLDERID_Windows,
1284 .typeid = &FOLDERID_Windows
1286 { /* 0x15 - CSIDL_TEMPLATES */
1287 .id = &FOLDERID_Templates,
1288 .type = CSIDL_Type_User,
1289 .value = L"Templates",
1290 .category = KF_CATEGORY_PERUSER,
1291 .name = L"Templates",
1292 .parent = &FOLDERID_RoamingAppData,
1293 .path = L"Microsoft\\Windows\\Templates",
1295 { /* 0x16 - CSIDL_COMMON_STARTMENU */
1296 .id = &FOLDERID_CommonStartMenu,
1297 .type = CSIDL_Type_ProgramData,
1298 .value = L"Common Start Menu",
1299 .category = KF_CATEGORY_COMMON,
1300 .name = L"Common Start Menu",
1301 .parent = &FOLDERID_ProgramData,
1302 .path = L"Microsoft\\Windows\\Start Menu",
1303 .attributes = FILE_ATTRIBUTE_READONLY,
1305 { /* 0x17 - CSIDL_COMMON_PROGRAMS */
1306 .id = &FOLDERID_CommonPrograms,
1307 .type = CSIDL_Type_ProgramData,
1308 .value = L"Common Programs",
1309 .category = KF_CATEGORY_COMMON,
1310 .name = L"Common Programs",
1311 .parent = &FOLDERID_CommonStartMenu,
1312 .path = L"Programs",
1313 .attributes = FILE_ATTRIBUTE_READONLY,
1315 { /* 0x18 - CSIDL_COMMON_STARTUP */
1316 .id = &FOLDERID_CommonStartup,
1317 .type = CSIDL_Type_ProgramData,
1318 .value = L"Common StartUp",
1319 .category = KF_CATEGORY_COMMON,
1320 .name = L"Common Startup",
1321 .parent = &FOLDERID_CommonPrograms,
1322 .path = L"StartUp",
1323 .attributes = FILE_ATTRIBUTE_READONLY,
1324 .flags = KFDF_PRECREATE,
1326 { /* 0x19 - CSIDL_COMMON_DESKTOPDIRECTORY */
1327 .id = &FOLDERID_PublicDesktop,
1328 .type = CSIDL_Type_AllUsers,
1329 .value = L"Common Desktop",
1330 .category = KF_CATEGORY_COMMON,
1331 .name = L"Common Desktop",
1332 .parent = &FOLDERID_Public,
1333 .path = L"Desktop",
1334 .attributes = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN,
1335 .flags = KFDF_PRECREATE,
1337 { /* 0x1a - CSIDL_APPDATA */
1338 .id = &FOLDERID_RoamingAppData,
1339 .type = CSIDL_Type_User,
1340 .value = L"AppData",
1341 .category = KF_CATEGORY_PERUSER,
1342 .name = L"AppData",
1343 .parent = &FOLDERID_Profile,
1344 .path = L"AppData\\Roaming",
1346 { /* 0x1b - CSIDL_PRINTHOOD */
1347 .id = &FOLDERID_PrintHood,
1348 .type = CSIDL_Type_User,
1349 .value = L"PrintHood",
1350 .category = KF_CATEGORY_PERUSER,
1351 .name = L"PrintHood",
1352 .parent = &FOLDERID_RoamingAppData,
1353 .path = L"Microsoft\\Windows\\Printer Shortcuts",
1355 { /* 0x1c - CSIDL_LOCAL_APPDATA */
1356 .id = &FOLDERID_LocalAppData,
1357 .type = CSIDL_Type_User,
1358 .value = L"Local AppData",
1359 .category = KF_CATEGORY_PERUSER,
1360 .name = L"Local AppData",
1361 .parent = &FOLDERID_Profile,
1362 .path = L"AppData\\Local",
1363 .flags = KFDF_LOCAL_REDIRECT_ONLY | KFDF_PUBLISHEXPANDEDPATH,
1365 { /* 0x1d - CSIDL_ALTSTARTUP */
1366 .id = &GUID_NULL,
1367 .type = CSIDL_Type_NonExistent,
1369 { /* 0x1e - CSIDL_COMMON_ALTSTARTUP */
1370 .id = &GUID_NULL,
1371 .type = CSIDL_Type_NonExistent,
1373 { /* 0x1f - CSIDL_COMMON_FAVORITES */
1374 .id = &FOLDERID_Favorites,
1375 .type = CSIDL_Type_AllUsers,
1376 .value = L"Common Favorites",
1377 .category = KF_CATEGORY_PERUSER,
1378 .name = L"Favorites",
1379 .parent = &FOLDERID_Profile,
1380 .path = L"Favorites",
1381 .attributes = FILE_ATTRIBUTE_READONLY,
1382 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1384 { /* 0x20 - CSIDL_INTERNET_CACHE */
1385 .id = &FOLDERID_InternetCache,
1386 .type = CSIDL_Type_User,
1387 .value = L"Cache",
1388 .category = KF_CATEGORY_PERUSER,
1389 .name = L"Cache",
1390 .parent = &FOLDERID_LocalAppData,
1391 .path = L"Microsoft\\Windows\\INetCache",
1392 .flags = KFDF_LOCAL_REDIRECT_ONLY,
1394 { /* 0x21 - CSIDL_COOKIES */
1395 .id = &FOLDERID_Cookies,
1396 .type = CSIDL_Type_User,
1397 .value = L"Cookies",
1398 .category = KF_CATEGORY_PERUSER,
1399 .name = L"Cookies",
1400 .parent = &FOLDERID_LocalAppData,
1401 .path = L"Microsoft\\Windows\\INetCookies",
1403 { /* 0x22 - CSIDL_HISTORY */
1404 .id = &FOLDERID_History,
1405 .type = CSIDL_Type_User,
1406 .value = L"History",
1407 .category = KF_CATEGORY_PERUSER,
1408 .name = L"History",
1409 .parent = &FOLDERID_LocalAppData,
1410 .path = L"Microsoft\\Windows\\History",
1411 .flags = KFDF_LOCAL_REDIRECT_ONLY,
1413 { /* 0x23 - CSIDL_COMMON_APPDATA */
1414 .id = &FOLDERID_ProgramData,
1415 .type = CSIDL_Type_ProgramData,
1416 .value = L"Common AppData",
1417 .category = KF_CATEGORY_FIXED,
1418 .name = L"Common AppData",
1420 { /* 0x24 - CSIDL_WINDOWS */
1421 .id = &FOLDERID_Windows,
1422 .type = CSIDL_Type_WindowsPath,
1423 .category = KF_CATEGORY_FIXED,
1424 .name = L"Windows",
1426 { /* 0x25 - CSIDL_SYSTEM */
1427 .id = &FOLDERID_System,
1428 .type = CSIDL_Type_SystemPath,
1429 .category = KF_CATEGORY_FIXED,
1430 .name = L"System",
1432 { /* 0x26 - CSIDL_PROGRAM_FILES */
1433 .id = &FOLDERID_ProgramFiles,
1434 .type = CSIDL_Type_CurrVer,
1435 .value = L"ProgramFilesDir",
1436 .def_path = L"Program Files",
1437 .category = KF_CATEGORY_FIXED,
1438 .name = L"ProgramFiles",
1439 .attributes = FILE_ATTRIBUTE_READONLY,
1441 { /* 0x27 - CSIDL_MYPICTURES */
1442 .id = &FOLDERID_Pictures,
1443 .type = CSIDL_Type_User,
1444 .value = L"My Pictures",
1445 .category = KF_CATEGORY_PERUSER,
1446 .name = L"My Pictures",
1447 .parent = &FOLDERID_Profile,
1448 .path = L"Pictures",
1449 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{33E28130-4E1E-4676-835A-98395C3BC3BB}",
1450 .attributes = FILE_ATTRIBUTE_READONLY,
1451 .flags = KFDF_ROAMABLE | KFDF_PRECREATE,
1453 { /* 0x28 - CSIDL_PROFILE */
1454 .id = &FOLDERID_Profile,
1455 .type = CSIDL_Type_User,
1456 .category = KF_CATEGORY_FIXED,
1457 .name = L"Profile",
1459 { /* 0x29 - CSIDL_SYSTEMX86 */
1460 .id = &FOLDERID_SystemX86,
1461 .type = CSIDL_Type_SystemX86Path,
1462 .category = KF_CATEGORY_FIXED,
1463 .name = L"SystemX86",
1465 { /* 0x2a - CSIDL_PROGRAM_FILESX86 */
1466 .id = &FOLDERID_ProgramFilesX86,
1467 .type = CSIDL_Type_CurrVer,
1468 .value = L"ProgramFilesDir (x86)",
1469 .def_path = L"Program Files (x86)",
1470 .category = KF_CATEGORY_FIXED,
1471 .name = L"ProgramFilesX86",
1472 .attributes = FILE_ATTRIBUTE_READONLY,
1474 { /* 0x2b - CSIDL_PROGRAM_FILES_COMMON */
1475 .id = &FOLDERID_ProgramFilesCommon,
1476 .type = CSIDL_Type_CurrVer,
1477 .value = L"CommonFilesDir",
1478 .def_path = L"Program Files\\Common Files",
1479 .category = KF_CATEGORY_FIXED,
1480 .name = L"ProgramFilesCommon",
1482 { /* 0x2c - CSIDL_PROGRAM_FILES_COMMONX86 */
1483 .id = &FOLDERID_ProgramFilesCommonX86,
1484 .type = CSIDL_Type_CurrVer,
1485 .value = L"CommonFilesDir (x86)",
1486 .def_path = L"Program Files (x86)\\Common Files",
1487 .category = KF_CATEGORY_FIXED,
1488 .name = L"ProgramFilesCommonX86",
1490 { /* 0x2d - CSIDL_COMMON_TEMPLATES */
1491 .id = &FOLDERID_CommonTemplates,
1492 .type = CSIDL_Type_ProgramData,
1493 .value = L"Common Templates",
1494 .category = KF_CATEGORY_COMMON,
1495 .name = L"Common Templates",
1496 .parent = &FOLDERID_ProgramData,
1497 .path = L"Microsoft\\Windows\\Templates",
1499 { /* 0x2e - CSIDL_COMMON_DOCUMENTS */
1500 .id = &FOLDERID_PublicDocuments,
1501 .type = CSIDL_Type_AllUsers,
1502 .value = L"Common Documents",
1503 .category = KF_CATEGORY_COMMON,
1504 .name = L"Common Documents",
1505 .parent = &FOLDERID_Public,
1506 .path = L"Documents",
1507 .attributes = FILE_ATTRIBUTE_READONLY,
1508 .flags = KFDF_PRECREATE,
1510 { /* 0x2f - CSIDL_COMMON_ADMINTOOLS */
1511 .id = &FOLDERID_CommonAdminTools,
1512 .type = CSIDL_Type_ProgramData,
1513 .value = L"Common Administrative Tools",
1514 .category = KF_CATEGORY_COMMON,
1515 .name = L"Common Administrative Tools",
1516 .parent = &FOLDERID_CommonPrograms,
1517 .path = L"Administrative Tools",
1518 .attributes = FILE_ATTRIBUTE_READONLY,
1519 .flags = KFDF_PRECREATE,
1521 { /* 0x30 - CSIDL_ADMINTOOLS */
1522 .id = &FOLDERID_AdminTools,
1523 .type = CSIDL_Type_User,
1524 .value = L"Administrative Tools",
1525 .category = KF_CATEGORY_PERUSER,
1526 .name = L"Administrative Tools",
1527 .parent = &FOLDERID_Programs,
1528 .path = L"Administrative Tools",
1529 .attributes = FILE_ATTRIBUTE_READONLY,
1530 .flags = KFDF_PRECREATE,
1532 { /* 0x31 - CSIDL_CONNECTIONS */
1533 .id = &FOLDERID_ConnectionsFolder,
1534 .type = CSIDL_Type_Disallowed,
1535 .category = KF_CATEGORY_VIRTUAL,
1536 .name = L"ConnectionsFolder",
1537 .path = L"Administrative Tools",
1538 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}",
1540 { /* 0x32 - unassigned */
1541 .id = &GUID_NULL,
1542 .type = CSIDL_Type_Disallowed,
1544 { /* 0x33 - unassigned */
1545 .id = &GUID_NULL,
1546 .type = CSIDL_Type_Disallowed,
1548 { /* 0x34 - unassigned */
1549 .id = &GUID_NULL,
1550 .type = CSIDL_Type_Disallowed,
1552 { /* 0x35 - CSIDL_COMMON_MUSIC */
1553 .id = &FOLDERID_PublicMusic,
1554 .type = CSIDL_Type_AllUsers,
1555 .value = L"CommonMusic",
1556 .category = KF_CATEGORY_COMMON,
1557 .name = L"CommonMusic",
1558 .parent = &FOLDERID_Public,
1559 .path = L"Music",
1560 .attributes = FILE_ATTRIBUTE_READONLY,
1561 .flags = KFDF_PRECREATE,
1563 { /* 0x36 - CSIDL_COMMON_PICTURES */
1564 .id = &FOLDERID_PublicPictures,
1565 .type = CSIDL_Type_AllUsers,
1566 .value = L"CommonPictures",
1567 .category = KF_CATEGORY_COMMON,
1568 .name = L"CommonPictures",
1569 .parent = &FOLDERID_Public,
1570 .path = L"Pictures",
1571 .attributes = FILE_ATTRIBUTE_READONLY,
1572 .flags = KFDF_PRECREATE,
1574 { /* 0x37 - CSIDL_COMMON_VIDEO */
1575 .id = &FOLDERID_PublicVideos,
1576 .type = CSIDL_Type_AllUsers,
1577 .value = L"CommonVideo",
1578 .category = KF_CATEGORY_COMMON,
1579 .name = L"CommonVideo",
1580 .parent = &FOLDERID_Public,
1581 .path = L"Videos",
1582 .attributes = FILE_ATTRIBUTE_READONLY,
1583 .flags = KFDF_PRECREATE,
1585 { /* 0x38 - CSIDL_RESOURCES */
1586 .id = &FOLDERID_ResourceDir,
1587 .type = CSIDL_Type_WindowsPath,
1588 .def_path = L"Resources",
1589 .category = KF_CATEGORY_FIXED,
1590 .name = L"ResourceDir",
1592 { /* 0x39 - CSIDL_RESOURCES_LOCALIZED */
1593 .id = &FOLDERID_LocalizedResourcesDir,
1594 .type = CSIDL_Type_NonExistent,
1595 .category = KF_CATEGORY_FIXED,
1596 .name = L"LocalizedResourcesDir",
1598 { /* 0x3a - CSIDL_COMMON_OEM_LINKS */
1599 .id = &FOLDERID_CommonOEMLinks,
1600 .type = CSIDL_Type_ProgramData,
1601 .category = KF_CATEGORY_COMMON,
1602 .name = L"OEM Links",
1603 .parent = &FOLDERID_ProgramData,
1604 .path = L"OEM Links",
1606 { /* 0x3b - CSIDL_CDBURN_AREA */
1607 .id = &FOLDERID_CDBurning,
1608 .type = CSIDL_Type_User,
1609 .value = L"CD Burning",
1610 .category = KF_CATEGORY_PERUSER,
1611 .name = L"CD Burning",
1612 .parent = &FOLDERID_LocalAppData,
1613 .path = L"Microsoft\\Windows\\Burn\\Burn",
1614 .attributes = FILE_ATTRIBUTE_READONLY,
1615 .flags = KFDF_LOCAL_REDIRECT_ONLY,
1617 { /* 0x3c unassigned */
1618 .id = &GUID_NULL,
1619 .type = CSIDL_Type_Disallowed,
1621 { /* 0x3d - CSIDL_COMPUTERSNEARME */
1622 .id = &GUID_NULL,
1623 .type = CSIDL_Type_Disallowed, /* FIXME */
1625 { /* 0x3e - CSIDL_PROFILES */
1626 .id = &GUID_NULL,
1627 .type = CSIDL_Type_Disallowed, /* oddly, this matches WinXP */
1629 { /* 0x3f */
1630 .id = &FOLDERID_AddNewPrograms,
1631 .type = CSIDL_Type_Disallowed,
1632 .category = KF_CATEGORY_VIRTUAL,
1633 .name = L"AddNewProgramsFolder",
1634 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{15eae92e-f17a-4431-9f28-805e482dafd4}",
1636 { /* 0x40 */
1637 .id = &FOLDERID_AppUpdates,
1638 .type = CSIDL_Type_Disallowed,
1639 .category = KF_CATEGORY_VIRTUAL,
1640 .name = L"AppUpdatesFolder",
1641 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7b81be6a-ce2b-4676-a29e-eb907a5126c5}\\::{d450a8a1-9568-45c7-9c0e-b4f9fb4537bd}",
1643 { /* 0x41 */
1644 .id = &FOLDERID_ChangeRemovePrograms,
1645 .type = CSIDL_Type_Disallowed,
1646 .category = KF_CATEGORY_VIRTUAL,
1647 .name = L"ChangeRemoveProgramsFolder",
1648 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7b81be6a-ce2b-4676-a29e-eb907a5126c5}",
1650 { /* 0x42 */
1651 .id = &FOLDERID_ConflictFolder,
1652 .type = CSIDL_Type_Disallowed,
1653 .category = KF_CATEGORY_VIRTUAL,
1654 .name = L"ConflictFolder",
1655 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}\\::{E413D040-6788-4C22-957E-175D1C513A34},",
1657 { /* 0x43 - CSIDL_CONTACTS */
1658 .id = &FOLDERID_Contacts,
1659 .type = CSIDL_Type_User,
1660 .category = KF_CATEGORY_PERUSER,
1661 .name = L"Contacts",
1662 .parent = &FOLDERID_Profile,
1663 .path = L"Contacts",
1664 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{56784854-C6CB-462B-8169-88E350ACB882}",
1665 .attributes = FILE_ATTRIBUTE_READONLY,
1666 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1668 { /* 0x44 */
1669 .id = &FOLDERID_DeviceMetadataStore,
1670 .type = CSIDL_Type_Disallowed, /* FIXME */
1671 .category = KF_CATEGORY_COMMON,
1672 .name = L"Device Metadata Store",
1673 .parent = &FOLDERID_ProgramData,
1674 .path = L"Microsoft\\Windows\\DeviceMetadataStore",
1676 { /* 0x45 */
1677 .id = &GUID_NULL,
1678 .type = CSIDL_Type_Disallowed,
1680 { /* 0x46 */
1681 .id = &FOLDERID_DocumentsLibrary,
1682 .type = CSIDL_Type_Disallowed, /* FIXME */
1683 .category = KF_CATEGORY_PERUSER,
1684 .name = L"DocumentsLibrary",
1685 .parent = &FOLDERID_Libraries,
1686 .path = L"Documents.library-ms",
1687 .parsing = L"::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\\{7b0db17d-9cd2-4a93-9733-46cc89022e7c}",
1688 .flags = KFDF_PRECREATE | KFDF_STREAM,
1690 { /* 0x47 - CSIDL_DOWNLOADS */
1691 .id = &FOLDERID_Downloads,
1692 .type = CSIDL_Type_User,
1693 .category = KF_CATEGORY_PERUSER,
1694 .name = L"Downloads",
1695 .parent = &FOLDERID_Profile,
1696 .path = L"Downloads",
1697 .attributes = FILE_ATTRIBUTE_READONLY,
1698 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1700 { /* 0x48 */
1701 .id = &FOLDERID_Games,
1702 .type = CSIDL_Type_Disallowed,
1703 .category = KF_CATEGORY_VIRTUAL,
1704 .name = L"Games",
1705 .parsing = L"::{ED228FDF-9EA8-4870-83b1-96b02CFE0D52}",
1707 { /* 0x49 */
1708 .id = &FOLDERID_GameTasks,
1709 .type = CSIDL_Type_Disallowed, /* FIXME */
1710 .category = KF_CATEGORY_PERUSER,
1711 .name = L"GameTasks",
1712 .parent = &FOLDERID_LocalAppData,
1713 .path = L"Microsoft\\Windows\\GameExplorer",
1714 .flags = KFDF_LOCAL_REDIRECT_ONLY,
1716 { /* 0x4a */
1717 .id = &FOLDERID_HomeGroup,
1718 .type = CSIDL_Type_Disallowed,
1719 .category = KF_CATEGORY_VIRTUAL,
1720 .name = L"HomeGroupFolder",
1721 .parsing = L"::{B4FB3F98-C1EA-428d-A78A-D1F5659CBA93}",
1723 { /* 0x4b */
1724 .id = &FOLDERID_ImplicitAppShortcuts,
1725 .type = CSIDL_Type_User,
1726 .category = KF_CATEGORY_PERUSER,
1727 .name = L"ImplicitAppShortcuts",
1728 .parent = &FOLDERID_UserPinned,
1729 .path = L"ImplicitAppShortcuts",
1730 .flags = KFDF_PRECREATE,
1732 { /* 0x4c */
1733 .id = &FOLDERID_Libraries,
1734 .type = CSIDL_Type_Disallowed, /* FIXME */
1735 .category = KF_CATEGORY_PERUSER,
1736 .name = L"Libraries",
1737 .parent = &FOLDERID_RoamingAppData,
1738 .path = L"Microsoft\\Windows\\Libraries",
1739 .flags = KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1741 { /* 0x4d - CSIDL_LINKS */
1742 .id = &FOLDERID_Links,
1743 .type = CSIDL_Type_User,
1744 .category = KF_CATEGORY_PERUSER,
1745 .name = L"Links",
1746 .parent = &FOLDERID_Profile,
1747 .path = L"Links",
1748 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{bfb9d5e0-c6a9-404c-b2b2-ae6db6af4968}",
1749 .attributes = FILE_ATTRIBUTE_READONLY,
1750 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1752 { /* 0x4e - CSIDL_APPDATA_LOCALLOW */
1753 .id = &FOLDERID_LocalAppDataLow,
1754 .type = CSIDL_Type_User,
1755 .category = KF_CATEGORY_PERUSER,
1756 .name = L"LocalAppDataLow",
1757 .parent = &FOLDERID_Profile,
1758 .path = L"AppData\\LocalLow",
1759 .attributes = FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
1760 .flags = KFDF_LOCAL_REDIRECT_ONLY | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1762 { /* 0x4f */
1763 .id = &FOLDERID_MusicLibrary,
1764 .type = CSIDL_Type_Disallowed, /* FIXME */
1765 .category = KF_CATEGORY_PERUSER,
1766 .name = L"MusicLibrary",
1767 .parent = &FOLDERID_Libraries,
1768 .path = L"Music.library-ms",
1769 .parsing = L"::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\\{2112AB0A-C86A-4ffe-A368-0DE96E47012E}",
1770 .flags = KFDF_PRECREATE | KFDF_STREAM,
1772 { /* 0x50 */
1773 .id = &FOLDERID_OriginalImages,
1774 .type = CSIDL_Type_Disallowed, /* FIXME */
1775 .category = KF_CATEGORY_PERUSER,
1776 .name = L"Original Images",
1777 .parent = &FOLDERID_LocalAppData,
1778 .path = L"Microsoft\\Windows Photo Gallery\\Original Images",
1780 { /* 0x51 */
1781 .id = &FOLDERID_PhotoAlbums,
1782 .type = CSIDL_Type_User,
1783 .category = KF_CATEGORY_PERUSER,
1784 .name = L"PhotoAlbums",
1785 .parent = &FOLDERID_Pictures,
1786 .path = L"Slide Shows",
1787 .attributes = FILE_ATTRIBUTE_READONLY,
1789 { /* 0x52 */
1790 .id = &FOLDERID_PicturesLibrary,
1791 .type = CSIDL_Type_Disallowed, /* FIXME */
1792 .category = KF_CATEGORY_PERUSER,
1793 .name = L"PicturesLibrary",
1794 .parent = &FOLDERID_Libraries,
1795 .path = L"Pictures.library-ms",
1796 .parsing = L"::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\\{A990AE9F-A03B-4e80-94BC-9912D7504104}",
1797 .flags = KFDF_PRECREATE | KFDF_STREAM,
1799 { /* 0x53 */
1800 .id = &FOLDERID_Playlists,
1801 .type = CSIDL_Type_User,
1802 .category = KF_CATEGORY_PERUSER,
1803 .name = L"Playlists",
1804 .parent = &FOLDERID_Music,
1805 .path = L"Playlists",
1806 .attributes = FILE_ATTRIBUTE_READONLY,
1808 { /* 0x54 */
1809 .id = &FOLDERID_ProgramFilesX64,
1810 #ifdef _WIN64
1811 .type = CSIDL_Type_CurrVer,
1812 .value = L"ProgramFilesDir",
1813 .def_path = L"Program Files",
1814 #else
1815 .type = CSIDL_Type_NonExistent,
1816 #endif
1817 .category = KF_CATEGORY_FIXED,
1818 .name = L"ProgramFilesX64",
1820 { /* 0x55 */
1821 .id = &FOLDERID_ProgramFilesCommonX64,
1822 #ifdef _WIN64
1823 .type = CSIDL_Type_CurrVer,
1824 .value = L"ProgramFilesCommonX64",
1825 .def_path = L"Program Files\\Common Files",
1826 #else
1827 .type = CSIDL_Type_NonExistent,
1828 #endif
1829 .category = KF_CATEGORY_FIXED,
1830 .name = L"ProgramFilesCommonX64",
1832 { /* 0x56 */
1833 .id = &FOLDERID_Public,
1834 .type = CSIDL_Type_AllUsers,
1835 .category = KF_CATEGORY_FIXED,
1836 .name = L"Public",
1837 .parsing = L"::{4336a54d-038b-4685-ab02-99bb52d3fb8b}",
1838 .attributes = FILE_ATTRIBUTE_READONLY,
1839 .flags = KFDF_PRECREATE,
1841 { /* 0x57 */
1842 .id = &FOLDERID_PublicDownloads,
1843 .type = CSIDL_Type_AllUsers,
1844 .category = KF_CATEGORY_COMMON,
1845 .name = L"CommonDownloads",
1846 .parent = &FOLDERID_Public,
1847 .path = L"Downloads",
1848 .attributes = FILE_ATTRIBUTE_READONLY,
1849 .flags = KFDF_PRECREATE,
1851 { /* 0x58 */
1852 .id = &FOLDERID_PublicGameTasks,
1853 .type = CSIDL_Type_ProgramData,
1854 .category = KF_CATEGORY_COMMON,
1855 .name = L"PublicGameTasks",
1856 .parent = &FOLDERID_ProgramData,
1857 .path = L"Microsoft\\Windows\\GameExplorer",
1858 .flags = KFDF_LOCAL_REDIRECT_ONLY,
1860 { /* 0x59 */
1861 .id = &FOLDERID_PublicLibraries,
1862 .type = CSIDL_Type_AllUsers,
1863 .category = KF_CATEGORY_COMMON,
1864 .name = L"PublicLibraries",
1865 .parent = &FOLDERID_Public,
1866 .path = L"Libraries",
1867 .attributes = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN,
1868 .flags = KFDF_PRECREATE,
1870 { /* 0x5a */
1871 .id = &FOLDERID_PublicRingtones,
1872 .type = CSIDL_Type_ProgramData,
1873 .category = KF_CATEGORY_COMMON,
1874 .name = L"CommonRingtones",
1875 .parent = &FOLDERID_ProgramData,
1876 .path = L"Microsoft\\Windows\\Ringtones",
1877 .flags = KFDF_PRECREATE,
1879 { /* 0x5b */
1880 .id = &FOLDERID_QuickLaunch,
1881 .type = CSIDL_Type_User,
1882 .category = KF_CATEGORY_PERUSER,
1883 .name = L"Quick Launch",
1884 .parent = &FOLDERID_RoamingAppData,
1885 .path = L"Microsoft\\Internet Explorer\\Quick Launch",
1887 { /* 0x5c */
1888 .id = &FOLDERID_RecordedTVLibrary,
1889 .type = CSIDL_Type_Disallowed, /* FIXME */
1890 .category = KF_CATEGORY_COMMON,
1891 .name = L"RecordedTVLibrary",
1892 .parent = &FOLDERID_PublicLibraries,
1893 .path = L"RecordedTV.library-ms",
1894 .flags = KFDF_PRECREATE | KFDF_STREAM,
1896 { /* 0x5d */
1897 .id = &FOLDERID_Ringtones,
1898 .type = CSIDL_Type_Disallowed, /* FIXME */
1899 .category = KF_CATEGORY_PERUSER,
1900 .name = L"Ringtones",
1901 .parent = &FOLDERID_LocalAppData,
1902 .path = L"Microsoft\\Windows\\Ringtones",
1903 .flags = KFDF_PRECREATE,
1905 { /* 0x5e */
1906 .id = &FOLDERID_SampleMusic,
1907 .type = CSIDL_Type_AllUsers,
1908 .category = KF_CATEGORY_COMMON,
1909 .name = L"SampleMusic",
1910 .parent = &FOLDERID_PublicMusic,
1911 .path = L"Sample Music",
1912 .attributes = FILE_ATTRIBUTE_READONLY,
1913 .flags = KFDF_PRECREATE,
1915 { /* 0x5f */
1916 .id = &FOLDERID_SamplePictures,
1917 .type = CSIDL_Type_AllUsers,
1918 .category = KF_CATEGORY_COMMON,
1919 .name = L"SamplePictures",
1920 .parent = &FOLDERID_PublicPictures,
1921 .path = L"Sample Pictures",
1922 .attributes = FILE_ATTRIBUTE_READONLY,
1923 .flags = KFDF_PRECREATE,
1925 { /* 0x60 */
1926 .id = &FOLDERID_SamplePlaylists,
1927 .type = CSIDL_Type_AllUsers,
1928 .category = KF_CATEGORY_COMMON,
1929 .name = L"SamplePlaylists",
1930 .parent = &FOLDERID_PublicMusic,
1931 .path = L"Sample Playlists",
1932 .attributes = FILE_ATTRIBUTE_READONLY,
1933 .flags = KFDF_PRECREATE,
1935 { /* 0x61 */
1936 .id = &FOLDERID_SampleVideos,
1937 .type = CSIDL_Type_AllUsers,
1938 .category = KF_CATEGORY_COMMON,
1939 .name = L"SampleVideos",
1940 .parent = &FOLDERID_PublicVideos,
1941 .path = L"Sample Videos",
1942 .attributes = FILE_ATTRIBUTE_READONLY,
1943 .flags = KFDF_PRECREATE,
1945 { /* 0x62 - CSIDL_SAVED_GAMES */
1946 .id = &FOLDERID_SavedGames,
1947 .type = CSIDL_Type_User,
1948 .category = KF_CATEGORY_PERUSER,
1949 .name = L"SavedGames",
1950 .parent = &FOLDERID_Profile,
1951 .path = L"Saved Games",
1952 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{4C5C32FF-BB9D-43b0-B5B4-2D72E54EAAA4}",
1953 .attributes = FILE_ATTRIBUTE_READONLY,
1954 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1956 { /* 0x63 - CSIDL_SEARCHES */
1957 .id = &FOLDERID_SavedSearches,
1958 .type = CSIDL_Type_User,
1959 .category = KF_CATEGORY_PERUSER,
1960 .name = L"Searches",
1961 .parent = &FOLDERID_Profile,
1962 .path = L"Searches",
1963 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{7d1d3a04-debb-4115-95cf-2f29da2920da}",
1964 .attributes = FILE_ATTRIBUTE_READONLY,
1965 .flags = KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1967 { /* 0x64 */
1968 .id = &FOLDERID_SEARCH_CSC,
1969 .type = CSIDL_Type_Disallowed,
1970 .category = KF_CATEGORY_VIRTUAL,
1971 .name = L"CSCFolder",
1972 .parsing = L"shell:::{BD7A2E7B-21CB-41b2-A086-B309680C6B7E}\\*",
1974 { /* 0x65 */
1975 .id = &FOLDERID_SEARCH_MAPI,
1976 .type = CSIDL_Type_Disallowed,
1977 .category = KF_CATEGORY_VIRTUAL,
1978 .name = L"MAPIFolder",
1979 .parsing = L"shell:::{89D83576-6BD1-4C86-9454-BEB04E94C819}\\*",
1981 { /* 0x66 */
1982 .id = &FOLDERID_SearchHome,
1983 .type = CSIDL_Type_Disallowed,
1984 .category = KF_CATEGORY_VIRTUAL,
1985 .name = L"SearchHomeFolder",
1986 .parsing = L"::{9343812e-1c37-4a49-a12e-4b2d810d956b}",
1988 { /* 0x67 */
1989 .id = &FOLDERID_SidebarDefaultParts,
1990 .type = CSIDL_Type_Disallowed, /* FIXME */
1991 .category = KF_CATEGORY_COMMON,
1992 .name = L"Default Gadgets",
1993 .parent = &FOLDERID_ProgramFiles,
1994 .path = L"Windows Sidebar\\Gadgets",
1996 { /* 0x68 */
1997 .id = &FOLDERID_SidebarParts,
1998 .type = CSIDL_Type_Disallowed, /* FIXME */
1999 .category = KF_CATEGORY_PERUSER,
2000 .name = L"Gadgets",
2001 .parent = &FOLDERID_LocalAppData,
2002 .path = L"Microsoft\\Windows Sidebar\\Gadgets",
2004 { /* 0x69 */
2005 .id = &FOLDERID_SyncManagerFolder,
2006 .type = CSIDL_Type_Disallowed,
2007 .category = KF_CATEGORY_VIRTUAL,
2008 .name = L"SyncCenterFolder",
2009 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}",
2011 { /* 0x6a */
2012 .id = &FOLDERID_SyncResultsFolder,
2013 .type = CSIDL_Type_Disallowed,
2014 .category = KF_CATEGORY_VIRTUAL,
2015 .name = L"SyncResultsFolder",
2016 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}\\::{BC48B32F-5910-47F5-8570-5074A8A5636A},",
2018 { /* 0x6b */
2019 .id = &FOLDERID_SyncSetupFolder,
2020 .type = CSIDL_Type_Disallowed,
2021 .category = KF_CATEGORY_VIRTUAL,
2022 .name = L"SyncSetupFolder",
2023 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}\\::{F1390A9A-A3F4-4E5D-9C5F-98F3BD8D935C},",
2025 { /* 0x6c */
2026 .id = &FOLDERID_UserPinned,
2027 .type = CSIDL_Type_User,
2028 .category = KF_CATEGORY_PERUSER,
2029 .name = L"User Pinned",
2030 .parent = &FOLDERID_QuickLaunch,
2031 .path = L"User Pinned",
2032 .attributes = FILE_ATTRIBUTE_HIDDEN,
2033 .flags = KFDF_PRECREATE,
2035 { /* 0x6d */
2036 .id = &FOLDERID_UserProfiles,
2037 .type = CSIDL_Type_CurrVer,
2038 .value = L"Users",
2039 .def_path = L"Users",
2040 .category = KF_CATEGORY_FIXED,
2041 .name = L"UserProfiles",
2042 .attributes = FILE_ATTRIBUTE_READONLY,
2043 .flags = KFDF_PRECREATE,
2045 { /* 0x6e */
2046 .id = &FOLDERID_UserProgramFiles,
2047 .type = CSIDL_Type_Disallowed, /* FIXME */
2048 .category = KF_CATEGORY_PERUSER,
2049 .name = L"UserProgramFiles",
2050 .parent = &FOLDERID_LocalAppData,
2051 .path = L"Programs",
2053 { /* 0x6f */
2054 .id = &FOLDERID_UserProgramFilesCommon,
2055 .type = CSIDL_Type_Disallowed, /* FIXME */
2056 .category = KF_CATEGORY_PERUSER,
2057 .name = L"UserProgramFilesCommon",
2058 .parent = &FOLDERID_UserProgramFiles,
2059 .path = L"Common",
2061 { /* 0x70 */
2062 .id = &FOLDERID_UsersFiles,
2063 .type = CSIDL_Type_Disallowed,
2064 .category = KF_CATEGORY_VIRTUAL,
2065 .name = L"UsersFilesFolder",
2066 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}",
2068 { /* 0x71 */
2069 .id = &FOLDERID_UsersLibraries,
2070 .type = CSIDL_Type_Disallowed,
2071 .category = KF_CATEGORY_VIRTUAL,
2072 .name = L"UsersLibrariesFolder",
2073 .parsing = L"::{031E4825-7B94-4dc3-B131-E946B44C8DD5}",
2075 { /* 0x72 */
2076 .id = &FOLDERID_VideosLibrary,
2077 .type = CSIDL_Type_Disallowed, /* FIXME */
2078 .category = KF_CATEGORY_PERUSER,
2079 .name = L"VideosLibrary",
2080 .path = L"Videos.library-ms",
2081 .parsing = L"::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\\{491E922F-5643-4af4-A7EB-4E7A138D8174}",
2085 static int csidl_from_id( const KNOWNFOLDERID *id )
2087 int i;
2088 for (i = 0; i < ARRAY_SIZE(CSIDL_Data); i++)
2089 if (IsEqualGUID( CSIDL_Data[i].id, id )) return i;
2090 return -1;
2093 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest);
2095 /* Gets the value named value from the registry key
2096 * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
2097 * (or from rootKey\userPrefix\... if userPrefix is not NULL) into path, which
2098 * is assumed to be MAX_PATH WCHARs in length.
2099 * If it exists, expands the value and writes the expanded value to
2100 * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
2101 * Returns successful error code if the value was retrieved from the registry,
2102 * and a failure otherwise.
2104 static HRESULT _SHGetUserShellFolderPath(HKEY rootKey, LPCWSTR userPrefix,
2105 LPCWSTR value, LPWSTR path)
2107 HRESULT hr;
2108 WCHAR shellFolderPath[MAX_PATH], userShellFolderPath[MAX_PATH];
2109 LPCWSTR pShellFolderPath, pUserShellFolderPath;
2110 HKEY userShellFolderKey, shellFolderKey;
2111 DWORD dwType, dwPathLen;
2113 TRACE("%p,%s,%s,%p\n",rootKey, debugstr_w(userPrefix), debugstr_w(value),
2114 path);
2116 if (userPrefix)
2118 lstrcpyW(shellFolderPath, userPrefix);
2119 PathAddBackslashW(shellFolderPath);
2120 lstrcatW(shellFolderPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
2121 pShellFolderPath = shellFolderPath;
2122 lstrcpyW(userShellFolderPath, userPrefix);
2123 PathAddBackslashW(userShellFolderPath);
2124 lstrcatW(userShellFolderPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders");
2125 pUserShellFolderPath = userShellFolderPath;
2127 else
2129 pUserShellFolderPath = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders";
2130 pShellFolderPath = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
2133 if (RegCreateKeyW(rootKey, pShellFolderPath, &shellFolderKey))
2135 TRACE("Failed to create %s\n", debugstr_w(pShellFolderPath));
2136 return E_FAIL;
2138 if (RegCreateKeyW(rootKey, pUserShellFolderPath, &userShellFolderKey))
2140 TRACE("Failed to create %s\n",
2141 debugstr_w(pUserShellFolderPath));
2142 RegCloseKey(shellFolderKey);
2143 return E_FAIL;
2146 dwPathLen = MAX_PATH * sizeof(WCHAR);
2147 if (!RegQueryValueExW(userShellFolderKey, value, NULL, &dwType,
2148 (LPBYTE)path, &dwPathLen) && (dwType == REG_EXPAND_SZ || dwType == REG_SZ))
2150 LONG ret;
2152 path[dwPathLen / sizeof(WCHAR)] = '\0';
2153 if (dwType == REG_EXPAND_SZ && path[0] == '%')
2155 WCHAR szTemp[MAX_PATH];
2157 _SHExpandEnvironmentStrings(path, szTemp);
2158 lstrcpynW(path, szTemp, MAX_PATH);
2160 ret = RegSetValueExW(shellFolderKey, value, 0, REG_SZ, (LPBYTE)path,
2161 (lstrlenW(path) + 1) * sizeof(WCHAR));
2162 if (ret != ERROR_SUCCESS)
2163 hr = HRESULT_FROM_WIN32(ret);
2164 else
2165 hr = S_OK;
2167 else
2168 hr = E_FAIL;
2169 RegCloseKey(shellFolderKey);
2170 RegCloseKey(userShellFolderKey);
2171 TRACE("returning 0x%08lx\n", hr);
2172 return hr;
2175 static void append_relative_path(BYTE folder, WCHAR *pszPath)
2177 if (CSIDL_Data[folder].path)
2179 PathAddBackslashW(pszPath);
2180 lstrcatW(pszPath, CSIDL_Data[folder].path);
2182 else if (CSIDL_Data[folder].def_path)
2184 PathAddBackslashW(pszPath);
2185 lstrcatW(pszPath, CSIDL_Data[folder].def_path);
2189 /* Gets a 'semi-expanded' default value of the CSIDL with index folder into
2190 * pszPath, based on the entries in CSIDL_Data. By semi-expanded, I mean:
2191 * - Depending on the entry's type, the path may begin with an (unexpanded)
2192 * environment variable name. The caller is responsible for expanding
2193 * environment strings if so desired.
2194 * The types that are prepended with environment variables are:
2195 * CSIDL_Type_User: %USERPROFILE%
2196 * CSIDL_Type_AllUsers: %ALLUSERSPROFILE%
2197 * CSIDL_Type_CurrVer: %SystemDrive%
2198 * (Others might make sense too, but as yet are unneeded.)
2200 static HRESULT _SHGetDefaultValue(BYTE folder, LPWSTR pszPath)
2202 HRESULT hr;
2204 TRACE("0x%02x,%p\n", folder, pszPath);
2206 if (folder >= ARRAY_SIZE(CSIDL_Data))
2207 return E_INVALIDARG;
2209 if (!pszPath)
2210 return E_INVALIDARG;
2212 if (!is_win64)
2214 BOOL is_wow64;
2216 switch (folder)
2218 case CSIDL_PROGRAM_FILES:
2219 case CSIDL_PROGRAM_FILESX86:
2220 IsWow64Process( GetCurrentProcess(), &is_wow64 );
2221 folder = is_wow64 ? CSIDL_PROGRAM_FILESX86 : CSIDL_PROGRAM_FILES;
2222 break;
2223 case CSIDL_PROGRAM_FILES_COMMON:
2224 case CSIDL_PROGRAM_FILES_COMMONX86:
2225 IsWow64Process( GetCurrentProcess(), &is_wow64 );
2226 folder = is_wow64 ? CSIDL_PROGRAM_FILES_COMMONX86 : CSIDL_PROGRAM_FILES_COMMON;
2227 break;
2231 if (!CSIDL_Data[folder].parent)
2233 /* hit the root, sub in env var */
2234 switch (CSIDL_Data[folder].type)
2236 case CSIDL_Type_User:
2237 lstrcpyW(pszPath, L"%USERPROFILE%");
2238 break;
2239 case CSIDL_Type_AllUsers:
2240 lstrcpyW(pszPath, L"%PUBLIC%");
2241 break;
2242 case CSIDL_Type_ProgramData:
2243 lstrcpyW(pszPath, L"%ProgramData%");
2244 break;
2245 case CSIDL_Type_CurrVer:
2246 lstrcpyW(pszPath, L"%SystemDrive%");
2247 break;
2248 default:
2249 ; /* no corresponding env. var, do nothing */
2251 hr = S_OK;
2252 }else{
2253 /* prepend with parent */
2254 hr = _SHGetDefaultValue(csidl_from_id(CSIDL_Data[folder].parent), pszPath);
2257 if (SUCCEEDED(hr))
2258 append_relative_path(folder, pszPath);
2260 TRACE("returning 0x%08lx\n", hr);
2261 return hr;
2264 /* Gets the (unexpanded) value of the folder with index folder into pszPath.
2265 * The folder's type is assumed to be CSIDL_Type_CurrVer. Its default value
2266 * can be overridden in the HKLM\\Software\\Microsoft\\Windows\\CurrentVersion key.
2267 * If dwFlags has SHGFP_TYPE_DEFAULT set or if the value isn't overridden in
2268 * the registry, uses _SHGetDefaultValue to get the value.
2270 static HRESULT _SHGetCurrentVersionPath(DWORD dwFlags, BYTE folder,
2271 LPWSTR pszPath)
2273 HRESULT hr;
2275 TRACE("0x%08lx,0x%02x,%p\n", dwFlags, folder, pszPath);
2277 if (folder >= ARRAY_SIZE(CSIDL_Data))
2278 return E_INVALIDARG;
2279 if (CSIDL_Data[folder].type != CSIDL_Type_CurrVer)
2280 return E_INVALIDARG;
2281 if (!pszPath)
2282 return E_INVALIDARG;
2284 if (dwFlags & SHGFP_TYPE_DEFAULT)
2285 hr = _SHGetDefaultValue(folder, pszPath);
2286 else
2288 HKEY hKey;
2290 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion", &hKey))
2291 hr = E_FAIL;
2292 else
2294 DWORD dwType, dwPathLen = MAX_PATH * sizeof(WCHAR);
2296 if (RegQueryValueExW(hKey, CSIDL_Data[folder].value, NULL,
2297 &dwType, (LPBYTE)pszPath, &dwPathLen) ||
2298 (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
2300 hr = _SHGetDefaultValue(folder, pszPath);
2301 dwType = REG_EXPAND_SZ;
2302 switch (folder)
2304 case CSIDL_PROGRAM_FILESX86:
2305 case CSIDL_PROGRAM_FILES_COMMONX86:
2306 /* these two should never be set on 32-bit setups */
2307 if (!is_win64)
2309 BOOL is_wow64;
2310 IsWow64Process( GetCurrentProcess(), &is_wow64 );
2311 if (!is_wow64) break;
2313 /* fall through */
2314 default:
2315 RegSetValueExW(hKey, CSIDL_Data[folder].value, 0, dwType,
2316 (LPBYTE)pszPath, (lstrlenW(pszPath)+1)*sizeof(WCHAR));
2319 else
2321 pszPath[dwPathLen / sizeof(WCHAR)] = '\0';
2322 hr = S_OK;
2324 RegCloseKey(hKey);
2327 TRACE("returning 0x%08lx (output path is %s)\n", hr, debugstr_w(pszPath));
2328 return hr;
2331 static LPWSTR _GetUserSidStringFromToken(HANDLE Token)
2333 char InfoBuffer[64];
2334 PTOKEN_USER UserInfo;
2335 DWORD InfoSize;
2336 LPWSTR SidStr;
2338 UserInfo = (PTOKEN_USER) InfoBuffer;
2339 if (! GetTokenInformation(Token, TokenUser, InfoBuffer, sizeof(InfoBuffer),
2340 &InfoSize))
2342 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2343 return NULL;
2344 UserInfo = malloc(InfoSize);
2345 if (UserInfo == NULL)
2346 return NULL;
2347 if (! GetTokenInformation(Token, TokenUser, UserInfo, InfoSize,
2348 &InfoSize))
2350 free(UserInfo);
2351 return NULL;
2355 if (! ConvertSidToStringSidW(UserInfo->User.Sid, &SidStr))
2356 SidStr = NULL;
2358 if (UserInfo != (PTOKEN_USER) InfoBuffer)
2359 free(UserInfo);
2361 return SidStr;
2364 /* Gets the user's path (unexpanded) for the CSIDL with index folder:
2365 * If SHGFP_TYPE_DEFAULT is set, calls _SHGetDefaultValue for it. Otherwise
2366 * calls _SHGetUserShellFolderPath for it. Where it looks depends on hToken:
2367 * - if hToken is -1, looks in HKEY_USERS\.Default
2368 * - otherwise looks first in HKEY_CURRENT_USER, followed by HKEY_LOCAL_MACHINE
2369 * if HKEY_CURRENT_USER doesn't contain any entries. If both fail, finally
2370 * calls _SHGetDefaultValue for it.
2372 static HRESULT _SHGetUserProfilePath(HANDLE hToken, DWORD dwFlags, BYTE folder,
2373 LPWSTR pszPath)
2375 const WCHAR *szValueName;
2376 WCHAR buffer[40];
2377 HRESULT hr;
2379 TRACE("%p,0x%08lx,0x%02x,%p\n", hToken, dwFlags, folder, pszPath);
2381 if (folder >= ARRAY_SIZE(CSIDL_Data))
2382 return E_INVALIDARG;
2383 if (CSIDL_Data[folder].type != CSIDL_Type_User)
2384 return E_INVALIDARG;
2385 if (!pszPath)
2386 return E_INVALIDARG;
2388 if (dwFlags & SHGFP_TYPE_DEFAULT)
2390 if (hToken != NULL && hToken != (HANDLE)-1)
2392 FIXME("unsupported for user other than current or default\n");
2393 return E_FAIL;
2395 hr = _SHGetDefaultValue(folder, pszPath);
2397 else
2399 static const WCHAR DefaultW[] = L".Default";
2400 LPCWSTR userPrefix = NULL;
2401 HKEY hRootKey;
2403 if (hToken == (HANDLE)-1)
2405 hRootKey = HKEY_USERS;
2406 userPrefix = DefaultW;
2408 else if (hToken == NULL)
2409 hRootKey = HKEY_CURRENT_USER;
2410 else
2412 hRootKey = HKEY_USERS;
2413 userPrefix = _GetUserSidStringFromToken(hToken);
2414 if (userPrefix == NULL)
2416 hr = E_FAIL;
2417 goto error;
2421 /* For CSIDL_Type_User we also use the GUID if no szValueName is provided */
2422 szValueName = CSIDL_Data[folder].value;
2423 if (!szValueName)
2425 StringFromGUID2( CSIDL_Data[folder].id, buffer, 39 );
2426 szValueName = &buffer[0];
2429 hr = _SHGetUserShellFolderPath(hRootKey, userPrefix, szValueName, pszPath);
2430 if (FAILED(hr) && hRootKey != HKEY_LOCAL_MACHINE)
2431 hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL, szValueName, pszPath);
2432 if (FAILED(hr))
2433 hr = _SHGetDefaultValue(folder, pszPath);
2434 if (userPrefix != NULL && userPrefix != DefaultW)
2435 LocalFree((HLOCAL) userPrefix);
2437 error:
2438 TRACE("returning 0x%08lx (output path is %s)\n", hr, debugstr_w(pszPath));
2439 return hr;
2442 /* Gets the (unexpanded) path for the CSIDL with index folder. If dwFlags has
2443 * SHGFP_TYPE_DEFAULT set, calls _SHGetDefaultValue. Otherwise calls
2444 * _SHGetUserShellFolderPath for it, looking only in HKEY_LOCAL_MACHINE.
2445 * If this fails, falls back to _SHGetDefaultValue.
2447 static HRESULT _SHGetAllUsersProfilePath(DWORD dwFlags, BYTE folder,
2448 LPWSTR pszPath)
2450 HRESULT hr;
2452 TRACE("0x%08lx,0x%02x,%p\n", dwFlags, folder, pszPath);
2454 if (folder >= ARRAY_SIZE(CSIDL_Data))
2455 return E_INVALIDARG;
2456 if (CSIDL_Data[folder].type != CSIDL_Type_AllUsers && CSIDL_Data[folder].type != CSIDL_Type_ProgramData)
2457 return E_INVALIDARG;
2458 if (!pszPath)
2459 return E_INVALIDARG;
2461 if (dwFlags & SHGFP_TYPE_DEFAULT)
2462 hr = _SHGetDefaultValue(folder, pszPath);
2463 else
2465 hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL,
2466 CSIDL_Data[folder].value, pszPath);
2467 if (FAILED(hr))
2468 hr = _SHGetDefaultValue(folder, pszPath);
2470 TRACE("returning 0x%08lx (output path is %s)\n", hr, debugstr_w(pszPath));
2471 return hr;
2474 static HRESULT _SHOpenProfilesKey(PHKEY pKey)
2476 LONG lRet;
2477 DWORD disp;
2479 lRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList", 0, NULL, 0,
2480 KEY_ALL_ACCESS, NULL, pKey, &disp);
2481 return HRESULT_FROM_WIN32(lRet);
2484 /* Reads the value named szValueName from the key profilesKey (assumed to be
2485 * opened by _SHOpenProfilesKey) into szValue, which is assumed to be MAX_PATH
2486 * WCHARs in length. If it doesn't exist, returns szDefault (and saves
2487 * szDefault to the registry).
2489 static HRESULT _SHGetProfilesValue(HKEY profilesKey, LPCWSTR szValueName,
2490 LPWSTR szValue, LPCWSTR szDefault)
2492 HRESULT hr;
2493 DWORD type, dwPathLen = MAX_PATH * sizeof(WCHAR);
2494 LONG lRet;
2496 TRACE("%p,%s,%p,%s\n", profilesKey, debugstr_w(szValueName), szValue,
2497 debugstr_w(szDefault));
2498 lRet = RegQueryValueExW(profilesKey, szValueName, NULL, &type,
2499 (LPBYTE)szValue, &dwPathLen);
2500 if (!lRet && (type == REG_SZ || type == REG_EXPAND_SZ) && dwPathLen
2501 && *szValue)
2503 dwPathLen /= sizeof(WCHAR);
2504 szValue[dwPathLen] = '\0';
2505 hr = S_OK;
2507 else
2509 /* Missing or invalid value, set a default */
2510 lstrcpynW(szValue, szDefault, MAX_PATH);
2511 TRACE("Setting missing value %s to %s\n", debugstr_w(szValueName),
2512 debugstr_w(szValue));
2513 lRet = RegSetValueExW(profilesKey, szValueName, 0, REG_EXPAND_SZ,
2514 (LPBYTE)szValue,
2515 (lstrlenW(szValue) + 1) * sizeof(WCHAR));
2516 if (lRet)
2517 hr = HRESULT_FROM_WIN32(lRet);
2518 else
2519 hr = S_OK;
2521 TRACE("returning 0x%08lx (output value is %s)\n", hr, debugstr_w(szValue));
2522 return hr;
2525 /* Attempts to expand environment variables from szSrc into szDest, which is
2526 * assumed to be MAX_PATH characters in length. Before referring to the
2527 * environment, handles a few variables directly, because the environment
2528 * variables may not be set when this is called (as during Wine's installation
2529 * when default values are being written to the registry).
2530 * The directly handled environment variables, and their source, are:
2531 * - ALLUSERSPROFILE, USERPROFILE: reads from the registry
2532 * - SystemDrive: uses GetSystemDirectoryW and uses the drive portion of its
2533 * path
2534 * If one of the directly handled environment variables is expanded, only
2535 * expands a single variable, and only in the beginning of szSrc.
2537 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest)
2539 HRESULT hr;
2540 WCHAR szTemp[MAX_PATH], szProfilesPrefix[MAX_PATH] = { 0 };
2541 HKEY key = NULL;
2543 TRACE("%s, %p\n", debugstr_w(szSrc), szDest);
2545 if (!szSrc || !szDest) return E_INVALIDARG;
2547 /* short-circuit if there's nothing to expand */
2548 if (szSrc[0] != '%')
2550 lstrcpyW(szDest, szSrc);
2551 hr = S_OK;
2552 goto end;
2554 /* Get the profile prefix, we'll probably be needing it */
2555 hr = _SHOpenProfilesKey(&key);
2556 if (SUCCEEDED(hr))
2558 WCHAR def_val[MAX_PATH];
2560 /* get the system drive */
2561 GetSystemDirectoryW(def_val, MAX_PATH);
2562 lstrcpyW( def_val + 3, L"users" );
2564 hr = _SHGetProfilesValue(key, L"ProfilesDirectory", szProfilesPrefix, def_val );
2567 *szDest = 0;
2568 lstrcpyW(szTemp, szSrc);
2569 while (SUCCEEDED(hr) && szTemp[0] == '%')
2571 if (!wcsnicmp(szTemp, L"%ALLUSERSPROFILE%", lstrlenW(L"%ALLUSERSPROFILE%")))
2573 WCHAR szAllUsers[MAX_PATH], def_val[MAX_PATH];
2575 GetSystemDirectoryW(def_val, MAX_PATH);
2576 lstrcpyW( def_val + 3, L"users\\Public" );
2578 hr = _SHGetProfilesValue(key, L"Public", szAllUsers, def_val);
2579 PathAppendW(szDest, szAllUsers);
2580 PathAppendW(szDest, szTemp + lstrlenW(L"%ALLUSERSPROFILE%"));
2582 else if (!wcsnicmp(szTemp, L"%PUBLIC%", lstrlenW(L"%PUBLIC%")))
2584 WCHAR szAllUsers[MAX_PATH], def_val[MAX_PATH];
2586 GetSystemDirectoryW(def_val, MAX_PATH);
2587 lstrcpyW( def_val + 3, L"users\\Public" );
2589 hr = _SHGetProfilesValue(key, L"Public", szAllUsers, def_val);
2590 PathAppendW(szDest, szAllUsers);
2591 PathAppendW(szDest, szTemp + lstrlenW(L"%PUBLIC%"));
2593 else if (!wcsnicmp(szTemp, L"%ProgramData%", lstrlenW(L"%ProgramData%")))
2595 WCHAR szProgramData[MAX_PATH], def_val[MAX_PATH];
2596 HKEY shellFolderKey;
2597 DWORD dwType, dwPathLen = sizeof(def_val);
2598 BOOL in_registry = FALSE;
2600 if (!RegCreateKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", &shellFolderKey))
2602 if (!RegQueryValueExW(shellFolderKey, L"Common AppData", NULL, &dwType,
2603 (LPBYTE)def_val, &dwPathLen) && (dwType == REG_EXPAND_SZ || dwType == REG_SZ))
2604 in_registry = TRUE;
2606 RegCloseKey(shellFolderKey);
2609 if (!in_registry)
2611 GetSystemDirectoryW(def_val, MAX_PATH);
2612 lstrcpyW( def_val + 3, L"ProgramData" );
2615 hr = _SHGetProfilesValue(key, L"ProgramData", szProgramData, def_val);
2616 PathAppendW(szDest, szProgramData);
2617 PathAppendW(szDest, szTemp + lstrlenW(L"%ProgramData%"));
2619 else if (!wcsnicmp(szTemp, L"%USERPROFILE%", lstrlenW(L"%USERPROFILE%")))
2621 WCHAR userName[MAX_PATH];
2622 DWORD userLen = MAX_PATH;
2624 lstrcpyW(szDest, szProfilesPrefix);
2625 GetUserNameW(userName, &userLen);
2626 PathAppendW(szDest, userName);
2627 PathAppendW(szDest, szTemp + lstrlenW(L"%USERPROFILE%"));
2629 else if (!wcsnicmp(szTemp, L"%SystemDrive%", lstrlenW(L"%SystemDrive%")))
2631 GetSystemDirectoryW(szDest, MAX_PATH);
2632 lstrcpyW(szDest + 3, szTemp + lstrlenW(L"%SystemDrive%") + 1);
2634 else
2636 DWORD ret = ExpandEnvironmentStringsW(szTemp, szDest, MAX_PATH);
2638 if (ret > MAX_PATH)
2639 hr = E_NOT_SUFFICIENT_BUFFER;
2640 else if (ret == 0)
2641 hr = HRESULT_FROM_WIN32(GetLastError());
2642 else if (!wcscmp( szTemp, szDest )) break; /* nothing expanded */
2644 if (SUCCEEDED(hr)) lstrcpyW(szTemp, szDest);
2646 end:
2647 if (key)
2648 RegCloseKey(key);
2649 TRACE("returning 0x%08lx (input was %s, output is %s)\n", hr,
2650 debugstr_w(szSrc), debugstr_w(szDest));
2651 return hr;
2654 static char *xdg_config;
2655 static DWORD xdg_config_len;
2657 static BOOL WINAPI init_xdg_dirs( INIT_ONCE *once, void *param, void **context )
2659 const WCHAR *var, *fmt = L"\\??\\unix%s/user-dirs.dirs";
2660 char *p;
2661 WCHAR *name, *ptr;
2662 HANDLE file;
2663 DWORD len;
2665 if (!(var = _wgetenv( L"XDG_CONFIG_HOME" )) || var[0] != '/')
2667 if (!(var = _wgetenv( L"WINEHOMEDIR" ))) return TRUE;
2668 fmt = L"%s/.config/user-dirs.dirs";
2670 len = lstrlenW(var) + lstrlenW(fmt);
2671 name = malloc( len * sizeof(WCHAR) );
2672 swprintf( name, len, fmt, var );
2673 name[1] = '\\'; /* change \??\ to \\?\ */
2674 for (ptr = name; *ptr; ptr++) if (*ptr == '/') *ptr = '\\';
2676 file = CreateFileW( name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
2677 free( name );
2678 if (file != INVALID_HANDLE_VALUE)
2680 len = GetFileSize( file, NULL );
2681 if (!(xdg_config = malloc( len + 1 ))) return TRUE;
2682 if (!ReadFile( file, xdg_config, len, &xdg_config_len, NULL ))
2684 free( xdg_config );
2685 xdg_config = NULL;
2687 else
2689 for (p = xdg_config; p < xdg_config + xdg_config_len; p++) if (*p == '\n') *p = 0;
2690 *p = 0; /* append null to simplify string parsing */
2692 CloseHandle( file );
2694 return TRUE;
2697 static char *get_xdg_path( const char *var )
2699 static INIT_ONCE once;
2700 char *p, *ret = NULL;
2701 int i;
2703 InitOnceExecuteOnce( &once, init_xdg_dirs, NULL, NULL );
2704 if (!xdg_config) return NULL;
2706 for (p = xdg_config; p < xdg_config + xdg_config_len; p += strlen(p) + 1)
2708 while (*p == ' ' || *p == '\t') p++;
2709 if (strncmp( p, var, strlen(var) )) continue;
2710 p += strlen(var);
2711 while (*p == ' ' || *p == '\t') p++;
2712 if (*p != '=') continue;
2713 p++;
2714 while (*p == ' ' || *p == '\t') p++;
2715 if (*p != '"') continue;
2716 p++;
2717 if (*p != '/' && strncmp( p, "$HOME/", 6 )) continue;
2719 if (!(ret = malloc( strlen(p) + 1 ))) break;
2720 for (i = 0; *p && *p != '"'; i++, p++)
2722 if (*p == '\\' && p[1]) p++;
2723 ret[i] = *p;
2725 ret[i] = 0;
2726 if (*p != '"')
2728 free( ret );
2729 ret = NULL;
2731 break;
2733 return ret;
2736 static BOOL link_folder( HANDLE mgr, const UNICODE_STRING *path, const char *link )
2738 struct mountmgr_shell_folder *ioctl;
2739 DWORD len = sizeof(*ioctl) + path->Length + strlen(link) + 1;
2740 BOOL ret;
2742 if (!(ioctl = malloc( len ))) return FALSE;
2743 ioctl->create_backup = FALSE;
2744 ioctl->folder_offset = sizeof(*ioctl);
2745 ioctl->folder_size = path->Length;
2746 memcpy( (char *)ioctl + ioctl->folder_offset, path->Buffer, ioctl->folder_size );
2747 ioctl->symlink_offset = ioctl->folder_offset + ioctl->folder_size;
2748 strcpy( (char *)ioctl + ioctl->symlink_offset, link );
2750 ret = DeviceIoControl( mgr, IOCTL_MOUNTMGR_DEFINE_SHELL_FOLDER, ioctl, len, NULL, 0, NULL, NULL );
2751 free( ioctl );
2752 return ret;
2755 /******************************************************************************
2756 * create_link
2758 * Sets up a symbolic link for one of the 'My Whatever' shell folders to point
2759 * into the corresponding XDG directory.
2761 static void create_link( const WCHAR *path, const char *xdg_name, const char *default_name )
2763 UNICODE_STRING nt_name;
2764 char *target = NULL;
2765 HANDLE mgr;
2767 if ((mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,
2768 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
2769 0, 0 )) == INVALID_HANDLE_VALUE)
2771 FIXME( "failed to connect to mount manager\n" );
2772 return;
2775 nt_name.Buffer = NULL;
2776 if (!RtlDosPathNameToNtPathName_U( path, &nt_name, NULL, NULL )) goto done;
2778 if ((target = get_xdg_path( xdg_name )))
2780 if (link_folder( mgr, &nt_name, target )) goto done;
2782 link_folder( mgr, &nt_name, default_name );
2784 done:
2785 RtlFreeUnicodeString( &nt_name );
2786 free( target );
2787 CloseHandle( mgr );
2790 /******************************************************************************
2791 * _SHCreateSymbolicLink [Internal]
2793 * Sets up a symbolic link for one of the special shell folders to point into
2794 * the users home directory.
2796 * PARAMS
2797 * nFolder [I] CSIDL identifying the folder.
2799 static void _SHCreateSymbolicLink(int nFolder, const WCHAR *path)
2801 DWORD folder = nFolder & CSIDL_FOLDER_MASK;
2803 switch (folder) {
2804 case CSIDL_PERSONAL:
2805 create_link( path, "XDG_DOCUMENTS_DIR", "$HOME/Documents" );
2806 break;
2807 case CSIDL_DESKTOPDIRECTORY:
2808 create_link( path, "XDG_DESKTOP_DIR", "$HOME/Desktop" );
2809 break;
2810 case CSIDL_MYPICTURES:
2811 create_link( path, "XDG_PICTURES_DIR", "$HOME/Pictures" );
2812 break;
2813 case CSIDL_MYVIDEO:
2814 create_link( path, "XDG_VIDEOS_DIR", "$HOME/Movies" );
2815 break;
2816 case CSIDL_MYMUSIC:
2817 create_link( path, "XDG_MUSIC_DIR", "$HOME/Music" );
2818 break;
2819 case CSIDL_DOWNLOADS:
2820 create_link( path, "XDG_DOWNLOAD_DIR", "$HOME/Downloads" );
2821 break;
2822 case CSIDL_TEMPLATES:
2823 create_link( path, "XDG_TEMPLATES_DIR", "$HOME/Templates" );
2824 break;
2828 /******************************************************************************
2829 * SHGetFolderPathW [SHELL32.@]
2831 * Convert nFolder to path.
2833 * RETURNS
2834 * Success: S_OK
2835 * Failure: standard HRESULT error codes.
2837 * NOTES
2838 * Most values can be overridden in either
2839 * HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
2840 * or in the same location in HKLM.
2841 * The "Shell Folders" registry key was used in NT4 and earlier systems.
2842 * Beginning with Windows 2000, the "User Shell Folders" key is used, so
2843 * changes made to it are made to the former key too. This synchronization is
2844 * done on-demand: not until someone requests the value of one of these paths
2845 * (by calling one of the SHGet functions) is the value synchronized.
2846 * Furthermore, the HKCU paths take precedence over the HKLM paths.
2848 HRESULT WINAPI SHGetFolderPathW(
2849 HWND hwndOwner, /* [I] owner window */
2850 int nFolder, /* [I] CSIDL identifying the folder */
2851 HANDLE hToken, /* [I] access token */
2852 DWORD dwFlags, /* [I] which path to return */
2853 LPWSTR pszPath) /* [O] converted path */
2855 HRESULT hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, NULL, pszPath);
2856 if(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
2857 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
2858 return hr;
2861 HRESULT WINAPI SHGetFolderPathAndSubDirA(
2862 HWND hwndOwner, /* [I] owner window */
2863 int nFolder, /* [I] CSIDL identifying the folder */
2864 HANDLE hToken, /* [I] access token */
2865 DWORD dwFlags, /* [I] which path to return */
2866 LPCSTR pszSubPath, /* [I] sub directory of the specified folder */
2867 LPSTR pszPath) /* [O] converted path */
2869 int length;
2870 HRESULT hr = S_OK;
2871 LPWSTR pszSubPathW = NULL;
2872 LPWSTR pszPathW = NULL;
2874 TRACE("%p,%#x,%p,%#lx,%s,%p\n", hwndOwner, nFolder, hToken, dwFlags, debugstr_a(pszSubPath), pszPath);
2876 if(pszPath) {
2877 pszPathW = malloc(MAX_PATH * sizeof(WCHAR));
2878 if(!pszPathW) {
2879 hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
2880 goto cleanup;
2883 TRACE("%08x,%08lx,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
2885 /* SHGetFolderPathAndSubDirW does not distinguish if pszSubPath isn't
2886 * set (null), or an empty string.therefore call it without the parameter set
2887 * if pszSubPath is an empty string
2889 if (pszSubPath && pszSubPath[0]) {
2890 length = MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, NULL, 0);
2891 pszSubPathW = malloc(length * sizeof(WCHAR));
2892 if(!pszSubPathW) {
2893 hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
2894 goto cleanup;
2896 MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, pszSubPathW, length);
2899 hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, pszSubPathW, pszPathW);
2901 if (SUCCEEDED(hr) && pszPath)
2902 WideCharToMultiByte(CP_ACP, 0, pszPathW, -1, pszPath, MAX_PATH, NULL, NULL);
2904 cleanup:
2905 free(pszPathW);
2906 free(pszSubPathW);
2907 return hr;
2910 /*************************************************************************
2911 * SHGetFolderPathAndSubDirW [SHELL32.@]
2913 HRESULT WINAPI SHGetFolderPathAndSubDirW(
2914 HWND hwndOwner, /* [I] owner window */
2915 int nFolder, /* [I] CSIDL identifying the folder */
2916 HANDLE hToken, /* [I] access token */
2917 DWORD dwFlags, /* [I] which path to return */
2918 LPCWSTR pszSubPath,/* [I] sub directory of the specified folder */
2919 LPWSTR pszPath) /* [O] converted path */
2921 HRESULT hr;
2922 WCHAR szBuildPath[MAX_PATH], szTemp[MAX_PATH];
2923 DWORD folder = nFolder & CSIDL_FOLDER_MASK;
2924 CSIDL_Type type;
2925 int ret;
2927 TRACE("%p,%#x,%p,%#lx,%s,%p\n", hwndOwner, nFolder, hToken, dwFlags, debugstr_w(pszSubPath), pszPath);
2929 /* Windows always NULL-terminates the resulting path regardless of success
2930 * or failure, so do so first
2932 if (pszPath)
2933 *pszPath = '\0';
2935 if (folder >= ARRAY_SIZE(CSIDL_Data))
2936 return E_INVALIDARG;
2937 if ((SHGFP_TYPE_CURRENT != dwFlags) && (SHGFP_TYPE_DEFAULT != dwFlags))
2938 return E_INVALIDARG;
2939 szTemp[0] = 0;
2940 type = CSIDL_Data[folder].type;
2941 switch (type)
2943 case CSIDL_Type_Disallowed:
2944 hr = E_INVALIDARG;
2945 break;
2946 case CSIDL_Type_NonExistent:
2947 hr = S_FALSE;
2948 break;
2949 case CSIDL_Type_WindowsPath:
2950 GetWindowsDirectoryW(szTemp, MAX_PATH);
2951 append_relative_path(folder, szTemp);
2952 hr = S_OK;
2953 break;
2954 case CSIDL_Type_SystemPath:
2955 GetSystemDirectoryW(szTemp, MAX_PATH);
2956 append_relative_path(folder, szTemp);
2957 hr = S_OK;
2958 break;
2959 case CSIDL_Type_SystemX86Path:
2960 if (!GetSystemWow64DirectoryW(szTemp, MAX_PATH)) GetSystemDirectoryW(szTemp, MAX_PATH);
2961 append_relative_path(folder, szTemp);
2962 hr = S_OK;
2963 break;
2964 case CSIDL_Type_CurrVer:
2965 hr = _SHGetCurrentVersionPath(dwFlags, folder, szTemp);
2966 break;
2967 case CSIDL_Type_User:
2968 hr = _SHGetUserProfilePath(hToken, dwFlags, folder, szTemp);
2969 break;
2970 case CSIDL_Type_AllUsers:
2971 case CSIDL_Type_ProgramData:
2972 hr = _SHGetAllUsersProfilePath(dwFlags, folder, szTemp);
2973 break;
2974 default:
2975 FIXME("bogus type %d, please fix\n", type);
2976 hr = E_INVALIDARG;
2977 break;
2980 /* Expand environment strings if necessary */
2981 if (*szTemp == '%')
2982 hr = _SHExpandEnvironmentStrings(szTemp, szBuildPath);
2983 else
2984 lstrcpyW(szBuildPath, szTemp);
2986 if (FAILED(hr)) goto end;
2988 if(pszSubPath) {
2989 /* make sure the new path does not exceed the buffer length
2990 * and remember to backslash and terminate it */
2991 if(MAX_PATH < (lstrlenW(szBuildPath) + lstrlenW(pszSubPath) + 2)) {
2992 hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
2993 goto end;
2995 PathAppendW(szBuildPath, pszSubPath);
2996 PathRemoveBackslashW(szBuildPath);
2998 /* Copy the path if it's available before we might return */
2999 if (SUCCEEDED(hr) && pszPath)
3000 lstrcpyW(pszPath, szBuildPath);
3002 /* if we don't care about existing directories we are ready */
3003 if(nFolder & CSIDL_FLAG_DONT_VERIFY) goto end;
3005 if (PathFileExistsW(szBuildPath)) goto end;
3007 /* not existing but we are not allowed to create it. The return value
3008 * is verified against shell32 version 6.0.
3010 if (!(nFolder & CSIDL_FLAG_CREATE))
3012 hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
3013 goto end;
3016 /* create symbolic links rather than directories for specific
3017 * user shell folders */
3018 _SHCreateSymbolicLink(folder, szBuildPath);
3020 /* create directory/directories */
3021 ret = SHCreateDirectoryExW(hwndOwner, szBuildPath, NULL);
3022 if (ret && ret != ERROR_ALREADY_EXISTS)
3024 ERR("Failed to create directory %s.\n", debugstr_w(szBuildPath));
3025 hr = E_FAIL;
3026 goto end;
3029 TRACE("Created missing system directory %s\n", debugstr_w(szBuildPath));
3030 end:
3031 TRACE("returning 0x%08lx (final path is %s)\n", hr, debugstr_w(szBuildPath));
3032 return hr;
3035 /*************************************************************************
3036 * SHGetFolderPathA [SHELL32.@]
3038 * See SHGetFolderPathW.
3040 HRESULT WINAPI SHGetFolderPathA(
3041 HWND hwndOwner,
3042 int nFolder,
3043 HANDLE hToken,
3044 DWORD dwFlags,
3045 LPSTR pszPath)
3047 WCHAR szTemp[MAX_PATH];
3048 HRESULT hr;
3050 TRACE("%p,%d,%p,%#lx,%p\n", hwndOwner, nFolder, hToken, dwFlags, pszPath);
3052 if (pszPath)
3053 *pszPath = '\0';
3054 hr = SHGetFolderPathW(hwndOwner, nFolder, hToken, dwFlags, szTemp);
3055 if (SUCCEEDED(hr) && pszPath)
3056 WideCharToMultiByte(CP_ACP, 0, szTemp, -1, pszPath, MAX_PATH, NULL,
3057 NULL);
3059 return hr;
3062 /* For each folder in folders, if its value has not been set in the registry,
3063 * calls _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the
3064 * folder's type) to get the unexpanded value first.
3065 * Writes the unexpanded value to User Shell Folders, and queries it with
3066 * SHGetFolderPathW to force the creation of the directory if it doesn't
3067 * already exist. SHGetFolderPathW also returns the expanded value, which
3068 * this then writes to Shell Folders.
3070 static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken,
3071 LPCWSTR szUserShellFolderPath, LPCWSTR szShellFolderPath, const UINT folders[],
3072 UINT foldersLen)
3074 const WCHAR *szValueName;
3075 WCHAR buffer[40];
3076 UINT i;
3077 WCHAR path[MAX_PATH];
3078 HRESULT hr = S_OK;
3079 HKEY hUserKey = NULL, hKey = NULL;
3080 DWORD dwType, dwPathLen;
3081 LONG ret;
3083 TRACE("%p,%p,%s,%p,%u\n", hRootKey, hToken,
3084 debugstr_w(szUserShellFolderPath), folders, foldersLen);
3086 ret = RegCreateKeyW(hRootKey, szUserShellFolderPath, &hUserKey);
3087 if (ret)
3088 hr = HRESULT_FROM_WIN32(ret);
3089 else
3091 ret = RegCreateKeyW(hRootKey, szShellFolderPath, &hKey);
3092 if (ret)
3093 hr = HRESULT_FROM_WIN32(ret);
3095 for (i = 0; SUCCEEDED(hr) && i < foldersLen; i++)
3097 dwPathLen = MAX_PATH * sizeof(WCHAR);
3099 /* For CSIDL_Type_User we also use the GUID if no szValueName is provided */
3100 szValueName = CSIDL_Data[folders[i]].value;
3101 if (!szValueName && CSIDL_Data[folders[i]].type == CSIDL_Type_User)
3103 StringFromGUID2( CSIDL_Data[folders[i]].id, buffer, 39 );
3104 szValueName = &buffer[0];
3107 if (RegQueryValueExW(hUserKey, szValueName, NULL,
3108 &dwType, (LPBYTE)path, &dwPathLen) || (dwType != REG_SZ &&
3109 dwType != REG_EXPAND_SZ))
3111 *path = '\0';
3112 if (CSIDL_Data[folders[i]].type == CSIDL_Type_User)
3113 _SHGetUserProfilePath(hToken, SHGFP_TYPE_DEFAULT, folders[i],
3114 path);
3115 else if (CSIDL_Data[folders[i]].type == CSIDL_Type_AllUsers ||
3116 CSIDL_Data[folders[i]].type == CSIDL_Type_ProgramData)
3117 _SHGetAllUsersProfilePath(SHGFP_TYPE_DEFAULT, folders[i], path);
3118 else if (CSIDL_Data[folders[i]].type == CSIDL_Type_WindowsPath)
3120 GetWindowsDirectoryW(path, MAX_PATH);
3121 append_relative_path(folders[i], path);
3123 else
3124 hr = E_FAIL;
3125 if (*path)
3127 ret = RegSetValueExW(hUserKey, szValueName, 0, REG_EXPAND_SZ,
3128 (LPBYTE)path, (lstrlenW(path) + 1) * sizeof(WCHAR));
3129 if (ret)
3130 hr = HRESULT_FROM_WIN32(ret);
3131 else
3133 hr = SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
3134 hToken, SHGFP_TYPE_DEFAULT, path);
3135 ret = RegSetValueExW(hKey, szValueName, 0, REG_SZ,
3136 (LPBYTE)path, (lstrlenW(path) + 1) * sizeof(WCHAR));
3137 if (ret)
3138 hr = HRESULT_FROM_WIN32(ret);
3142 else
3144 /* create the default dir, which may be different from the path
3145 * stored in the registry. */
3146 SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
3147 hToken, SHGFP_TYPE_DEFAULT, path);
3150 if (hUserKey)
3151 RegCloseKey(hUserKey);
3152 if (hKey)
3153 RegCloseKey(hKey);
3155 TRACE("returning 0x%08lx\n", hr);
3156 return hr;
3159 static HRESULT _SHRegisterUserShellFolders(BOOL bDefault)
3161 static const UINT folders[] = {
3162 CSIDL_PROGRAMS,
3163 CSIDL_PERSONAL,
3164 CSIDL_FAVORITES,
3165 CSIDL_APPDATA,
3166 CSIDL_STARTUP,
3167 CSIDL_RECENT,
3168 CSIDL_SENDTO,
3169 CSIDL_STARTMENU,
3170 CSIDL_MYMUSIC,
3171 CSIDL_MYVIDEO,
3172 CSIDL_DESKTOPDIRECTORY,
3173 CSIDL_NETHOOD,
3174 CSIDL_TEMPLATES,
3175 CSIDL_PRINTHOOD,
3176 CSIDL_LOCAL_APPDATA,
3177 CSIDL_INTERNET_CACHE,
3178 CSIDL_COOKIES,
3179 CSIDL_HISTORY,
3180 CSIDL_MYPICTURES,
3181 CSIDL_FONTS,
3182 CSIDL_ADMINTOOLS,
3183 CSIDL_CONTACTS,
3184 CSIDL_DOWNLOADS,
3185 CSIDL_LINKS,
3186 CSIDL_APPDATA_LOCALLOW,
3187 CSIDL_SAVED_GAMES,
3188 CSIDL_SEARCHES
3190 WCHAR userShellFolderPath[MAX_PATH], shellFolderPath[MAX_PATH];
3191 LPCWSTR pUserShellFolderPath, pShellFolderPath;
3192 HRESULT hr = S_OK;
3193 HKEY hRootKey;
3194 HANDLE hToken;
3196 TRACE("%s\n", bDefault ? "TRUE" : "FALSE");
3197 if (bDefault)
3199 hToken = (HANDLE)-1;
3200 hRootKey = HKEY_USERS;
3201 lstrcpyW(userShellFolderPath, L".Default");
3202 PathAddBackslashW(userShellFolderPath);
3203 lstrcatW(userShellFolderPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders");
3204 pUserShellFolderPath = userShellFolderPath;
3205 lstrcpyW(shellFolderPath, L".Default");
3206 PathAddBackslashW(shellFolderPath);
3207 lstrcatW(shellFolderPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
3208 pShellFolderPath = shellFolderPath;
3210 else
3212 hToken = NULL;
3213 hRootKey = HKEY_CURRENT_USER;
3214 pUserShellFolderPath = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders";
3215 pShellFolderPath = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
3218 hr = _SHRegisterFolders(hRootKey, hToken, pUserShellFolderPath,
3219 pShellFolderPath, folders, ARRAY_SIZE(folders));
3220 TRACE("returning 0x%08lx\n", hr);
3221 return hr;
3224 static HRESULT _SHRegisterCommonShellFolders(void)
3226 static const UINT folders[] = {
3227 CSIDL_COMMON_STARTMENU,
3228 CSIDL_COMMON_PROGRAMS,
3229 CSIDL_COMMON_STARTUP,
3230 CSIDL_COMMON_DESKTOPDIRECTORY,
3231 CSIDL_COMMON_FAVORITES,
3232 CSIDL_COMMON_APPDATA,
3233 CSIDL_COMMON_TEMPLATES,
3234 CSIDL_COMMON_DOCUMENTS,
3235 CSIDL_COMMON_ADMINTOOLS,
3236 CSIDL_COMMON_MUSIC,
3237 CSIDL_COMMON_PICTURES,
3238 CSIDL_COMMON_VIDEO,
3240 HRESULT hr;
3242 TRACE("\n");
3243 hr = _SHRegisterFolders(HKEY_LOCAL_MACHINE, NULL,
3244 L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders",
3245 L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
3246 folders, ARRAY_SIZE(folders));
3247 TRACE("returning 0x%08lx\n", hr);
3248 return hr;
3251 /******************************************************************************
3252 * create_extra_folders [Internal]
3254 * Create some extra folders that don't have a standard CSIDL definition.
3256 static HRESULT create_extra_folders(void)
3258 WCHAR path[MAX_PATH+5];
3259 HRESULT hr;
3260 HKEY hkey;
3261 DWORD type, size, ret;
3263 ret = RegCreateKeyW( HKEY_CURRENT_USER, L"Environment", &hkey );
3264 if (ret) return HRESULT_FROM_WIN32( ret );
3266 /* FIXME: should be under AppData, but we don't want spaces in the temp path */
3267 hr = SHGetFolderPathAndSubDirW( 0, CSIDL_PROFILE | CSIDL_FLAG_CREATE, NULL,
3268 SHGFP_TYPE_DEFAULT, L"Temp", path );
3269 if (SUCCEEDED(hr))
3271 size = sizeof(path);
3272 if (RegQueryValueExW( hkey, L"TEMP", NULL, &type, (LPBYTE)path, &size ))
3273 RegSetValueExW( hkey, L"TEMP", 0, REG_SZ, (LPBYTE)path, (lstrlenW(path) + 1) * sizeof(WCHAR) );
3274 size = sizeof(path);
3275 if (RegQueryValueExW( hkey, L"TMP", NULL, &type, (LPBYTE)path, &size ))
3276 RegSetValueExW( hkey, L"TMP", 0, REG_SZ, (LPBYTE)path, (lstrlenW(path) + 1) * sizeof(WCHAR) );
3278 RegCloseKey( hkey );
3280 if (SUCCEEDED(hr))
3282 hr = SHGetFolderPathAndSubDirW( 0, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, NULL,
3283 SHGFP_TYPE_DEFAULT, L"Microsoft", path );
3285 if (SUCCEEDED(hr))
3287 hr = SHGetFolderPathAndSubDirW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL,
3288 SHGFP_TYPE_DEFAULT, L"Microsoft\\Windows\\Themes", path);
3290 return hr;
3294 /******************************************************************************
3295 * set_folder_attributes
3297 * Set the various folder attributes registry keys.
3299 static HRESULT set_folder_attributes(void)
3301 static const struct
3303 const CLSID *clsid;
3304 unsigned int wfparsing : 1;
3305 unsigned int wfdisplay : 1;
3306 unsigned int hideasdel : 1;
3307 DWORD attr;
3308 DWORD call_for_attr;
3309 } folders[] =
3311 { &CLSID_UnixFolder, TRUE, FALSE, FALSE },
3312 { &CLSID_UnixDosFolder, TRUE, FALSE, FALSE,
3313 SFGAO_FILESYSANCESTOR|SFGAO_FOLDER|SFGAO_HASSUBFOLDER, SFGAO_FILESYSTEM },
3314 { &CLSID_FolderShortcut, FALSE, FALSE, FALSE,
3315 SFGAO_FILESYSTEM|SFGAO_FOLDER|SFGAO_LINK,
3316 SFGAO_HASSUBFOLDER|SFGAO_FILESYSTEM|SFGAO_FOLDER|SFGAO_FILESYSANCESTOR },
3317 { &CLSID_MyDocuments, TRUE, FALSE, FALSE,
3318 SFGAO_FILESYSANCESTOR|SFGAO_FOLDER|SFGAO_HASSUBFOLDER, SFGAO_FILESYSTEM },
3319 { &CLSID_RecycleBin, FALSE, FALSE, FALSE,
3320 SFGAO_FOLDER|SFGAO_DROPTARGET|SFGAO_HASPROPSHEET },
3321 { &CLSID_ControlPanel, FALSE, TRUE, TRUE,
3322 SFGAO_FOLDER|SFGAO_HASSUBFOLDER }
3325 unsigned int i;
3326 WCHAR buffer[39 + ARRAY_SIZE(L"CLSID\\") + ARRAY_SIZE(L"\\ShellFolder")];
3327 LONG res;
3328 HKEY hkey;
3330 for (i = 0; i < ARRAY_SIZE(folders); i++)
3332 lstrcpyW( buffer, L"CLSID\\" );
3333 StringFromGUID2( folders[i].clsid, buffer + lstrlenW(buffer), 39 );
3334 lstrcatW( buffer, L"\\ShellFolder" );
3335 res = RegCreateKeyExW( HKEY_CLASSES_ROOT, buffer, 0, NULL, 0,
3336 KEY_READ | KEY_WRITE, NULL, &hkey, NULL);
3337 if (res) return HRESULT_FROM_WIN32( res );
3338 if (folders[i].wfparsing)
3339 res = RegSetValueExW( hkey, L"WantsFORPARSING", 0, REG_SZ, (const BYTE *)L"", sizeof(WCHAR) );
3340 if (folders[i].wfdisplay)
3341 res = RegSetValueExW( hkey, L"WantsFORDISPLAY", 0, REG_SZ, (const BYTE *)L"", sizeof(WCHAR) );
3342 if (folders[i].hideasdel)
3343 res = RegSetValueExW( hkey, L"HideAsDeletePerUser", 0, REG_SZ, (const BYTE *)L"", sizeof(WCHAR) );
3344 if (folders[i].attr)
3345 res = RegSetValueExW( hkey, L"Attributes", 0, REG_DWORD,
3346 (const BYTE *)&folders[i].attr, sizeof(DWORD));
3347 if (folders[i].call_for_attr)
3348 res = RegSetValueExW( hkey, L"CallForAttributes", 0, REG_DWORD,
3349 (const BYTE *)&folders[i].call_for_attr, sizeof(DWORD));
3350 RegCloseKey( hkey );
3352 return S_OK;
3355 /*************************************************************************
3356 * SHGetSpecialFolderPathA [SHELL32.@]
3358 BOOL WINAPI SHGetSpecialFolderPathA (
3359 HWND hwndOwner,
3360 LPSTR szPath,
3361 int nFolder,
3362 BOOL bCreate)
3364 return SHGetFolderPathA(hwndOwner, nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0), NULL, 0,
3365 szPath) == S_OK;
3368 /*************************************************************************
3369 * SHGetSpecialFolderPathW
3371 BOOL WINAPI SHGetSpecialFolderPathW (
3372 HWND hwndOwner,
3373 LPWSTR szPath,
3374 int nFolder,
3375 BOOL bCreate)
3377 return SHGetFolderPathW(hwndOwner, nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0), NULL, 0,
3378 szPath) == S_OK;
3381 /*************************************************************************
3382 * SHGetSpecialFolderPath (SHELL32.175)
3384 BOOL WINAPI SHGetSpecialFolderPathAW (
3385 HWND hwndOwner,
3386 LPVOID szPath,
3387 int nFolder,
3388 BOOL bCreate)
3391 if (SHELL_OsIsUnicode())
3392 return SHGetSpecialFolderPathW (hwndOwner, szPath, nFolder, bCreate);
3393 return SHGetSpecialFolderPathA (hwndOwner, szPath, nFolder, bCreate);
3396 /*************************************************************************
3397 * SHGetFolderLocation [SHELL32.@]
3399 * Gets the folder locations from the registry and creates a pidl.
3401 * PARAMS
3402 * hwndOwner [I]
3403 * nFolder [I] CSIDL_xxxxx
3404 * hToken [I] token representing user, or NULL for current user, or -1 for
3405 * default user
3406 * dwReserved [I] must be zero
3407 * ppidl [O] PIDL of a special folder
3409 * RETURNS
3410 * Success: S_OK
3411 * Failure: Standard OLE-defined error result, S_FALSE or E_INVALIDARG
3413 * NOTES
3414 * Creates missing reg keys and directories.
3415 * Mostly forwards to SHGetFolderPathW, but a few values of nFolder return
3416 * virtual folders that are handled here.
3418 HRESULT WINAPI SHGetFolderLocation(
3419 HWND hwndOwner,
3420 int nFolder,
3421 HANDLE hToken,
3422 DWORD dwReserved,
3423 LPITEMIDLIST *ppidl)
3425 HRESULT hr = E_INVALIDARG;
3427 TRACE("%p 0x%08x %p 0x%08lx %p\n",
3428 hwndOwner, nFolder, hToken, dwReserved, ppidl);
3430 if (!ppidl)
3431 return E_INVALIDARG;
3432 if (dwReserved)
3433 return E_INVALIDARG;
3435 /* The virtual folders' locations are not user-dependent */
3436 *ppidl = NULL;
3437 switch (nFolder & CSIDL_FOLDER_MASK)
3439 case CSIDL_DESKTOP:
3440 *ppidl = _ILCreateDesktop();
3441 break;
3443 case CSIDL_PERSONAL:
3444 *ppidl = _ILCreateMyDocuments();
3445 break;
3447 case CSIDL_INTERNET:
3448 *ppidl = _ILCreateIExplore();
3449 break;
3451 case CSIDL_CONTROLS:
3452 *ppidl = _ILCreateControlPanel();
3453 break;
3455 case CSIDL_PRINTERS:
3456 *ppidl = _ILCreatePrinters();
3457 break;
3459 case CSIDL_BITBUCKET:
3460 *ppidl = _ILCreateBitBucket();
3461 break;
3463 case CSIDL_DRIVES:
3464 *ppidl = _ILCreateMyComputer();
3465 break;
3467 case CSIDL_NETWORK:
3468 *ppidl = _ILCreateNetwork();
3469 break;
3471 default:
3473 WCHAR szPath[MAX_PATH];
3475 hr = SHGetFolderPathW(hwndOwner, nFolder, hToken,
3476 SHGFP_TYPE_CURRENT, szPath);
3477 if (SUCCEEDED(hr))
3479 DWORD attributes=0;
3481 TRACE("Value=%s\n", debugstr_w(szPath));
3482 hr = SHILCreateFromPathW(szPath, ppidl, &attributes);
3484 else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
3486 /* unlike SHGetFolderPath, SHGetFolderLocation in shell32
3487 * version 6.0 returns E_FAIL for nonexistent paths
3489 hr = E_FAIL;
3493 if(*ppidl)
3494 hr = S_OK;
3496 TRACE("-- (new pidl %p)\n",*ppidl);
3497 return hr;
3500 /*************************************************************************
3501 * SHGetSpecialFolderLocation [SHELL32.@]
3503 * NOTES
3504 * In NT5, SHGetSpecialFolderLocation needs the <winntdir>/Recent
3505 * directory.
3507 HRESULT WINAPI SHGetSpecialFolderLocation(
3508 HWND hwndOwner,
3509 INT nFolder,
3510 LPITEMIDLIST * ppidl)
3512 HRESULT hr = E_INVALIDARG;
3514 TRACE("(%p,0x%x,%p)\n", hwndOwner,nFolder,ppidl);
3516 if (!ppidl)
3517 return E_INVALIDARG;
3519 hr = SHGetFolderLocation(hwndOwner, nFolder, NULL, 0, ppidl);
3520 return hr;
3523 /*************************************************************************
3524 * SHGetKnownFolderPath [SHELL32.@]
3526 HRESULT WINAPI SHGetKnownFolderPath(REFKNOWNFOLDERID rfid, DWORD flags, HANDLE token, WCHAR **ret_path)
3528 WCHAR pathW[MAX_PATH];
3529 HRESULT hr;
3530 int folder = csidl_from_id(rfid), shgfp_flags;
3532 TRACE("%s, 0x%08lx, %p, %p\n", debugstr_guid(rfid), flags, token, ret_path);
3534 *ret_path = NULL;
3536 if (folder < 0)
3537 return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
3539 if (flags & ~(KF_FLAG_CREATE|KF_FLAG_SIMPLE_IDLIST|KF_FLAG_DONT_UNEXPAND|
3540 KF_FLAG_DONT_VERIFY|KF_FLAG_NO_ALIAS|KF_FLAG_INIT|KF_FLAG_DEFAULT_PATH|KF_FLAG_NOT_PARENT_RELATIVE))
3542 FIXME("flags 0x%08lx not supported\n", flags);
3543 return E_INVALIDARG;
3546 if ((flags & (KF_FLAG_DEFAULT_PATH | KF_FLAG_NOT_PARENT_RELATIVE)) == KF_FLAG_NOT_PARENT_RELATIVE)
3548 WARN("Invalid flags mask %#lx.\n", flags);
3549 return E_INVALIDARG;
3552 if (flags & KF_FLAG_NOT_PARENT_RELATIVE)
3554 FIXME("Ignoring KF_FLAG_NOT_PARENT_RELATIVE.\n");
3555 flags &= ~KF_FLAG_NOT_PARENT_RELATIVE;
3558 folder |= flags & CSIDL_FLAG_MASK;
3559 shgfp_flags = flags & KF_FLAG_DEFAULT_PATH ? SHGFP_TYPE_DEFAULT : SHGFP_TYPE_CURRENT;
3561 hr = SHGetFolderPathAndSubDirW( 0, folder, token, shgfp_flags, NULL, pathW );
3562 if (FAILED( hr ))
3564 TRACE("Failed to get folder path, %#lx.\n", hr);
3565 return hr;
3568 TRACE("Final path is %s, %#lx\n", debugstr_w(pathW), hr);
3570 *ret_path = CoTaskMemAlloc((lstrlenW(pathW) + 1) * sizeof(WCHAR));
3571 if (!*ret_path)
3572 return E_OUTOFMEMORY;
3573 lstrcpyW(*ret_path, pathW);
3575 return hr;
3578 /*************************************************************************
3579 * SHGetFolderPathEx [SHELL32.@]
3581 HRESULT WINAPI SHGetFolderPathEx(REFKNOWNFOLDERID rfid, DWORD flags, HANDLE token, LPWSTR path, DWORD len)
3583 HRESULT hr;
3584 WCHAR *buffer;
3586 TRACE("%s, 0x%08lx, %p, %p, %lu\n", debugstr_guid(rfid), flags, token, path, len);
3588 if (!path || !len) return E_INVALIDARG;
3590 hr = SHGetKnownFolderPath( rfid, flags, token, &buffer );
3591 if (SUCCEEDED( hr ))
3593 if (lstrlenW( buffer ) + 1 > len)
3595 CoTaskMemFree( buffer );
3596 return HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER );
3598 lstrcpyW( path, buffer );
3599 CoTaskMemFree( buffer );
3601 return hr;
3605 * Internal function to convert known folder identifier to path of registry key
3606 * associated with known folder.
3608 * Parameters:
3609 * rfid [I] pointer to known folder identifier (may be NULL)
3610 * lpStringGuid [I] string with known folder identifier (used when rfid is NULL)
3611 * lpPath [O] place to store string address. String should be
3612 * later freed using HeapFree(GetProcessHeap(),0, ... )
3614 static HRESULT get_known_folder_registry_path(
3615 REFKNOWNFOLDERID rfid,
3616 LPWSTR lpStringGuid,
3617 LPWSTR *lpPath)
3619 HRESULT hr = S_OK;
3620 int length;
3621 WCHAR sGuid[50];
3623 TRACE("(%s, %s, %p)\n", debugstr_guid(rfid), debugstr_w(lpStringGuid), lpPath);
3625 if(rfid)
3626 StringFromGUID2(rfid, sGuid, ARRAY_SIZE(sGuid));
3627 else
3628 lstrcpyW(sGuid, lpStringGuid);
3630 length = lstrlenW(L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FolderDescriptions")+51;
3631 *lpPath = malloc(length * sizeof(WCHAR));
3632 if(!(*lpPath))
3633 hr = E_OUTOFMEMORY;
3635 if(SUCCEEDED(hr))
3637 lstrcpyW(*lpPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FolderDescriptions\\");
3638 lstrcatW(*lpPath, sGuid);
3641 return hr;
3644 static HRESULT get_known_folder_wstr(const WCHAR *regpath, const WCHAR *value, WCHAR **out)
3646 DWORD size = 0;
3647 HRESULT hr;
3649 size = 0;
3650 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, regpath, value, RRF_RT_REG_SZ, NULL, NULL, &size));
3651 if(FAILED(hr))
3652 return hr;
3654 *out = CoTaskMemAlloc(size);
3655 if(!*out)
3656 return E_OUTOFMEMORY;
3658 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, regpath, value, RRF_RT_REG_SZ, NULL, *out, &size));
3659 if(FAILED(hr)){
3660 CoTaskMemFree(*out);
3661 *out = NULL;
3664 return hr;
3667 static HRESULT get_known_folder_dword(const WCHAR *registryPath, const WCHAR *value, DWORD *out)
3669 DWORD dwSize = sizeof(DWORD);
3670 DWORD dwType;
3671 return HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, registryPath, value, RRF_RT_DWORD, &dwType, out, &dwSize));
3675 * Internal function to get place where folder redirection information are stored.
3677 * Parameters:
3678 * rfid [I] pointer to known folder identifier (may be NULL)
3679 * rootKey [O] root key where the redirection information are stored
3680 * It can be HKLM for COMMON folders, and HKCU for PERUSER folders.
3682 static HRESULT get_known_folder_redirection_place(
3683 REFKNOWNFOLDERID rfid,
3684 HKEY *rootKey)
3686 HRESULT hr;
3687 LPWSTR lpRegistryPath = NULL;
3688 DWORD category;
3690 /* first, get known folder's category */
3691 hr = get_known_folder_registry_path(rfid, NULL, &lpRegistryPath);
3693 if(SUCCEEDED(hr))
3694 hr = get_known_folder_dword(lpRegistryPath, L"Category", &category);
3696 if(SUCCEEDED(hr))
3698 if(category == KF_CATEGORY_COMMON)
3700 *rootKey = HKEY_LOCAL_MACHINE;
3701 hr = S_OK;
3703 else if(category == KF_CATEGORY_PERUSER)
3705 *rootKey = HKEY_CURRENT_USER;
3706 hr = S_OK;
3708 else
3709 hr = E_FAIL;
3712 free(lpRegistryPath);
3713 return hr;
3716 static HRESULT get_known_folder_path_by_id(REFKNOWNFOLDERID folderId, LPWSTR lpRegistryPath, DWORD dwFlags, LPWSTR *ppszPath);
3718 static HRESULT redirect_known_folder(
3719 REFKNOWNFOLDERID rfid,
3720 HWND hwnd,
3721 KF_REDIRECT_FLAGS flags,
3722 LPCWSTR pszTargetPath,
3723 UINT cFolders,
3724 KNOWNFOLDERID const *pExclusion,
3725 LPWSTR *ppszError)
3727 HRESULT hr;
3728 HKEY rootKey = HKEY_LOCAL_MACHINE, hKey;
3729 WCHAR sGuid[39];
3730 LPWSTR lpRegistryPath = NULL, lpSrcPath = NULL;
3731 TRACE("(%s, %p, 0x%08x, %s, %d, %p, %p)\n", debugstr_guid(rfid), hwnd, flags, debugstr_w(pszTargetPath), cFolders, pExclusion, ppszError);
3733 if (ppszError) *ppszError = NULL;
3735 hr = get_known_folder_registry_path(rfid, NULL, &lpRegistryPath);
3737 if(SUCCEEDED(hr))
3738 hr = get_known_folder_path_by_id(rfid, lpRegistryPath, 0, &lpSrcPath);
3740 free(lpRegistryPath);
3742 /* get path to redirection storage */
3743 if(SUCCEEDED(hr))
3744 hr = get_known_folder_redirection_place(rfid, &rootKey);
3746 /* write redirection information */
3747 if(SUCCEEDED(hr))
3748 hr = HRESULT_FROM_WIN32(RegCreateKeyExW(rootKey, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL));
3750 if(SUCCEEDED(hr))
3752 StringFromGUID2(rfid, sGuid, ARRAY_SIZE(sGuid));
3754 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sGuid, 0, REG_SZ, (LPBYTE)pszTargetPath, (lstrlenW(pszTargetPath)+1)*sizeof(WCHAR)));
3756 RegCloseKey(hKey);
3759 /* make sure destination path exists */
3760 SHCreateDirectory(NULL, pszTargetPath);
3762 /* copy content if required */
3763 if(SUCCEEDED(hr) && (flags & KF_REDIRECT_COPY_CONTENTS) )
3765 WCHAR srcPath[MAX_PATH+1], dstPath[MAX_PATH+1];
3766 SHFILEOPSTRUCTW fileOp;
3768 ZeroMemory(srcPath, sizeof(srcPath));
3769 lstrcpyW(srcPath, lpSrcPath);
3770 lstrcatW(srcPath, L"\\*");
3772 ZeroMemory(dstPath, sizeof(dstPath));
3773 lstrcpyW(dstPath, pszTargetPath);
3775 ZeroMemory(&fileOp, sizeof(fileOp));
3777 if(flags & KF_REDIRECT_DEL_SOURCE_CONTENTS)
3778 fileOp.wFunc = FO_MOVE;
3779 else
3780 fileOp.wFunc = FO_COPY;
3782 fileOp.pFrom = srcPath;
3783 fileOp.pTo = dstPath;
3784 fileOp.fFlags = FOF_NO_UI;
3786 hr = (SHFileOperationW(&fileOp)==0 ? S_OK : E_FAIL);
3788 if(flags & KF_REDIRECT_DEL_SOURCE_CONTENTS)
3790 ZeroMemory(srcPath, sizeof(srcPath));
3791 lstrcpyW(srcPath, lpSrcPath);
3793 ZeroMemory(&fileOp, sizeof(fileOp));
3794 fileOp.wFunc = FO_DELETE;
3795 fileOp.pFrom = srcPath;
3796 fileOp.fFlags = FOF_NO_UI;
3798 hr = (SHFileOperationW(&fileOp)==0 ? S_OK : E_FAIL);
3802 CoTaskMemFree(lpSrcPath);
3804 return hr;
3808 struct knownfolder
3810 IKnownFolder IKnownFolder_iface;
3811 LONG refs;
3812 KNOWNFOLDERID id;
3813 LPWSTR registryPath;
3816 static inline struct knownfolder *impl_from_IKnownFolder( IKnownFolder *iface )
3818 return CONTAINING_RECORD( iface, struct knownfolder, IKnownFolder_iface );
3821 static ULONG WINAPI knownfolder_AddRef(
3822 IKnownFolder *iface )
3824 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
3825 return InterlockedIncrement( &knownfolder->refs );
3828 static ULONG WINAPI knownfolder_Release(
3829 IKnownFolder *iface )
3831 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
3832 LONG refs = InterlockedDecrement( &knownfolder->refs );
3833 if (!refs)
3835 TRACE("destroying %p\n", knownfolder);
3836 free( knownfolder->registryPath );
3837 free( knownfolder );
3839 return refs;
3842 static HRESULT WINAPI knownfolder_QueryInterface(
3843 IKnownFolder *iface,
3844 REFIID riid,
3845 void **ppv )
3847 struct knownfolder *This = impl_from_IKnownFolder( iface );
3849 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppv );
3851 *ppv = NULL;
3852 if ( IsEqualGUID( riid, &IID_IKnownFolder ) ||
3853 IsEqualGUID( riid, &IID_IUnknown ) )
3855 *ppv = iface;
3857 else if ( IsEqualGUID( riid, &IID_IMarshal ) )
3859 TRACE("IID_IMarshal returning NULL.\n");
3860 return E_NOINTERFACE;
3862 else
3864 FIXME("interface %s not implemented\n", debugstr_guid(riid));
3865 return E_NOINTERFACE;
3867 IKnownFolder_AddRef( iface );
3868 return S_OK;
3871 static HRESULT knownfolder_set_id(
3872 struct knownfolder *knownfolder,
3873 const KNOWNFOLDERID *kfid)
3875 HKEY hKey;
3876 HRESULT hr;
3878 TRACE("%s\n", debugstr_guid(kfid));
3880 knownfolder->id = *kfid;
3882 /* check is it registry-registered folder */
3883 hr = get_known_folder_registry_path(kfid, NULL, &knownfolder->registryPath);
3884 if(SUCCEEDED(hr))
3885 hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, knownfolder->registryPath, 0, 0, &hKey));
3887 if(SUCCEEDED(hr))
3889 hr = S_OK;
3890 RegCloseKey(hKey);
3892 else
3894 /* This known folder is not registered. To mark it, we set registryPath to NULL */
3895 free(knownfolder->registryPath);
3896 knownfolder->registryPath = NULL;
3897 hr = S_OK;
3900 return hr;
3903 static HRESULT WINAPI knownfolder_GetId(
3904 IKnownFolder *iface,
3905 KNOWNFOLDERID *pkfid)
3907 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
3909 TRACE("%p\n", pkfid);
3911 *pkfid = knownfolder->id;
3912 return S_OK;
3915 static HRESULT WINAPI knownfolder_GetCategory(
3916 IKnownFolder *iface,
3917 KF_CATEGORY *pCategory)
3919 struct knownfolder *knownfolder = impl_from_IKnownFolder(iface);
3920 HRESULT hr = S_OK;
3922 TRACE("%p, %p\n", knownfolder, pCategory);
3924 /* we cannot get a category for a folder which is not registered */
3925 if(!knownfolder->registryPath)
3926 hr = E_FAIL;
3928 if(SUCCEEDED(hr))
3929 hr = get_known_folder_dword(knownfolder->registryPath, L"Category", (DWORD *)pCategory);
3931 return hr;
3934 static HRESULT WINAPI knownfolder_GetShellItem(
3935 IKnownFolder *iface,
3936 DWORD flags,
3937 REFIID riid,
3938 void **ppv)
3940 struct knownfolder *knownfolder = impl_from_IKnownFolder(iface);
3941 TRACE("(%p, 0x%08lx, %s, %p)\n", knownfolder, flags, debugstr_guid(riid), ppv);
3942 return SHGetKnownFolderItem(&knownfolder->id, flags, NULL, riid, ppv);
3945 static HRESULT get_known_folder_path(
3946 LPWSTR sFolderId,
3947 LPWSTR registryPath,
3948 LPWSTR *ppszPath)
3950 HRESULT hr;
3951 DWORD dwSize, dwType;
3952 WCHAR path[MAX_PATH] = {0};
3953 WCHAR parentGuid[39];
3954 DWORD category;
3955 LPWSTR parentRegistryPath, parentPath;
3956 HKEY hRedirectionRootKey = NULL;
3958 TRACE("(%s, %p)\n", debugstr_w(registryPath), ppszPath);
3959 *ppszPath = NULL;
3961 /* check if folder has parent */
3962 dwSize = sizeof(parentGuid);
3963 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, registryPath, L"ParentFolder", RRF_RT_REG_SZ, &dwType, parentGuid, &dwSize));
3964 if(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) hr = S_FALSE;
3966 if(hr == S_OK)
3968 /* get parent's known folder path (recursive) */
3969 hr = get_known_folder_registry_path(NULL, parentGuid, &parentRegistryPath);
3970 if(FAILED(hr)) return hr;
3972 hr = get_known_folder_path(parentGuid, parentRegistryPath, &parentPath);
3973 if(FAILED(hr)) {
3974 free(parentRegistryPath);
3975 return hr;
3978 lstrcatW(path, parentPath);
3979 lstrcatW(path, L"\\");
3981 free(parentRegistryPath);
3982 free(parentPath);
3985 /* check, if folder was redirected */
3986 if(SUCCEEDED(hr))
3987 hr = get_known_folder_dword(registryPath, L"Category", &category);
3989 if(SUCCEEDED(hr))
3991 if(category == KF_CATEGORY_COMMON)
3992 hRedirectionRootKey = HKEY_LOCAL_MACHINE;
3993 else if(category == KF_CATEGORY_PERUSER)
3994 hRedirectionRootKey = HKEY_CURRENT_USER;
3996 if(hRedirectionRootKey)
3998 hr = HRESULT_FROM_WIN32(RegGetValueW(hRedirectionRootKey, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders", sFolderId, RRF_RT_REG_SZ, NULL, NULL, &dwSize));
4000 if(SUCCEEDED(hr))
4002 *ppszPath = CoTaskMemAlloc(dwSize+(lstrlenW(path)+1)*sizeof(WCHAR));
4003 if(!*ppszPath) hr = E_OUTOFMEMORY;
4006 if(SUCCEEDED(hr))
4008 lstrcpyW(*ppszPath, path);
4009 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));
4013 if(!*ppszPath)
4015 /* no redirection, use previous way - read the relative path from folder definition */
4016 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, registryPath, L"RelativePath", RRF_RT_REG_SZ, &dwType, NULL, &dwSize));
4018 if(SUCCEEDED(hr))
4020 *ppszPath = CoTaskMemAlloc(dwSize+(lstrlenW(path)+1)*sizeof(WCHAR));
4021 if(!*ppszPath) hr = E_OUTOFMEMORY;
4024 if(SUCCEEDED(hr))
4026 lstrcpyW(*ppszPath, path);
4027 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, registryPath, L"RelativePath", RRF_RT_REG_SZ, &dwType, *ppszPath + lstrlenW(path), &dwSize));
4032 TRACE("returning path: %s\n", debugstr_w(*ppszPath));
4033 return hr;
4036 static HRESULT get_known_folder_path_by_id(
4037 REFKNOWNFOLDERID folderId,
4038 LPWSTR lpRegistryPath,
4039 DWORD dwFlags,
4040 LPWSTR *ppszPath)
4042 HRESULT hr = E_FAIL;
4043 WCHAR sGuid[39];
4044 DWORD dwAttributes;
4046 TRACE("(%s, %s, 0x%08lx, %p)\n", debugstr_guid(folderId), debugstr_w(lpRegistryPath), dwFlags, ppszPath);
4048 /* if this is registry-registered known folder, get path from registry */
4049 if(lpRegistryPath)
4051 StringFromGUID2(folderId, sGuid, ARRAY_SIZE(sGuid));
4053 hr = get_known_folder_path(sGuid, lpRegistryPath, ppszPath);
4055 /* in other case, use older way */
4057 if(FAILED(hr))
4058 hr = SHGetKnownFolderPath( folderId, dwFlags, NULL, ppszPath );
4060 if (FAILED(hr)) return hr;
4062 /* check if known folder really exists */
4063 dwAttributes = GetFileAttributesW(*ppszPath);
4064 if(dwAttributes == INVALID_FILE_ATTRIBUTES || !(dwAttributes & FILE_ATTRIBUTE_DIRECTORY) )
4066 TRACE("directory %s not found\n", debugstr_w(*ppszPath));
4067 CoTaskMemFree(*ppszPath);
4068 *ppszPath = NULL;
4069 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
4072 return hr;
4075 static HRESULT WINAPI knownfolder_GetPath(
4076 IKnownFolder *iface,
4077 DWORD dwFlags,
4078 LPWSTR *ppszPath)
4080 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
4081 TRACE("(%p, 0x%08lx, %p)\n", knownfolder, dwFlags, ppszPath);
4083 return get_known_folder_path_by_id(&knownfolder->id, knownfolder->registryPath, dwFlags, ppszPath);
4086 static HRESULT WINAPI knownfolder_SetPath(
4087 IKnownFolder *iface,
4088 DWORD dwFlags,
4089 LPCWSTR pszPath)
4091 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
4092 HRESULT hr = S_OK;
4094 TRACE("(%p, 0x%08lx, %s)\n", knownfolder, dwFlags, debugstr_w(pszPath));
4096 /* check if the known folder is registered */
4097 if(!knownfolder->registryPath)
4098 hr = E_FAIL;
4100 if(SUCCEEDED(hr))
4101 hr = redirect_known_folder(&knownfolder->id, NULL, 0, pszPath, 0, NULL, NULL);
4103 return hr;
4106 static HRESULT WINAPI knownfolder_GetIDList(
4107 IKnownFolder *iface,
4108 DWORD flags,
4109 PIDLIST_ABSOLUTE *ppidl)
4111 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
4112 TRACE("(%p, 0x%08lx, %p)\n", knownfolder, flags, ppidl);
4113 return SHGetKnownFolderIDList(&knownfolder->id, flags, NULL, ppidl);
4116 static HRESULT WINAPI knownfolder_GetFolderType(
4117 IKnownFolder *iface,
4118 FOLDERTYPEID *pftid)
4120 FIXME("%p\n", pftid);
4121 return E_NOTIMPL;
4124 static HRESULT WINAPI knownfolder_GetRedirectionCapabilities(
4125 IKnownFolder *iface,
4126 KF_REDIRECTION_CAPABILITIES *pCapabilities)
4128 FIXME("%p stub\n", pCapabilities);
4129 if(!pCapabilities) return E_INVALIDARG;
4130 *pCapabilities = KF_REDIRECTION_CAPABILITIES_DENY_ALL;
4131 return S_OK;
4134 static HRESULT WINAPI knownfolder_GetFolderDefinition(
4135 IKnownFolder *iface,
4136 KNOWNFOLDER_DEFINITION *pKFD)
4138 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
4139 HRESULT hr;
4140 DWORD dwSize;
4141 WCHAR parentGuid[39];
4142 TRACE("(%p, %p)\n", knownfolder, pKFD);
4144 if(!pKFD) return E_INVALIDARG;
4146 ZeroMemory(pKFD, sizeof(*pKFD));
4148 /* required fields */
4149 hr = get_known_folder_dword(knownfolder->registryPath, L"Category", (DWORD *)&pKFD->category);
4150 if(FAILED(hr))
4151 return hr;
4153 hr = get_known_folder_wstr(knownfolder->registryPath, L"Name", &pKFD->pszName);
4154 if(FAILED(hr))
4155 return hr;
4157 /* optional fields */
4158 dwSize = sizeof(parentGuid);
4159 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, knownfolder->registryPath, L"ParentFolder",
4160 RRF_RT_REG_SZ, NULL, parentGuid, &dwSize));
4161 if(SUCCEEDED(hr))
4163 hr = IIDFromString(parentGuid, &pKFD->fidParent);
4164 if(FAILED(hr))
4165 return hr;
4168 get_known_folder_dword(knownfolder->registryPath, L"Attributes", &pKFD->dwAttributes);
4170 get_known_folder_wstr(knownfolder->registryPath, L"RelativePath", &pKFD->pszRelativePath);
4172 get_known_folder_wstr(knownfolder->registryPath, L"ParsingName", &pKFD->pszParsingName);
4174 return S_OK;
4177 static const struct IKnownFolderVtbl knownfolder_vtbl =
4179 knownfolder_QueryInterface,
4180 knownfolder_AddRef,
4181 knownfolder_Release,
4182 knownfolder_GetId,
4183 knownfolder_GetCategory,
4184 knownfolder_GetShellItem,
4185 knownfolder_GetPath,
4186 knownfolder_SetPath,
4187 knownfolder_GetIDList,
4188 knownfolder_GetFolderType,
4189 knownfolder_GetRedirectionCapabilities,
4190 knownfolder_GetFolderDefinition
4193 static HRESULT knownfolder_create( struct knownfolder **knownfolder )
4195 struct knownfolder *kf;
4197 kf = malloc( sizeof(*kf) );
4198 if (!kf) return E_OUTOFMEMORY;
4200 kf->IKnownFolder_iface.lpVtbl = &knownfolder_vtbl;
4201 kf->refs = 1;
4202 memset( &kf->id, 0, sizeof(kf->id) );
4203 kf->registryPath = NULL;
4205 *knownfolder = kf;
4207 TRACE("returning iface %p\n", &kf->IKnownFolder_iface);
4208 return S_OK;
4211 struct foldermanager
4213 IKnownFolderManager IKnownFolderManager_iface;
4214 LONG refs;
4215 UINT num_ids;
4216 KNOWNFOLDERID *ids;
4219 static inline struct foldermanager *impl_from_IKnownFolderManager( IKnownFolderManager *iface )
4221 return CONTAINING_RECORD( iface, struct foldermanager, IKnownFolderManager_iface );
4224 static ULONG WINAPI foldermanager_AddRef(
4225 IKnownFolderManager *iface )
4227 struct foldermanager *foldermanager = impl_from_IKnownFolderManager( iface );
4228 return InterlockedIncrement( &foldermanager->refs );
4231 static ULONG WINAPI foldermanager_Release(
4232 IKnownFolderManager *iface )
4234 struct foldermanager *foldermanager = impl_from_IKnownFolderManager( iface );
4235 LONG refs = InterlockedDecrement( &foldermanager->refs );
4236 if (!refs)
4238 TRACE("destroying %p\n", foldermanager);
4239 free( foldermanager->ids );
4240 free( foldermanager );
4242 return refs;
4245 static HRESULT WINAPI foldermanager_QueryInterface(
4246 IKnownFolderManager *iface,
4247 REFIID riid,
4248 void **ppv )
4250 struct foldermanager *This = impl_from_IKnownFolderManager( iface );
4252 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppv );
4254 *ppv = NULL;
4255 if ( IsEqualGUID( riid, &IID_IKnownFolderManager ) ||
4256 IsEqualGUID( riid, &IID_IUnknown ) )
4258 *ppv = iface;
4260 else if ( IsEqualGUID( riid, &IID_IMarshal ) )
4262 TRACE("IID_IMarshal returning NULL.\n");
4263 return E_NOINTERFACE;
4265 else
4267 FIXME("interface %s not implemented\n", debugstr_guid(riid));
4268 return E_NOINTERFACE;
4270 IKnownFolderManager_AddRef( iface );
4271 return S_OK;
4274 static HRESULT WINAPI foldermanager_FolderIdFromCsidl(
4275 IKnownFolderManager *iface,
4276 int nCsidl,
4277 KNOWNFOLDERID *pfid)
4279 TRACE("%d, %p\n", nCsidl, pfid);
4281 if (nCsidl >= ARRAY_SIZE(CSIDL_Data))
4282 return E_INVALIDARG;
4283 *pfid = *CSIDL_Data[nCsidl].id;
4284 return S_OK;
4287 static HRESULT WINAPI foldermanager_FolderIdToCsidl(
4288 IKnownFolderManager *iface,
4289 REFKNOWNFOLDERID rfid,
4290 int *pnCsidl)
4292 int csidl;
4294 TRACE("%s, %p\n", debugstr_guid(rfid), pnCsidl);
4296 csidl = csidl_from_id( rfid );
4297 if (csidl == -1) return E_INVALIDARG;
4298 *pnCsidl = csidl;
4299 return S_OK;
4302 static HRESULT WINAPI foldermanager_GetFolderIds(
4303 IKnownFolderManager *iface,
4304 KNOWNFOLDERID **ppKFId,
4305 UINT *pCount)
4307 struct foldermanager *fm = impl_from_IKnownFolderManager( iface );
4309 TRACE("%p, %p\n", ppKFId, pCount);
4311 *ppKFId = CoTaskMemAlloc(fm->num_ids * sizeof(KNOWNFOLDERID));
4312 memcpy(*ppKFId, fm->ids, fm->num_ids * sizeof(KNOWNFOLDERID));
4313 *pCount = fm->num_ids;
4314 return S_OK;
4317 static BOOL is_knownfolder( struct foldermanager *fm, const KNOWNFOLDERID *id )
4319 UINT i;
4320 HRESULT hr;
4321 LPWSTR registryPath = NULL;
4322 HKEY hKey;
4324 /* TODO: move all entries from "CSIDL_Data" static array to registry known folder descriptions */
4325 for (i = 0; i < fm->num_ids; i++)
4326 if (IsEqualGUID( &fm->ids[i], id )) return TRUE;
4328 hr = get_known_folder_registry_path(id, NULL, &registryPath);
4329 if(SUCCEEDED(hr))
4331 hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, registryPath, 0, 0, &hKey));
4332 free(registryPath);
4335 if(SUCCEEDED(hr))
4337 hr = S_OK;
4338 RegCloseKey(hKey);
4341 return hr == S_OK;
4344 static HRESULT WINAPI foldermanager_GetFolder(
4345 IKnownFolderManager *iface,
4346 REFKNOWNFOLDERID rfid,
4347 IKnownFolder **ppkf)
4349 struct foldermanager *fm = impl_from_IKnownFolderManager( iface );
4350 struct knownfolder *kf;
4351 HRESULT hr;
4353 TRACE("%s, %p\n", debugstr_guid(rfid), ppkf);
4355 if (!is_knownfolder( fm, rfid ))
4357 WARN("unknown folder\n");
4358 return E_INVALIDARG;
4360 hr = knownfolder_create( &kf );
4361 if (SUCCEEDED( hr ))
4363 hr = knownfolder_set_id( kf, rfid );
4364 *ppkf = &kf->IKnownFolder_iface;
4366 else
4367 *ppkf = NULL;
4369 return hr;
4372 static HRESULT WINAPI foldermanager_GetFolderByName(
4373 IKnownFolderManager *iface,
4374 LPCWSTR pszCanonicalName,
4375 IKnownFolder **ppkf)
4377 struct foldermanager *fm = impl_from_IKnownFolderManager( iface );
4378 struct knownfolder *kf;
4379 BOOL found = FALSE;
4380 HRESULT hr;
4381 UINT i;
4383 TRACE( "%s, %p\n", debugstr_w(pszCanonicalName), ppkf );
4385 for (i = 0; i < fm->num_ids; i++)
4387 WCHAR *path, *name;
4388 hr = get_known_folder_registry_path( &fm->ids[i], NULL, &path );
4389 if (FAILED( hr )) return hr;
4391 hr = get_known_folder_wstr( path, L"Name", &name );
4392 free( path );
4393 if (FAILED( hr )) return hr;
4395 found = !wcsicmp( pszCanonicalName, name );
4396 CoTaskMemFree( name );
4397 if (found) break;
4400 if (found)
4402 hr = knownfolder_create( &kf );
4403 if (FAILED( hr )) return hr;
4405 hr = knownfolder_set_id( kf, &fm->ids[i] );
4406 if (FAILED( hr ))
4408 IKnownFolder_Release( &kf->IKnownFolder_iface );
4409 return hr;
4411 *ppkf = &kf->IKnownFolder_iface;
4413 else
4415 hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
4416 *ppkf = NULL;
4419 return hr;
4422 static HRESULT register_folder(const KNOWNFOLDERID *rfid, const KNOWNFOLDER_DEFINITION *pKFD)
4424 HRESULT hr;
4425 HKEY hKey = NULL;
4426 DWORD dwDisp;
4427 LPWSTR registryPath = NULL;
4429 hr = get_known_folder_registry_path(rfid, NULL, &registryPath);
4430 TRACE("registry path: %s\n", debugstr_w(registryPath));
4432 if(SUCCEEDED(hr))
4433 hr = HRESULT_FROM_WIN32(RegCreateKeyExW(HKEY_LOCAL_MACHINE, registryPath, 0, NULL, 0, KEY_WRITE, 0, &hKey, &dwDisp));
4435 if(SUCCEEDED(hr))
4437 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"Category", 0, REG_DWORD, (LPBYTE)&pKFD->category, sizeof(pKFD->category)));
4439 if(SUCCEEDED(hr) && pKFD->dwAttributes != 0)
4440 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"Attributes", 0, REG_DWORD, (LPBYTE)&pKFD->dwAttributes, sizeof(pKFD->dwAttributes)));
4442 if(SUCCEEDED(hr))
4443 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"Name", 0, REG_SZ, (LPBYTE)pKFD->pszName, (lstrlenW(pKFD->pszName)+1)*sizeof(WCHAR) ));
4445 if(SUCCEEDED(hr) && pKFD->pszParsingName)
4446 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"ParsingName", 0, REG_SZ, (LPBYTE)pKFD->pszParsingName, (lstrlenW(pKFD->pszParsingName)+1)*sizeof(WCHAR) ));
4448 if(SUCCEEDED(hr) && !IsEqualGUID(&pKFD->fidParent, &GUID_NULL))
4450 WCHAR sParentGuid[39];
4451 StringFromGUID2(&pKFD->fidParent, sParentGuid, ARRAY_SIZE(sParentGuid));
4453 /* this known folder has parent folder */
4454 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"ParentFolder", 0, REG_SZ, (LPBYTE)sParentGuid, sizeof(sParentGuid)));
4457 if(SUCCEEDED(hr) && pKFD->category != KF_CATEGORY_VIRTUAL && pKFD->pszRelativePath)
4458 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"RelativePath", 0, REG_SZ, (LPBYTE)pKFD->pszRelativePath, (lstrlenW(pKFD->pszRelativePath)+1)*sizeof(WCHAR) ));
4460 RegCloseKey(hKey);
4462 if(FAILED(hr))
4463 SHDeleteKeyW(HKEY_LOCAL_MACHINE, registryPath);
4466 free(registryPath);
4467 return hr;
4470 static HRESULT WINAPI foldermanager_RegisterFolder(
4471 IKnownFolderManager *iface,
4472 REFKNOWNFOLDERID rfid,
4473 KNOWNFOLDER_DEFINITION const *pKFD)
4475 TRACE("(%p, %s, %p)\n", iface, debugstr_guid(rfid), pKFD);
4476 return register_folder(rfid, pKFD);
4479 static HRESULT WINAPI foldermanager_UnregisterFolder(
4480 IKnownFolderManager *iface,
4481 REFKNOWNFOLDERID rfid)
4483 HRESULT hr;
4484 LPWSTR registryPath = NULL;
4485 TRACE("(%p, %s)\n", iface, debugstr_guid(rfid));
4487 hr = get_known_folder_registry_path(rfid, NULL, &registryPath);
4489 if(SUCCEEDED(hr))
4490 hr = HRESULT_FROM_WIN32(SHDeleteKeyW(HKEY_LOCAL_MACHINE, registryPath));
4492 free(registryPath);
4493 return hr;
4496 static HRESULT WINAPI foldermanager_FindFolderFromPath(
4497 IKnownFolderManager *iface,
4498 LPCWSTR pszPath,
4499 FFFP_MODE mode,
4500 IKnownFolder **ppkf)
4502 FIXME("%s, 0x%08x, %p\n", debugstr_w(pszPath), mode, ppkf);
4503 return E_NOTIMPL;
4506 static HRESULT WINAPI foldermanager_FindFolderFromIDList(
4507 IKnownFolderManager *iface,
4508 PCIDLIST_ABSOLUTE pidl,
4509 IKnownFolder **ppkf)
4511 FIXME("%p, %p\n", pidl, ppkf);
4512 return E_NOTIMPL;
4515 static HRESULT WINAPI foldermanager_Redirect(
4516 IKnownFolderManager *iface,
4517 REFKNOWNFOLDERID rfid,
4518 HWND hwnd,
4519 KF_REDIRECT_FLAGS flags,
4520 LPCWSTR pszTargetPath,
4521 UINT cFolders,
4522 KNOWNFOLDERID const *pExclusion,
4523 LPWSTR *ppszError)
4525 return redirect_known_folder(rfid, hwnd, flags, pszTargetPath, cFolders, pExclusion, ppszError);
4528 static const struct IKnownFolderManagerVtbl foldermanager_vtbl =
4530 foldermanager_QueryInterface,
4531 foldermanager_AddRef,
4532 foldermanager_Release,
4533 foldermanager_FolderIdFromCsidl,
4534 foldermanager_FolderIdToCsidl,
4535 foldermanager_GetFolderIds,
4536 foldermanager_GetFolder,
4537 foldermanager_GetFolderByName,
4538 foldermanager_RegisterFolder,
4539 foldermanager_UnregisterFolder,
4540 foldermanager_FindFolderFromPath,
4541 foldermanager_FindFolderFromIDList,
4542 foldermanager_Redirect
4545 static HRESULT foldermanager_create( void **ppv )
4547 UINT i, j;
4548 struct foldermanager *fm;
4550 fm = malloc( sizeof(*fm) );
4551 if (!fm) return E_OUTOFMEMORY;
4553 fm->IKnownFolderManager_iface.lpVtbl = &foldermanager_vtbl;
4554 fm->refs = 1;
4555 fm->num_ids = 0;
4557 for (i = 0; i < ARRAY_SIZE(CSIDL_Data); i++)
4559 if (!IsEqualGUID( CSIDL_Data[i].id, &GUID_NULL )) fm->num_ids++;
4561 fm->ids = malloc( fm->num_ids * sizeof(KNOWNFOLDERID) );
4562 if (!fm->ids)
4564 free( fm );
4565 return E_OUTOFMEMORY;
4567 for (i = j = 0; i < ARRAY_SIZE(CSIDL_Data); i++)
4569 if (!IsEqualGUID( CSIDL_Data[i].id, &GUID_NULL ))
4571 fm->ids[j] = *CSIDL_Data[i].id;
4572 j++;
4575 TRACE("found %u known folders\n", fm->num_ids);
4576 *ppv = &fm->IKnownFolderManager_iface;
4578 TRACE("returning iface %p\n", *ppv);
4579 return S_OK;
4582 HRESULT WINAPI KnownFolderManager_Constructor( IUnknown *punk, REFIID riid, void **ppv )
4584 TRACE("%p, %s, %p\n", punk, debugstr_guid(riid), ppv);
4586 if (!ppv)
4587 return E_POINTER;
4588 if (punk)
4589 return CLASS_E_NOAGGREGATION;
4591 return foldermanager_create( ppv );
4594 HRESULT WINAPI SHGetKnownFolderIDList(REFKNOWNFOLDERID rfid, DWORD flags, HANDLE token, PIDLIST_ABSOLUTE *pidl)
4596 TRACE("%s, 0x%08lx, %p, %p\n", debugstr_guid(rfid), flags, token, pidl);
4598 if (!pidl)
4599 return E_INVALIDARG;
4601 if (flags)
4602 FIXME("unsupported flags: 0x%08lx\n", flags);
4604 if (token)
4605 FIXME("user token is not used.\n");
4607 *pidl = NULL;
4608 if (IsEqualIID(rfid, &FOLDERID_Desktop))
4609 *pidl = _ILCreateDesktop();
4610 else if (IsEqualIID(rfid, &FOLDERID_RecycleBinFolder))
4611 *pidl = _ILCreateBitBucket();
4612 else if (IsEqualIID(rfid, &FOLDERID_ComputerFolder))
4613 *pidl = _ILCreateMyComputer();
4614 else if (IsEqualIID(rfid, &FOLDERID_PrintersFolder))
4615 *pidl = _ILCreatePrinters();
4616 else if (IsEqualIID(rfid, &FOLDERID_ControlPanelFolder))
4617 *pidl = _ILCreateControlPanel();
4618 else if (IsEqualIID(rfid, &FOLDERID_NetworkFolder))
4619 *pidl = _ILCreateNetwork();
4620 else if (IsEqualIID(rfid, &FOLDERID_Documents))
4621 *pidl = _ILCreateMyDocuments();
4622 else
4624 DWORD attributes = 0;
4625 WCHAR *pathW;
4626 HRESULT hr;
4628 hr = SHGetKnownFolderPath(rfid, flags, token, &pathW);
4629 if (FAILED(hr))
4630 return hr;
4632 hr = SHILCreateFromPathW(pathW, pidl, &attributes);
4633 CoTaskMemFree(pathW);
4634 return hr;
4637 return *pidl ? S_OK : E_FAIL;
4640 HRESULT WINAPI SHGetKnownFolderItem(REFKNOWNFOLDERID rfid, KNOWN_FOLDER_FLAG flags, HANDLE hToken,
4641 REFIID riid, void **ppv)
4643 PIDLIST_ABSOLUTE pidl;
4644 HRESULT hr;
4646 TRACE("%s, 0x%08x, %p, %s, %p\n", debugstr_guid(rfid), flags, hToken, debugstr_guid(riid), ppv);
4648 hr = SHGetKnownFolderIDList(rfid, flags, hToken, &pidl);
4649 if (FAILED(hr))
4651 *ppv = NULL;
4652 return hr;
4655 hr = SHCreateItemFromIDList(pidl, riid, ppv);
4656 CoTaskMemFree(pidl);
4657 return hr;
4660 static void register_system_knownfolders(void)
4662 int i;
4664 for (i = 0; i < ARRAY_SIZE(CSIDL_Data); ++i)
4666 const CSIDL_DATA *folder = &CSIDL_Data[i];
4667 if(folder->name){
4668 KNOWNFOLDER_DEFINITION kfd;
4670 /* register_folder won't modify kfd, so cast away const instead of
4671 * reallocating */
4672 kfd.category = folder->category;
4673 kfd.pszName = (WCHAR *)folder->name;
4674 kfd.pszDescription = NULL;
4675 kfd.fidParent = folder->parent ? *folder->parent : GUID_NULL;
4676 kfd.pszRelativePath = (WCHAR *)folder->path;
4677 kfd.pszParsingName = (WCHAR *)folder->parsing;
4678 kfd.pszTooltip = NULL;
4679 kfd.pszLocalizedName = NULL;
4680 kfd.pszIcon = NULL;
4681 kfd.pszSecurity = NULL;
4682 kfd.dwAttributes = folder->attributes;
4683 kfd.kfdFlags = folder->flags;
4684 kfd.ftidType = folder->typeid ? *folder->typeid : GUID_NULL;
4685 register_folder(folder->id, &kfd);
4690 HRESULT SHELL_RegisterShellFolders(void)
4692 HRESULT hr;
4694 hr = _SHRegisterUserShellFolders(TRUE);
4695 if (SUCCEEDED(hr))
4696 hr = _SHRegisterUserShellFolders(FALSE);
4697 if (SUCCEEDED(hr))
4698 hr = _SHRegisterCommonShellFolders();
4699 if (SUCCEEDED(hr))
4700 hr = create_extra_folders();
4701 if (SUCCEEDED(hr))
4702 hr = set_folder_attributes();
4703 if (SUCCEEDED(hr))
4704 register_system_knownfolders();
4705 return hr;