mfmediaengine: Remove unnecessary import library.
[wine.git] / dlls / shell32 / shellpath.c
blob7bfa504a62fdea92ad7448d7a4a7a396999af222
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 "undocshell.h"
50 #include "pidl.h"
51 #include "shlwapi.h"
52 #include "sddl.h"
53 #include "knownfolders.h"
54 #include "initguid.h"
55 #include "shobjidl.h"
56 #include "wine/debug.h"
58 WINE_DEFAULT_DEBUG_CHANNEL(shell);
60 static const BOOL is_win64 = sizeof(void *) > sizeof(int);
63 ########## Combining and Constructing paths ##########
66 /*************************************************************************
67 * PathAppend [SHELL32.36]
69 BOOL WINAPI PathAppendAW(
70 LPVOID lpszPath1,
71 LPCVOID lpszPath2)
73 if (SHELL_OsIsUnicode())
74 return PathAppendW(lpszPath1, lpszPath2);
75 return PathAppendA(lpszPath1, lpszPath2);
78 /*************************************************************************
79 * PathCombine [SHELL32.37]
81 LPVOID WINAPI PathCombineAW(
82 LPVOID szDest,
83 LPCVOID lpszDir,
84 LPCVOID lpszFile)
86 if (SHELL_OsIsUnicode())
87 return PathCombineW( szDest, lpszDir, lpszFile );
88 return PathCombineA( szDest, lpszDir, lpszFile );
91 /*************************************************************************
92 * PathAddBackslash [SHELL32.32]
94 LPVOID WINAPI PathAddBackslashAW(LPVOID lpszPath)
96 if(SHELL_OsIsUnicode())
97 return PathAddBackslashW(lpszPath);
98 return PathAddBackslashA(lpszPath);
101 /*************************************************************************
102 * PathBuildRoot [SHELL32.30]
104 LPVOID WINAPI PathBuildRootAW(LPVOID lpszPath, int drive)
106 if(SHELL_OsIsUnicode())
107 return PathBuildRootW(lpszPath, drive);
108 return PathBuildRootA(lpszPath, drive);
112 Extracting Component Parts
115 /*************************************************************************
116 * PathFindFileName [SHELL32.34]
118 LPVOID WINAPI PathFindFileNameAW(LPCVOID lpszPath)
120 if(SHELL_OsIsUnicode())
121 return PathFindFileNameW(lpszPath);
122 return PathFindFileNameA(lpszPath);
125 /*************************************************************************
126 * PathFindExtension [SHELL32.31]
128 LPVOID WINAPI PathFindExtensionAW(LPCVOID lpszPath)
130 if (SHELL_OsIsUnicode())
131 return PathFindExtensionW(lpszPath);
132 return PathFindExtensionA(lpszPath);
136 /*************************************************************************
137 * PathGetExtensionA [internal]
139 * NOTES
140 * exported by ordinal
141 * return value points to the first char after the dot
143 static LPSTR PathGetExtensionA(LPCSTR lpszPath)
145 TRACE("(%s)\n",lpszPath);
147 lpszPath = PathFindExtensionA(lpszPath);
148 return (LPSTR)(*lpszPath?(lpszPath+1):lpszPath);
151 /*************************************************************************
152 * PathGetExtensionW [internal]
154 static LPWSTR PathGetExtensionW(LPCWSTR lpszPath)
156 TRACE("(%s)\n",debugstr_w(lpszPath));
158 lpszPath = PathFindExtensionW(lpszPath);
159 return (LPWSTR)(*lpszPath?(lpszPath+1):lpszPath);
162 /*************************************************************************
163 * PathGetExtension [SHELL32.158]
165 LPVOID WINAPI PathGetExtensionAW(LPCVOID lpszPath,DWORD void1, DWORD void2)
167 if (SHELL_OsIsUnicode())
168 return PathGetExtensionW(lpszPath);
169 return PathGetExtensionA(lpszPath);
172 /*************************************************************************
173 * PathGetArgs [SHELL32.52]
175 LPVOID WINAPI PathGetArgsAW(LPVOID lpszPath)
177 if (SHELL_OsIsUnicode())
178 return PathGetArgsW(lpszPath);
179 return PathGetArgsA(lpszPath);
182 /*************************************************************************
183 * PathGetDriveNumber [SHELL32.57]
185 int WINAPI PathGetDriveNumberAW(LPVOID lpszPath)
187 if (SHELL_OsIsUnicode())
188 return PathGetDriveNumberW(lpszPath);
189 return PathGetDriveNumberA(lpszPath);
192 /*************************************************************************
193 * PathRemoveFileSpec [SHELL32.35]
195 BOOL WINAPI PathRemoveFileSpecAW(LPVOID lpszPath)
197 if (SHELL_OsIsUnicode())
198 return PathRemoveFileSpecW(lpszPath);
199 return PathRemoveFileSpecA(lpszPath);
202 /*************************************************************************
203 * PathStripPath [SHELL32.38]
205 void WINAPI PathStripPathAW(LPVOID lpszPath)
207 if (SHELL_OsIsUnicode())
208 PathStripPathW(lpszPath);
209 else
210 PathStripPathA(lpszPath);
213 /*************************************************************************
214 * PathStripToRoot [SHELL32.50]
216 BOOL WINAPI PathStripToRootAW(LPVOID lpszPath)
218 if (SHELL_OsIsUnicode())
219 return PathStripToRootW(lpszPath);
220 return PathStripToRootA(lpszPath);
223 /*************************************************************************
224 * PathRemoveArgs [SHELL32.251]
226 void WINAPI PathRemoveArgsAW(LPVOID lpszPath)
228 if (SHELL_OsIsUnicode())
229 PathRemoveArgsW(lpszPath);
230 else
231 PathRemoveArgsA(lpszPath);
234 /*************************************************************************
235 * PathRemoveExtension [SHELL32.250]
237 void WINAPI PathRemoveExtensionAW(LPVOID lpszPath)
239 if (SHELL_OsIsUnicode())
240 PathRemoveExtensionW(lpszPath);
241 else
242 PathRemoveExtensionA(lpszPath);
247 Path Manipulations
250 /*************************************************************************
251 * PathGetShortPathA [internal]
253 static void PathGetShortPathA(LPSTR pszPath)
255 CHAR path[MAX_PATH];
257 TRACE("%s\n", pszPath);
259 if (GetShortPathNameA(pszPath, path, MAX_PATH))
261 lstrcpyA(pszPath, path);
265 /*************************************************************************
266 * PathGetShortPathW [internal]
268 static void PathGetShortPathW(LPWSTR pszPath)
270 WCHAR path[MAX_PATH];
272 TRACE("%s\n", debugstr_w(pszPath));
274 if (GetShortPathNameW(pszPath, path, MAX_PATH))
276 lstrcpyW(pszPath, path);
280 /*************************************************************************
281 * PathGetShortPath [SHELL32.92]
283 VOID WINAPI PathGetShortPathAW(LPVOID pszPath)
285 if(SHELL_OsIsUnicode())
286 PathGetShortPathW(pszPath);
287 PathGetShortPathA(pszPath);
290 /*************************************************************************
291 * PathRemoveBlanks [SHELL32.33]
293 void WINAPI PathRemoveBlanksAW(LPVOID str)
295 if(SHELL_OsIsUnicode())
296 PathRemoveBlanksW(str);
297 else
298 PathRemoveBlanksA(str);
301 /*************************************************************************
302 * PathQuoteSpaces [SHELL32.55]
304 VOID WINAPI PathQuoteSpacesAW (LPVOID lpszPath)
306 if(SHELL_OsIsUnicode())
307 PathQuoteSpacesW(lpszPath);
308 else
309 PathQuoteSpacesA(lpszPath);
312 /*************************************************************************
313 * PathUnquoteSpaces [SHELL32.56]
315 VOID WINAPI PathUnquoteSpacesAW(LPVOID str)
317 if(SHELL_OsIsUnicode())
318 PathUnquoteSpacesW(str);
319 else
320 PathUnquoteSpacesA(str);
323 /*************************************************************************
324 * PathParseIconLocation [SHELL32.249]
326 int WINAPI PathParseIconLocationAW (LPVOID lpszPath)
328 if(SHELL_OsIsUnicode())
329 return PathParseIconLocationW(lpszPath);
330 return PathParseIconLocationA(lpszPath);
334 ########## Path Testing ##########
336 /*************************************************************************
337 * PathIsUNC [SHELL32.39]
339 BOOL WINAPI PathIsUNCAW (LPCVOID lpszPath)
341 if (SHELL_OsIsUnicode())
342 return PathIsUNCW( lpszPath );
343 return PathIsUNCA( lpszPath );
346 /*************************************************************************
347 * PathIsRelative [SHELL32.40]
349 BOOL WINAPI PathIsRelativeAW (LPCVOID lpszPath)
351 if (SHELL_OsIsUnicode())
352 return PathIsRelativeW( lpszPath );
353 return PathIsRelativeA( lpszPath );
356 /*************************************************************************
357 * PathIsRoot [SHELL32.29]
359 BOOL WINAPI PathIsRootAW(LPCVOID lpszPath)
361 if (SHELL_OsIsUnicode())
362 return PathIsRootW(lpszPath);
363 return PathIsRootA(lpszPath);
366 /*************************************************************************
367 * PathIsExeA [internal]
369 static BOOL PathIsExeA (LPCSTR lpszPath)
371 LPCSTR lpszExtension = PathGetExtensionA(lpszPath);
372 int i;
373 static const char * const lpszExtensions[] =
374 {"exe", "com", "pif", "cmd", "bat", "scf", "scr", NULL };
376 TRACE("path=%s\n",lpszPath);
378 for(i=0; lpszExtensions[i]; i++)
379 if (!lstrcmpiA(lpszExtension,lpszExtensions[i])) return TRUE;
381 return FALSE;
384 /*************************************************************************
385 * PathIsExeW [internal]
387 static BOOL PathIsExeW (LPCWSTR lpszPath)
389 LPCWSTR lpszExtension = PathGetExtensionW(lpszPath);
390 int i;
391 static const WCHAR lpszExtensions[][4] =
392 {L"exe", L"com", L"pif",L"cmd", L"bat", L"scf",L"scr",L"" };
394 TRACE("path=%s\n",debugstr_w(lpszPath));
396 for(i=0; lpszExtensions[i][0]; i++)
397 if (!wcsicmp(lpszExtension,lpszExtensions[i])) return TRUE;
399 return FALSE;
402 /*************************************************************************
403 * PathIsExe [SHELL32.43]
405 BOOL WINAPI PathIsExeAW (LPCVOID path)
407 if (SHELL_OsIsUnicode())
408 return PathIsExeW (path);
409 return PathIsExeA(path);
412 /*************************************************************************
413 * PathIsDirectory [SHELL32.159]
415 BOOL WINAPI PathIsDirectoryAW (LPCVOID lpszPath)
417 if (SHELL_OsIsUnicode())
418 return PathIsDirectoryW (lpszPath);
419 return PathIsDirectoryA (lpszPath);
422 /*************************************************************************
423 * PathFileExists [SHELL32.45]
425 BOOL WINAPI PathFileExistsAW (LPCVOID lpszPath)
427 if (SHELL_OsIsUnicode())
428 return PathFileExistsW (lpszPath);
429 return PathFileExistsA (lpszPath);
432 /*************************************************************************
433 * PathMatchSpec [SHELL32.46]
435 BOOL WINAPI PathMatchSpecAW(LPVOID name, LPVOID mask)
437 if (SHELL_OsIsUnicode())
438 return PathMatchSpecW( name, mask );
439 return PathMatchSpecA( name, mask );
442 /*************************************************************************
443 * PathIsSameRoot [SHELL32.650]
445 BOOL WINAPI PathIsSameRootAW(LPCVOID lpszPath1, LPCVOID lpszPath2)
447 if (SHELL_OsIsUnicode())
448 return PathIsSameRootW(lpszPath1, lpszPath2);
449 return PathIsSameRootA(lpszPath1, lpszPath2);
452 /*************************************************************************
453 * IsLFNDriveA [SHELL32.41]
455 BOOL WINAPI IsLFNDriveA(LPCSTR lpszPath)
457 DWORD fnlen;
459 if (!GetVolumeInformationA(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
460 return FALSE;
461 return fnlen > 12;
464 /*************************************************************************
465 * IsLFNDriveW [SHELL32.42]
467 BOOL WINAPI IsLFNDriveW(LPCWSTR lpszPath)
469 DWORD fnlen;
471 if (!GetVolumeInformationW(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
472 return FALSE;
473 return fnlen > 12;
476 /*************************************************************************
477 * IsLFNDrive [SHELL32.119]
479 BOOL WINAPI IsLFNDriveAW(LPCVOID lpszPath)
481 if (SHELL_OsIsUnicode())
482 return IsLFNDriveW(lpszPath);
483 return IsLFNDriveA(lpszPath);
487 ########## Creating Something Unique ##########
489 /*************************************************************************
490 * PathMakeUniqueNameA [internal]
492 static BOOL PathMakeUniqueNameA(
493 LPSTR lpszBuffer,
494 DWORD dwBuffSize,
495 LPCSTR lpszShortName,
496 LPCSTR lpszLongName,
497 LPCSTR lpszPathName)
499 FIXME("%p %u %s %s %s stub\n",
500 lpszBuffer, dwBuffSize, debugstr_a(lpszShortName),
501 debugstr_a(lpszLongName), debugstr_a(lpszPathName));
502 return TRUE;
505 /*************************************************************************
506 * PathMakeUniqueNameW [internal]
508 static BOOL PathMakeUniqueNameW(
509 LPWSTR lpszBuffer,
510 DWORD dwBuffSize,
511 LPCWSTR lpszShortName,
512 LPCWSTR lpszLongName,
513 LPCWSTR lpszPathName)
515 FIXME("%p %u %s %s %s stub\n",
516 lpszBuffer, dwBuffSize, debugstr_w(lpszShortName),
517 debugstr_w(lpszLongName), debugstr_w(lpszPathName));
518 return TRUE;
521 /*************************************************************************
522 * PathMakeUniqueName [SHELL32.47]
524 BOOL WINAPI PathMakeUniqueNameAW(
525 LPVOID lpszBuffer,
526 DWORD dwBuffSize,
527 LPCVOID lpszShortName,
528 LPCVOID lpszLongName,
529 LPCVOID lpszPathName)
531 if (SHELL_OsIsUnicode())
532 return PathMakeUniqueNameW(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
533 return PathMakeUniqueNameA(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
536 /*************************************************************************
537 * PathYetAnotherMakeUniqueName [SHELL32.75]
539 BOOL WINAPI PathYetAnotherMakeUniqueName(LPWSTR buffer, LPCWSTR path, LPCWSTR shortname, LPCWSTR longname)
541 WCHAR pathW[MAX_PATH], retW[MAX_PATH];
542 const WCHAR *file, *ext;
543 int i = 2;
545 TRACE("(%p, %s, %s, %s)\n", buffer, debugstr_w(path), debugstr_w(shortname), debugstr_w(longname));
547 file = longname ? longname : shortname;
548 PathCombineW(pathW, path, file);
549 lstrcpyW(retW, pathW);
550 PathRemoveExtensionW(pathW);
552 ext = PathFindExtensionW(file);
554 /* now try to make it unique */
555 while (PathFileExistsW(retW))
557 swprintf(retW, ARRAY_SIZE(retW), L"%s (%d)%s", pathW, i, ext);
558 i++;
561 lstrcpyW(buffer, retW);
562 TRACE("ret - %s\n", debugstr_w(buffer));
564 return TRUE;
568 ########## cleaning and resolving paths ##########
571 /*************************************************************************
572 * PathFindOnPath [SHELL32.145]
574 BOOL WINAPI PathFindOnPathAW(LPVOID sFile, LPCVOID *sOtherDirs)
576 if (SHELL_OsIsUnicode())
577 return PathFindOnPathW(sFile, (LPCWSTR *)sOtherDirs);
578 return PathFindOnPathA(sFile, (LPCSTR *)sOtherDirs);
581 /*************************************************************************
582 * PathCleanupSpec [SHELL32.171]
584 * lpszFile is changed in place.
586 int WINAPI PathCleanupSpec( LPCWSTR lpszPathW, LPWSTR lpszFileW )
588 int i = 0;
589 DWORD rc = 0;
590 int length = 0;
592 if (SHELL_OsIsUnicode())
594 LPWSTR p = lpszFileW;
596 TRACE("Cleanup %s\n",debugstr_w(lpszFileW));
598 if (lpszPathW)
599 length = lstrlenW(lpszPathW);
601 while (*p)
603 int gct = PathGetCharTypeW(*p);
604 if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
606 lpszFileW[i]='-';
607 rc |= PCS_REPLACEDCHAR;
609 else
610 lpszFileW[i]=*p;
611 i++;
612 p++;
613 if (length + i == MAX_PATH)
615 rc |= PCS_FATAL | PCS_PATHTOOLONG;
616 break;
619 lpszFileW[i]=0;
621 else
623 LPSTR lpszFileA = (LPSTR)lpszFileW;
624 LPCSTR lpszPathA = (LPCSTR)lpszPathW;
625 LPSTR p = lpszFileA;
627 TRACE("Cleanup %s\n",debugstr_a(lpszFileA));
629 if (lpszPathA)
630 length = strlen(lpszPathA);
632 while (*p)
634 int gct = PathGetCharTypeA(*p);
635 if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
637 lpszFileA[i]='-';
638 rc |= PCS_REPLACEDCHAR;
640 else
641 lpszFileA[i]=*p;
642 i++;
643 p++;
644 if (length + i == MAX_PATH)
646 rc |= PCS_FATAL | PCS_PATHTOOLONG;
647 break;
650 lpszFileA[i]=0;
652 return rc;
655 /*************************************************************************
656 * PathQualifyA [SHELL32]
658 static BOOL PathQualifyA(LPCSTR pszPath)
660 FIXME("%s\n",pszPath);
661 return FALSE;
664 /*************************************************************************
665 * PathQualifyW [SHELL32]
667 static BOOL PathQualifyW(LPCWSTR pszPath)
669 FIXME("%s\n",debugstr_w(pszPath));
670 return FALSE;
673 /*************************************************************************
674 * PathQualify [SHELL32.49]
676 BOOL WINAPI PathQualifyAW(LPCVOID pszPath)
678 if (SHELL_OsIsUnicode())
679 return PathQualifyW(pszPath);
680 return PathQualifyA(pszPath);
683 BOOL WINAPI PathFindOnPathExA(LPSTR,LPCSTR *,DWORD);
684 BOOL WINAPI PathFindOnPathExW(LPWSTR,LPCWSTR *,DWORD);
685 BOOL WINAPI PathFileExistsDefExtA(LPSTR,DWORD);
686 BOOL WINAPI PathFileExistsDefExtW(LPWSTR,DWORD);
688 static BOOL PathResolveA(char *path, const char **dirs, DWORD flags)
690 BOOL is_file_spec = PathIsFileSpecA(path);
691 DWORD dwWhich = flags & PRF_DONTFINDLNK ? 0xf : 0xff;
693 TRACE("(%s,%p,0x%08x)\n", debugstr_a(path), dirs, flags);
695 if (flags & PRF_VERIFYEXISTS && !PathFileExistsA(path))
697 if (PathFindOnPathExA(path, dirs, dwWhich))
698 return TRUE;
699 if (PathFileExistsDefExtA(path, dwWhich))
700 return TRUE;
701 if (!is_file_spec) GetFullPathNameA(path, MAX_PATH, path, NULL);
702 SetLastError(ERROR_FILE_NOT_FOUND);
703 return FALSE;
706 if (is_file_spec)
708 SetLastError(ERROR_FILE_NOT_FOUND);
709 return FALSE;
712 GetFullPathNameA(path, MAX_PATH, path, NULL);
714 return TRUE;
717 static BOOL PathResolveW(WCHAR *path, const WCHAR **dirs, DWORD flags)
719 BOOL is_file_spec = PathIsFileSpecW(path);
720 DWORD dwWhich = flags & PRF_DONTFINDLNK ? 0xf : 0xff;
722 TRACE("(%s,%p,0x%08x)\n", debugstr_w(path), dirs, flags);
724 if (flags & PRF_VERIFYEXISTS && !PathFileExistsW(path))
726 if (PathFindOnPathExW(path, dirs, dwWhich))
727 return TRUE;
728 if (PathFileExistsDefExtW(path, dwWhich))
729 return TRUE;
730 if (!is_file_spec) GetFullPathNameW(path, MAX_PATH, path, NULL);
731 SetLastError(ERROR_FILE_NOT_FOUND);
732 return FALSE;
735 if (is_file_spec)
737 SetLastError(ERROR_FILE_NOT_FOUND);
738 return FALSE;
741 GetFullPathNameW(path, MAX_PATH, path, NULL);
743 return TRUE;
746 /*************************************************************************
747 * PathResolve [SHELL32.51]
749 BOOL WINAPI PathResolveAW(void *path, const void **paths, DWORD flags)
751 if (SHELL_OsIsUnicode())
752 return PathResolveW(path, (const WCHAR **)paths, flags);
753 else
754 return PathResolveA(path, (const char **)paths, flags);
757 /*************************************************************************
758 * PathProcessCommandA
760 static LONG PathProcessCommandA (
761 LPCSTR lpszPath,
762 LPSTR lpszBuff,
763 DWORD dwBuffSize,
764 DWORD dwFlags)
766 FIXME("%s %p 0x%04x 0x%04x stub\n",
767 lpszPath, lpszBuff, dwBuffSize, dwFlags);
768 if(!lpszPath) return -1;
769 if(lpszBuff) strcpy(lpszBuff, lpszPath);
770 return strlen(lpszPath);
773 /*************************************************************************
774 * PathProcessCommandW
776 static LONG PathProcessCommandW (
777 LPCWSTR lpszPath,
778 LPWSTR lpszBuff,
779 DWORD dwBuffSize,
780 DWORD dwFlags)
782 FIXME("(%s, %p, 0x%04x, 0x%04x) stub\n",
783 debugstr_w(lpszPath), lpszBuff, dwBuffSize, dwFlags);
784 if(!lpszPath) return -1;
785 if(lpszBuff) lstrcpyW(lpszBuff, lpszPath);
786 return lstrlenW(lpszPath);
789 /*************************************************************************
790 * PathProcessCommand (SHELL32.653)
792 LONG WINAPI PathProcessCommandAW (
793 LPCVOID lpszPath,
794 LPVOID lpszBuff,
795 DWORD dwBuffSize,
796 DWORD dwFlags)
798 if (SHELL_OsIsUnicode())
799 return PathProcessCommandW(lpszPath, lpszBuff, dwBuffSize, dwFlags);
800 return PathProcessCommandA(lpszPath, lpszBuff, dwBuffSize, dwFlags);
804 ########## special ##########
807 /*************************************************************************
808 * PathSetDlgItemPath (SHELL32.48)
810 VOID WINAPI PathSetDlgItemPathAW(HWND hDlg, int id, LPCVOID pszPath)
812 if (SHELL_OsIsUnicode())
813 PathSetDlgItemPathW(hDlg, id, pszPath);
814 else
815 PathSetDlgItemPathA(hDlg, id, pszPath);
818 typedef enum _CSIDL_Type {
819 CSIDL_Type_User,
820 CSIDL_Type_AllUsers,
821 CSIDL_Type_CurrVer,
822 CSIDL_Type_Disallowed,
823 CSIDL_Type_NonExistent,
824 CSIDL_Type_WindowsPath,
825 CSIDL_Type_SystemPath,
826 CSIDL_Type_SystemX86Path,
827 CSIDL_Type_ProgramData,
828 } CSIDL_Type;
830 #define CSIDL_CONTACTS 0x0043
831 #define CSIDL_DOWNLOADS 0x0047
832 #define CSIDL_LINKS 0x004d
833 #define CSIDL_APPDATA_LOCALLOW 0x004e
834 #define CSIDL_SAVED_GAMES 0x0062
835 #define CSIDL_SEARCHES 0x0063
837 typedef struct
839 IApplicationDestinations IApplicationDestinations_iface;
840 LONG ref;
841 } IApplicationDestinationsImpl;
843 static inline IApplicationDestinationsImpl *impl_from_IApplicationDestinations( IApplicationDestinations *iface )
845 return CONTAINING_RECORD(iface, IApplicationDestinationsImpl, IApplicationDestinations_iface);
848 static HRESULT WINAPI ApplicationDestinations_QueryInterface(IApplicationDestinations *iface, REFIID riid,
849 LPVOID *ppv)
851 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
853 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppv);
855 if (ppv == NULL)
856 return E_POINTER;
858 if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IApplicationDestinations, riid))
860 *ppv = &This->IApplicationDestinations_iface;
861 IUnknown_AddRef((IUnknown*)*ppv);
863 TRACE("Returning IApplicationDestinations: %p\n", *ppv);
864 return S_OK;
867 *ppv = NULL;
868 FIXME("(%p)->(%s, %p) interface not supported.\n", This, debugstr_guid(riid), ppv);
870 return E_NOINTERFACE;
873 static ULONG WINAPI ApplicationDestinations_AddRef(IApplicationDestinations *iface)
875 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
876 ULONG ref = InterlockedIncrement(&This->ref);
878 TRACE("(%p), new refcount=%i\n", This, ref);
880 return ref;
883 static ULONG WINAPI ApplicationDestinations_Release(IApplicationDestinations *iface)
885 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
886 ULONG ref = InterlockedDecrement(&This->ref);
888 TRACE("(%p), new refcount=%i\n", This, ref);
890 if (ref == 0)
891 heap_free(This);
893 return ref;
896 static HRESULT WINAPI ApplicationDestinations_SetAppID(IApplicationDestinations *iface, const WCHAR *appid)
898 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
900 FIXME("(%p, %s) stub!\n", This, debugstr_w(appid));
902 return E_NOTIMPL;
905 static HRESULT WINAPI ApplicationDestinations_RemoveDestination(IApplicationDestinations *iface, IUnknown *punk)
907 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
909 FIXME("(%p, %p) stub!\n", This, punk);
911 return E_NOTIMPL;
914 static HRESULT WINAPI ApplicationDestinations_RemoveAllDestinations(IApplicationDestinations *iface)
916 IApplicationDestinationsImpl *This = impl_from_IApplicationDestinations(iface);
918 FIXME("(%p) stub!\n", This);
920 return E_NOTIMPL;
923 static const IApplicationDestinationsVtbl ApplicationDestinationsVtbl =
925 ApplicationDestinations_QueryInterface,
926 ApplicationDestinations_AddRef,
927 ApplicationDestinations_Release,
928 ApplicationDestinations_SetAppID,
929 ApplicationDestinations_RemoveDestination,
930 ApplicationDestinations_RemoveAllDestinations
933 HRESULT WINAPI ApplicationDestinations_Constructor(IUnknown *outer, REFIID riid, LPVOID *ppv)
935 IApplicationDestinationsImpl *This;
936 HRESULT hr;
938 TRACE("(%p, %s, %p)\n", outer, debugstr_guid(riid), ppv);
940 if (outer)
941 return CLASS_E_NOAGGREGATION;
943 if (!(This = SHAlloc(sizeof(*This))))
944 return E_OUTOFMEMORY;
946 This->IApplicationDestinations_iface.lpVtbl = &ApplicationDestinationsVtbl;
947 This->ref = 0;
949 hr = IUnknown_QueryInterface(&This->IApplicationDestinations_iface, riid, ppv);
950 if (FAILED(hr))
951 SHFree(This);
953 return hr;
956 typedef struct
958 IApplicationDocumentLists IApplicationDocumentLists_iface;
959 LONG ref;
960 } IApplicationDocumentListsImpl;
962 static inline IApplicationDocumentListsImpl *impl_from_IApplicationDocumentLists( IApplicationDocumentLists *iface )
964 return CONTAINING_RECORD(iface, IApplicationDocumentListsImpl, IApplicationDocumentLists_iface);
967 static HRESULT WINAPI ApplicationDocumentLists_QueryInterface(IApplicationDocumentLists *iface,
968 REFIID riid, LPVOID *ppv)
970 IApplicationDocumentListsImpl *This = impl_from_IApplicationDocumentLists(iface);
972 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppv);
974 if (ppv == NULL)
975 return E_POINTER;
977 if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IApplicationDocumentLists, riid))
979 *ppv = &This->IApplicationDocumentLists_iface;
980 IUnknown_AddRef((IUnknown*)*ppv);
982 TRACE("Returning IApplicationDocumentLists: %p\n", *ppv);
983 return S_OK;
986 *ppv = NULL;
987 FIXME("(%p)->(%s, %p) interface not supported.\n", This, debugstr_guid(riid), ppv);
989 return E_NOINTERFACE;
992 static ULONG WINAPI ApplicationDocumentLists_AddRef(IApplicationDocumentLists *iface)
994 IApplicationDocumentListsImpl *This = impl_from_IApplicationDocumentLists(iface);
995 ULONG ref = InterlockedIncrement(&This->ref);
997 TRACE("(%p), new refcount=%i\n", This, ref);
999 return ref;
1002 static ULONG WINAPI ApplicationDocumentLists_Release(IApplicationDocumentLists *iface)
1004 IApplicationDocumentListsImpl *This = impl_from_IApplicationDocumentLists(iface);
1005 ULONG ref = InterlockedDecrement(&This->ref);
1007 TRACE("(%p), new refcount=%i\n", This, ref);
1009 if (ref == 0)
1010 heap_free(This);
1012 return ref;
1015 static HRESULT WINAPI ApplicationDocumentLists_SetAppID(IApplicationDocumentLists *iface,
1016 const WCHAR *appid)
1018 IApplicationDocumentListsImpl *This = impl_from_IApplicationDocumentLists(iface);
1020 FIXME("(%p, %s) stub!\n", This, debugstr_w(appid));
1022 return E_NOTIMPL;
1025 static HRESULT WINAPI ApplicationDocumentLists_GetList(IApplicationDocumentLists *iface,
1026 APPDOCLISTTYPE list_type, UINT item_count,
1027 REFIID riid, void **obj)
1029 IApplicationDocumentListsImpl *This = impl_from_IApplicationDocumentLists(iface);
1031 FIXME("(%p, %u, %u, %s, %p): stub\n", This, list_type, item_count, debugstr_guid(riid), obj);
1033 return E_NOTIMPL;
1036 static const IApplicationDocumentListsVtbl ApplicationDocumentListsVtbl =
1038 ApplicationDocumentLists_QueryInterface,
1039 ApplicationDocumentLists_AddRef,
1040 ApplicationDocumentLists_Release,
1041 ApplicationDocumentLists_SetAppID,
1042 ApplicationDocumentLists_GetList
1045 HRESULT WINAPI ApplicationDocumentLists_Constructor(IUnknown *outer, REFIID riid, LPVOID *ppv)
1047 IApplicationDocumentListsImpl *This;
1048 HRESULT hr;
1050 TRACE("(%p, %s, %p)\n", outer, debugstr_guid(riid), ppv);
1052 if (outer)
1053 return CLASS_E_NOAGGREGATION;
1055 if (!(This = SHAlloc(sizeof(*This))))
1056 return E_OUTOFMEMORY;
1058 This->IApplicationDocumentLists_iface.lpVtbl = &ApplicationDocumentListsVtbl;
1059 This->ref = 0;
1061 hr = IUnknown_QueryInterface(&This->IApplicationDocumentLists_iface, riid, ppv);
1062 if (FAILED(hr))
1063 SHFree(This);
1065 return hr;
1068 typedef struct
1070 const KNOWNFOLDERID *id;
1071 CSIDL_Type type;
1072 const WCHAR *value;
1073 const WCHAR *def_path; /* fallback string or resource ID */
1074 KF_CATEGORY category;
1075 const WCHAR *name;
1076 const KNOWNFOLDERID *parent;
1077 const WCHAR *path;
1078 const WCHAR *parsing;
1079 DWORD attributes;
1080 KF_DEFINITION_FLAGS flags;
1081 const FOLDERTYPEID *typeid;
1082 } CSIDL_DATA;
1084 static const CSIDL_DATA CSIDL_Data[] =
1086 { /* 0x00 - CSIDL_DESKTOP */
1087 .id = &FOLDERID_Desktop,
1088 .type = CSIDL_Type_User,
1089 .value = L"Desktop",
1090 .category = KF_CATEGORY_PERUSER,
1091 .name = L"Desktop",
1092 .path = L"Desktop",
1093 .attributes = FILE_ATTRIBUTE_READONLY,
1095 { /* 0x01 - CSIDL_INTERNET */
1096 .id = &FOLDERID_InternetFolder,
1097 .type = CSIDL_Type_Disallowed,
1098 .category = KF_CATEGORY_VIRTUAL,
1099 .name = L"InternetFolder",
1100 .parsing = L"::{871C5380-42A0-1069-A2EA-08002B30309D}",
1102 { /* 0x02 - CSIDL_PROGRAMS */
1103 .id = &FOLDERID_Programs,
1104 .type = CSIDL_Type_User,
1105 .value = L"Programs",
1106 .category = KF_CATEGORY_PERUSER,
1107 .name = L"Programs",
1108 .parent = &FOLDERID_StartMenu,
1109 .path = L"Programs",
1110 .attributes = FILE_ATTRIBUTE_READONLY,
1112 { /* 0x03 - CSIDL_CONTROLS (.CPL files) */
1113 .id = &FOLDERID_ControlPanelFolder,
1114 .type = CSIDL_Type_SystemPath,
1115 .category = KF_CATEGORY_VIRTUAL,
1116 .name = L"ControlPanelFolder",
1117 .path = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}",
1118 .parsing = L"::{26EE0668-A00A-44D7-9371-BEB064C98683}\\0",
1120 { /* 0x04 - CSIDL_PRINTERS */
1121 .id = &FOLDERID_PrintersFolder,
1122 .type = CSIDL_Type_SystemPath,
1123 .category = KF_CATEGORY_VIRTUAL,
1124 .name = L"PrintersFolder",
1125 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}",
1127 { /* 0x05 - CSIDL_PERSONAL */
1128 .id = &FOLDERID_Documents,
1129 .type = CSIDL_Type_User,
1130 .value = L"Personal",
1131 .category = KF_CATEGORY_PERUSER,
1132 .name = L"Personal",
1133 .parent = &FOLDERID_Profile,
1134 .path = L"Documents",
1135 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{FDD39AD0-238F-46AF-ADB4-6C85480369C7}",
1136 .attributes = FILE_ATTRIBUTE_READONLY,
1137 .flags = KFDF_ROAMABLE | KFDF_PRECREATE,
1139 { /* 0x06 - CSIDL_FAVORITES */
1140 .id = &FOLDERID_Favorites,
1141 .type = CSIDL_Type_User,
1142 .value = L"Favorites",
1143 .category = KF_CATEGORY_PERUSER,
1144 .name = L"Favorites",
1145 .path = L"Favorites",
1146 .attributes = FILE_ATTRIBUTE_READONLY,
1147 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1149 { /* 0x07 - CSIDL_STARTUP */
1150 .id = &FOLDERID_Startup,
1151 .type = CSIDL_Type_User,
1152 .value = L"StartUp",
1153 .category = KF_CATEGORY_PERUSER,
1154 .name = L"Startup",
1155 .parent = &FOLDERID_Programs,
1156 .path = L"StartUp",
1157 .attributes = FILE_ATTRIBUTE_READONLY,
1158 .flags = KFDF_PRECREATE,
1160 { /* 0x08 - CSIDL_RECENT */
1161 .id = &FOLDERID_Recent,
1162 .type = CSIDL_Type_User,
1163 .value = L"Recent",
1164 .category = KF_CATEGORY_PERUSER,
1165 .name = L"Recent",
1166 .parent = &FOLDERID_RoamingAppData,
1167 .path = L"Microsoft\\Windows\\Recent",
1168 .attributes = FILE_ATTRIBUTE_READONLY,
1169 .flags = KFDF_PRECREATE,
1171 { /* 0x09 - CSIDL_SENDTO */
1172 .id = &FOLDERID_SendTo,
1173 .type = CSIDL_Type_User,
1174 .value = L"SendTo",
1175 .category = KF_CATEGORY_PERUSER,
1176 .name = L"SendTo",
1177 .parent = &FOLDERID_RoamingAppData,
1178 .path = L"Microsoft\\Windows\\SendTo",
1179 .flags = KFDF_PRECREATE,
1181 { /* 0x0a - CSIDL_BITBUCKET - Recycle Bin */
1182 .id = &FOLDERID_RecycleBinFolder,
1183 .type = CSIDL_Type_Disallowed,
1184 .category = KF_CATEGORY_VIRTUAL,
1185 .name = L"RecycleBinFolder",
1186 .parsing = L"::{645FF040-5081-101B-9F08-00AA002F954E}",
1188 { /* 0x0b - CSIDL_STARTMENU */
1189 .id = &FOLDERID_StartMenu,
1190 .type = CSIDL_Type_User,
1191 .value = L"Start Menu",
1192 .category = KF_CATEGORY_PERUSER,
1193 .name = L"Start Menu",
1194 .parent = &FOLDERID_RoamingAppData,
1195 .path = L"Microsoft\\Windows\\Start Menu",
1196 .attributes = FILE_ATTRIBUTE_READONLY,
1197 .flags = KFDF_PRECREATE,
1199 { /* 0x0c - CSIDL_MYDOCUMENTS */
1200 .id = &GUID_NULL,
1201 .type = CSIDL_Type_Disallowed, /* matches WinXP--can't get its path */
1203 { /* 0x0d - CSIDL_MYMUSIC */
1204 .id = &FOLDERID_Music,
1205 .type = CSIDL_Type_User,
1206 .value = L"My Music",
1207 .category = KF_CATEGORY_PERUSER,
1208 .name = L"My Music",
1209 .parent = &FOLDERID_Profile,
1210 .path = L"Music",
1211 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{4BD8D571-6D19-48D3-BE97-422220080E43}",
1212 .attributes = FILE_ATTRIBUTE_READONLY,
1213 .flags = KFDF_ROAMABLE | KFDF_PRECREATE,
1215 { /* 0x0e - CSIDL_MYVIDEO */
1216 .id = &FOLDERID_Videos,
1217 .type = CSIDL_Type_User,
1218 .value = L"My Videos",
1219 .category = KF_CATEGORY_PERUSER,
1220 .name = L"My Video",
1221 .parent = &FOLDERID_Profile,
1222 .path = L"Videos",
1223 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{18989B1D-99B5-455B-841C-AB7C74E4DDFC}",
1224 .attributes = FILE_ATTRIBUTE_READONLY,
1225 .flags = KFDF_ROAMABLE | KFDF_PRECREATE,
1227 { /* 0x0f - unassigned */
1228 .id = &GUID_NULL,
1229 .type = CSIDL_Type_Disallowed,
1231 { /* 0x10 - CSIDL_DESKTOPDIRECTORY */
1232 .id = &FOLDERID_Desktop,
1233 .type = CSIDL_Type_User,
1234 .value = L"Desktop",
1235 .category = KF_CATEGORY_PERUSER,
1236 .name = L"Desktop",
1237 .parent = &FOLDERID_Profile,
1238 .path = L"Desktop",
1239 .attributes = FILE_ATTRIBUTE_READONLY,
1240 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1242 { /* 0x11 - CSIDL_DRIVES */
1243 .id = &FOLDERID_ComputerFolder,
1244 .type = CSIDL_Type_Disallowed,
1245 .category = KF_CATEGORY_VIRTUAL,
1246 .name = L"MyComputerFolder",
1247 .parsing = L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
1249 { /* 0x12 - CSIDL_NETWORK */
1250 .id = &FOLDERID_NetworkFolder,
1251 .type = CSIDL_Type_Disallowed,
1252 .category = KF_CATEGORY_VIRTUAL,
1253 .name = L"NetworkPlacesFolder",
1254 .parsing = L"::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}",
1256 { /* 0x13 - CSIDL_NETHOOD */
1257 .id = &FOLDERID_NetHood,
1258 .type = CSIDL_Type_User,
1259 .value = L"NetHood",
1260 .category = KF_CATEGORY_PERUSER,
1261 .name = L"NetHood",
1262 .parent = &FOLDERID_RoamingAppData,
1263 .path = L"Microsoft\\Windows\\Network Shortcuts",
1265 { /* 0x14 - CSIDL_FONTS */
1266 .id = &FOLDERID_Fonts,
1267 .type = CSIDL_Type_WindowsPath,
1268 .value = L"Fonts",
1269 .def_path = L"Fonts",
1270 .category = KF_CATEGORY_FIXED,
1271 .name = L"Fonts",
1272 .parent = &FOLDERID_Windows,
1273 .typeid = &FOLDERID_Windows
1275 { /* 0x15 - CSIDL_TEMPLATES */
1276 .id = &FOLDERID_Templates,
1277 .type = CSIDL_Type_User,
1278 .value = L"Templates",
1279 .category = KF_CATEGORY_PERUSER,
1280 .name = L"Templates",
1281 .parent = &FOLDERID_RoamingAppData,
1282 .path = L"Microsoft\\Windows\\Templates",
1284 { /* 0x16 - CSIDL_COMMON_STARTMENU */
1285 .id = &FOLDERID_CommonStartMenu,
1286 .type = CSIDL_Type_ProgramData,
1287 .value = L"Common Start Menu",
1288 .category = KF_CATEGORY_COMMON,
1289 .name = L"Common Start Menu",
1290 .parent = &FOLDERID_ProgramData,
1291 .path = L"Microsoft\\Windows\\Start Menu",
1292 .attributes = FILE_ATTRIBUTE_READONLY,
1294 { /* 0x17 - CSIDL_COMMON_PROGRAMS */
1295 .id = &FOLDERID_CommonPrograms,
1296 .type = CSIDL_Type_ProgramData,
1297 .value = L"Common Programs",
1298 .category = KF_CATEGORY_COMMON,
1299 .name = L"Common Programs",
1300 .parent = &FOLDERID_CommonStartMenu,
1301 .path = L"Programs",
1302 .attributes = FILE_ATTRIBUTE_READONLY,
1304 { /* 0x18 - CSIDL_COMMON_STARTUP */
1305 .id = &FOLDERID_CommonStartup,
1306 .type = CSIDL_Type_ProgramData,
1307 .value = L"Common StartUp",
1308 .category = KF_CATEGORY_COMMON,
1309 .name = L"Common Startup",
1310 .parent = &FOLDERID_CommonPrograms,
1311 .path = L"StartUp",
1312 .attributes = FILE_ATTRIBUTE_READONLY,
1313 .flags = KFDF_PRECREATE,
1315 { /* 0x19 - CSIDL_COMMON_DESKTOPDIRECTORY */
1316 .id = &FOLDERID_PublicDesktop,
1317 .type = CSIDL_Type_AllUsers,
1318 .value = L"Common Desktop",
1319 .category = KF_CATEGORY_COMMON,
1320 .name = L"Common Desktop",
1321 .parent = &FOLDERID_Public,
1322 .path = L"Desktop",
1323 .attributes = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN,
1324 .flags = KFDF_PRECREATE,
1326 { /* 0x1a - CSIDL_APPDATA */
1327 .id = &FOLDERID_RoamingAppData,
1328 .type = CSIDL_Type_User,
1329 .value = L"AppData",
1330 .category = KF_CATEGORY_PERUSER,
1331 .name = L"AppData",
1332 .parent = &FOLDERID_Profile,
1333 .path = L"AppData\\Roaming",
1335 { /* 0x1b - CSIDL_PRINTHOOD */
1336 .id = &FOLDERID_PrintHood,
1337 .type = CSIDL_Type_User,
1338 .value = L"PrintHood",
1339 .category = KF_CATEGORY_PERUSER,
1340 .name = L"PrintHood",
1341 .parent = &FOLDERID_RoamingAppData,
1342 .path = L"Microsoft\\Windows\\Printer Shortcuts",
1344 { /* 0x1c - CSIDL_LOCAL_APPDATA */
1345 .id = &FOLDERID_LocalAppData,
1346 .type = CSIDL_Type_User,
1347 .value = L"Local AppData",
1348 .category = KF_CATEGORY_PERUSER,
1349 .name = L"Local AppData",
1350 .parent = &FOLDERID_Profile,
1351 .path = L"AppData\\Local",
1352 .flags = KFDF_LOCAL_REDIRECT_ONLY | KFDF_PUBLISHEXPANDEDPATH,
1354 { /* 0x1d - CSIDL_ALTSTARTUP */
1355 .id = &GUID_NULL,
1356 .type = CSIDL_Type_NonExistent,
1358 { /* 0x1e - CSIDL_COMMON_ALTSTARTUP */
1359 .id = &GUID_NULL,
1360 .type = CSIDL_Type_NonExistent,
1362 { /* 0x1f - CSIDL_COMMON_FAVORITES */
1363 .id = &FOLDERID_Favorites,
1364 .type = CSIDL_Type_AllUsers,
1365 .value = L"Common Favorites",
1366 .category = KF_CATEGORY_PERUSER,
1367 .name = L"Favorites",
1368 .parent = &FOLDERID_Profile,
1369 .path = L"Favorites",
1370 .attributes = FILE_ATTRIBUTE_READONLY,
1371 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1373 { /* 0x20 - CSIDL_INTERNET_CACHE */
1374 .id = &FOLDERID_InternetCache,
1375 .type = CSIDL_Type_User,
1376 .value = L"Cache",
1377 .category = KF_CATEGORY_PERUSER,
1378 .name = L"Cache",
1379 .parent = &FOLDERID_LocalAppData,
1380 .path = L"Microsoft\\Windows\\INetCache",
1381 .flags = KFDF_LOCAL_REDIRECT_ONLY,
1383 { /* 0x21 - CSIDL_COOKIES */
1384 .id = &FOLDERID_Cookies,
1385 .type = CSIDL_Type_User,
1386 .value = L"Cookies",
1387 .category = KF_CATEGORY_PERUSER,
1388 .name = L"Cookies",
1389 .parent = &FOLDERID_LocalAppData,
1390 .path = L"Microsoft\\Windows\\INetCookies",
1392 { /* 0x22 - CSIDL_HISTORY */
1393 .id = &FOLDERID_History,
1394 .type = CSIDL_Type_User,
1395 .value = L"History",
1396 .category = KF_CATEGORY_PERUSER,
1397 .name = L"History",
1398 .parent = &FOLDERID_LocalAppData,
1399 .path = L"Microsoft\\Windows\\History",
1400 .flags = KFDF_LOCAL_REDIRECT_ONLY,
1402 { /* 0x23 - CSIDL_COMMON_APPDATA */
1403 .id = &FOLDERID_ProgramData,
1404 .type = CSIDL_Type_ProgramData,
1405 .value = L"Common AppData",
1406 .category = KF_CATEGORY_FIXED,
1407 .name = L"Common AppData",
1409 { /* 0x24 - CSIDL_WINDOWS */
1410 .id = &FOLDERID_Windows,
1411 .type = CSIDL_Type_WindowsPath,
1412 .category = KF_CATEGORY_FIXED,
1413 .name = L"Windows",
1415 { /* 0x25 - CSIDL_SYSTEM */
1416 .id = &FOLDERID_System,
1417 .type = CSIDL_Type_SystemPath,
1418 .category = KF_CATEGORY_FIXED,
1419 .name = L"System",
1421 { /* 0x26 - CSIDL_PROGRAM_FILES */
1422 .id = &FOLDERID_ProgramFiles,
1423 .type = CSIDL_Type_CurrVer,
1424 .value = L"ProgramFilesDir",
1425 .def_path = L"Program Files",
1426 .category = KF_CATEGORY_FIXED,
1427 .name = L"ProgramFiles",
1428 .attributes = FILE_ATTRIBUTE_READONLY,
1430 { /* 0x27 - CSIDL_MYPICTURES */
1431 .id = &FOLDERID_Pictures,
1432 .type = CSIDL_Type_User,
1433 .value = L"My Pictures",
1434 .category = KF_CATEGORY_PERUSER,
1435 .name = L"My Pictures",
1436 .parent = &FOLDERID_Profile,
1437 .path = L"Pictures",
1438 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{33E28130-4E1E-4676-835A-98395C3BC3BB}",
1439 .attributes = FILE_ATTRIBUTE_READONLY,
1440 .flags = KFDF_ROAMABLE | KFDF_PRECREATE,
1442 { /* 0x28 - CSIDL_PROFILE */
1443 .id = &FOLDERID_Profile,
1444 .type = CSIDL_Type_User,
1445 .category = KF_CATEGORY_FIXED,
1446 .name = L"Profile",
1448 { /* 0x29 - CSIDL_SYSTEMX86 */
1449 .id = &FOLDERID_SystemX86,
1450 .type = CSIDL_Type_SystemX86Path,
1451 .category = KF_CATEGORY_FIXED,
1452 .name = L"SystemX86",
1454 { /* 0x2a - CSIDL_PROGRAM_FILESX86 */
1455 .id = &FOLDERID_ProgramFilesX86,
1456 .type = CSIDL_Type_CurrVer,
1457 .value = L"ProgramFilesDir (x86)",
1458 .def_path = L"Program Files (x86)",
1459 .category = KF_CATEGORY_FIXED,
1460 .name = L"ProgramFilesX86",
1461 .attributes = FILE_ATTRIBUTE_READONLY,
1463 { /* 0x2b - CSIDL_PROGRAM_FILES_COMMON */
1464 .id = &FOLDERID_ProgramFilesCommon,
1465 .type = CSIDL_Type_CurrVer,
1466 .value = L"CommonFilesDir",
1467 .def_path = L"Program Files\\Common Files",
1468 .category = KF_CATEGORY_FIXED,
1469 .name = L"ProgramFilesCommon",
1471 { /* 0x2c - CSIDL_PROGRAM_FILES_COMMONX86 */
1472 .id = &FOLDERID_ProgramFilesCommonX86,
1473 .type = CSIDL_Type_CurrVer,
1474 .value = L"CommonFilesDir (x86)",
1475 .def_path = L"Program Files (x86)\\Common Files",
1476 .category = KF_CATEGORY_FIXED,
1477 .name = L"ProgramFilesCommonX86",
1479 { /* 0x2d - CSIDL_COMMON_TEMPLATES */
1480 .id = &FOLDERID_CommonTemplates,
1481 .type = CSIDL_Type_ProgramData,
1482 .value = L"Common Templates",
1483 .category = KF_CATEGORY_COMMON,
1484 .name = L"Common Templates",
1485 .parent = &FOLDERID_ProgramData,
1486 .path = L"Microsoft\\Windows\\Templates",
1488 { /* 0x2e - CSIDL_COMMON_DOCUMENTS */
1489 .id = &FOLDERID_PublicDocuments,
1490 .type = CSIDL_Type_AllUsers,
1491 .value = L"Common Documents",
1492 .category = KF_CATEGORY_COMMON,
1493 .name = L"Common Documents",
1494 .parent = &FOLDERID_Public,
1495 .path = L"Documents",
1496 .attributes = FILE_ATTRIBUTE_READONLY,
1497 .flags = KFDF_PRECREATE,
1499 { /* 0x2f - CSIDL_COMMON_ADMINTOOLS */
1500 .id = &FOLDERID_CommonAdminTools,
1501 .type = CSIDL_Type_ProgramData,
1502 .value = L"Common Administrative Tools",
1503 .category = KF_CATEGORY_COMMON,
1504 .name = L"Common Administrative Tools",
1505 .parent = &FOLDERID_CommonPrograms,
1506 .path = L"Administrative Tools",
1507 .attributes = FILE_ATTRIBUTE_READONLY,
1508 .flags = KFDF_PRECREATE,
1510 { /* 0x30 - CSIDL_ADMINTOOLS */
1511 .id = &FOLDERID_AdminTools,
1512 .type = CSIDL_Type_User,
1513 .value = L"Administrative Tools",
1514 .category = KF_CATEGORY_PERUSER,
1515 .name = L"Administrative Tools",
1516 .parent = &FOLDERID_Programs,
1517 .path = L"Administrative Tools",
1518 .attributes = FILE_ATTRIBUTE_READONLY,
1519 .flags = KFDF_PRECREATE,
1521 { /* 0x31 - CSIDL_CONNECTIONS */
1522 .id = &FOLDERID_ConnectionsFolder,
1523 .type = CSIDL_Type_Disallowed,
1524 .category = KF_CATEGORY_VIRTUAL,
1525 .name = L"ConnectionsFolder",
1526 .path = L"Administrative Tools",
1527 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}",
1529 { /* 0x32 - unassigned */
1530 .id = &GUID_NULL,
1531 .type = CSIDL_Type_Disallowed,
1533 { /* 0x33 - unassigned */
1534 .id = &GUID_NULL,
1535 .type = CSIDL_Type_Disallowed,
1537 { /* 0x34 - unassigned */
1538 .id = &GUID_NULL,
1539 .type = CSIDL_Type_Disallowed,
1541 { /* 0x35 - CSIDL_COMMON_MUSIC */
1542 .id = &FOLDERID_PublicMusic,
1543 .type = CSIDL_Type_AllUsers,
1544 .value = L"CommonMusic",
1545 .category = KF_CATEGORY_COMMON,
1546 .name = L"CommonMusic",
1547 .parent = &FOLDERID_Public,
1548 .path = L"Music",
1549 .attributes = FILE_ATTRIBUTE_READONLY,
1550 .flags = KFDF_PRECREATE,
1552 { /* 0x36 - CSIDL_COMMON_PICTURES */
1553 .id = &FOLDERID_PublicPictures,
1554 .type = CSIDL_Type_AllUsers,
1555 .value = L"CommonPictures",
1556 .category = KF_CATEGORY_COMMON,
1557 .name = L"CommonPictures",
1558 .parent = &FOLDERID_Public,
1559 .path = L"Pictures",
1560 .attributes = FILE_ATTRIBUTE_READONLY,
1561 .flags = KFDF_PRECREATE,
1563 { /* 0x37 - CSIDL_COMMON_VIDEO */
1564 .id = &FOLDERID_PublicVideos,
1565 .type = CSIDL_Type_AllUsers,
1566 .value = L"CommonVideo",
1567 .category = KF_CATEGORY_COMMON,
1568 .name = L"CommonVideo",
1569 .parent = &FOLDERID_Public,
1570 .path = L"Videos",
1571 .attributes = FILE_ATTRIBUTE_READONLY,
1572 .flags = KFDF_PRECREATE,
1574 { /* 0x38 - CSIDL_RESOURCES */
1575 .id = &FOLDERID_ResourceDir,
1576 .type = CSIDL_Type_WindowsPath,
1577 .def_path = L"Resources",
1578 .category = KF_CATEGORY_FIXED,
1579 .name = L"ResourceDir",
1581 { /* 0x39 - CSIDL_RESOURCES_LOCALIZED */
1582 .id = &FOLDERID_LocalizedResourcesDir,
1583 .type = CSIDL_Type_NonExistent,
1584 .category = KF_CATEGORY_FIXED,
1585 .name = L"LocalizedResourcesDir",
1587 { /* 0x3a - CSIDL_COMMON_OEM_LINKS */
1588 .id = &FOLDERID_CommonOEMLinks,
1589 .type = CSIDL_Type_ProgramData,
1590 .category = KF_CATEGORY_COMMON,
1591 .name = L"OEM Links",
1592 .parent = &FOLDERID_ProgramData,
1593 .path = L"OEM Links",
1595 { /* 0x3b - CSIDL_CDBURN_AREA */
1596 .id = &FOLDERID_CDBurning,
1597 .type = CSIDL_Type_User,
1598 .value = L"CD Burning",
1599 .category = KF_CATEGORY_PERUSER,
1600 .name = L"CD Burning",
1601 .parent = &FOLDERID_LocalAppData,
1602 .path = L"Microsoft\\Windows\\Burn\\Burn",
1603 .attributes = FILE_ATTRIBUTE_READONLY,
1604 .flags = KFDF_LOCAL_REDIRECT_ONLY,
1606 { /* 0x3c unassigned */
1607 .id = &GUID_NULL,
1608 .type = CSIDL_Type_Disallowed,
1610 { /* 0x3d - CSIDL_COMPUTERSNEARME */
1611 .id = &GUID_NULL,
1612 .type = CSIDL_Type_Disallowed, /* FIXME */
1614 { /* 0x3e - CSIDL_PROFILES */
1615 .id = &GUID_NULL,
1616 .type = CSIDL_Type_Disallowed, /* oddly, this matches WinXP */
1618 { /* 0x3f */
1619 .id = &FOLDERID_AddNewPrograms,
1620 .type = CSIDL_Type_Disallowed,
1621 .category = KF_CATEGORY_VIRTUAL,
1622 .name = L"AddNewProgramsFolder",
1623 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{15eae92e-f17a-4431-9f28-805e482dafd4}",
1625 { /* 0x40 */
1626 .id = &FOLDERID_AppUpdates,
1627 .type = CSIDL_Type_Disallowed,
1628 .category = KF_CATEGORY_VIRTUAL,
1629 .name = L"AppUpdatesFolder",
1630 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7b81be6a-ce2b-4676-a29e-eb907a5126c5}\\::{d450a8a1-9568-45c7-9c0e-b4f9fb4537bd}",
1632 { /* 0x41 */
1633 .id = &FOLDERID_ChangeRemovePrograms,
1634 .type = CSIDL_Type_Disallowed,
1635 .category = KF_CATEGORY_VIRTUAL,
1636 .name = L"ChangeRemoveProgramsFolder",
1637 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7b81be6a-ce2b-4676-a29e-eb907a5126c5}",
1639 { /* 0x42 */
1640 .id = &FOLDERID_ConflictFolder,
1641 .type = CSIDL_Type_Disallowed,
1642 .category = KF_CATEGORY_VIRTUAL,
1643 .name = L"ConflictFolder",
1644 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}\\::{E413D040-6788-4C22-957E-175D1C513A34},",
1646 { /* 0x43 - CSIDL_CONTACTS */
1647 .id = &FOLDERID_Contacts,
1648 .type = CSIDL_Type_User,
1649 .category = KF_CATEGORY_PERUSER,
1650 .name = L"Contacts",
1651 .parent = &FOLDERID_Profile,
1652 .path = L"Contacts",
1653 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{56784854-C6CB-462B-8169-88E350ACB882}",
1654 .attributes = FILE_ATTRIBUTE_READONLY,
1655 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1657 { /* 0x44 */
1658 .id = &FOLDERID_DeviceMetadataStore,
1659 .type = CSIDL_Type_Disallowed, /* FIXME */
1660 .category = KF_CATEGORY_COMMON,
1661 .name = L"Device Metadata Store",
1662 .parent = &FOLDERID_ProgramData,
1663 .path = L"Microsoft\\Windows\\DeviceMetadataStore",
1665 { /* 0x45 */
1666 .id = &GUID_NULL,
1667 .type = CSIDL_Type_Disallowed,
1669 { /* 0x46 */
1670 .id = &FOLDERID_DocumentsLibrary,
1671 .type = CSIDL_Type_Disallowed, /* FIXME */
1672 .category = KF_CATEGORY_PERUSER,
1673 .name = L"DocumentsLibrary",
1674 .parent = &FOLDERID_Libraries,
1675 .path = L"Documents.library-ms",
1676 .parsing = L"::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\\{7b0db17d-9cd2-4a93-9733-46cc89022e7c}",
1677 .flags = KFDF_PRECREATE | KFDF_STREAM,
1679 { /* 0x47 - CSIDL_DOWNLOADS */
1680 .id = &FOLDERID_Downloads,
1681 .type = CSIDL_Type_User,
1682 .category = KF_CATEGORY_PERUSER,
1683 .name = L"Downloads",
1684 .parent = &FOLDERID_Profile,
1685 .path = L"Downloads",
1686 .attributes = FILE_ATTRIBUTE_READONLY,
1687 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1689 { /* 0x48 */
1690 .id = &FOLDERID_Games,
1691 .type = CSIDL_Type_Disallowed,
1692 .category = KF_CATEGORY_VIRTUAL,
1693 .name = L"Games",
1694 .parsing = L"::{ED228FDF-9EA8-4870-83b1-96b02CFE0D52}",
1696 { /* 0x49 */
1697 .id = &FOLDERID_GameTasks,
1698 .type = CSIDL_Type_Disallowed, /* FIXME */
1699 .category = KF_CATEGORY_PERUSER,
1700 .name = L"GameTasks",
1701 .parent = &FOLDERID_LocalAppData,
1702 .path = L"Microsoft\\Windows\\GameExplorer",
1703 .flags = KFDF_LOCAL_REDIRECT_ONLY,
1705 { /* 0x4a */
1706 .id = &FOLDERID_HomeGroup,
1707 .type = CSIDL_Type_Disallowed,
1708 .category = KF_CATEGORY_VIRTUAL,
1709 .name = L"HomeGroupFolder",
1710 .parsing = L"::{B4FB3F98-C1EA-428d-A78A-D1F5659CBA93}",
1712 { /* 0x4b */
1713 .id = &FOLDERID_ImplicitAppShortcuts,
1714 .type = CSIDL_Type_Disallowed, /* FIXME */
1715 .category = KF_CATEGORY_PERUSER,
1716 .name = L"ImplicitAppShortcuts",
1717 .parent = &FOLDERID_UserPinned,
1718 .path = L"ImplicitAppShortcuts",
1719 .flags = KFDF_PRECREATE,
1721 { /* 0x4c */
1722 .id = &FOLDERID_Libraries,
1723 .type = CSIDL_Type_Disallowed, /* FIXME */
1724 .category = KF_CATEGORY_PERUSER,
1725 .name = L"Libraries",
1726 .parent = &FOLDERID_RoamingAppData,
1727 .path = L"Microsoft\\Windows\\Libraries",
1728 .flags = KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1730 { /* 0x4d - CSIDL_LINKS */
1731 .id = &FOLDERID_Links,
1732 .type = CSIDL_Type_User,
1733 .category = KF_CATEGORY_PERUSER,
1734 .name = L"Links",
1735 .parent = &FOLDERID_Profile,
1736 .path = L"Links",
1737 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{bfb9d5e0-c6a9-404c-b2b2-ae6db6af4968}",
1738 .attributes = FILE_ATTRIBUTE_READONLY,
1739 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1741 { /* 0x4e - CSIDL_APPDATA_LOCALLOW */
1742 .id = &FOLDERID_LocalAppDataLow,
1743 .type = CSIDL_Type_User,
1744 .category = KF_CATEGORY_PERUSER,
1745 .name = L"LocalAppDataLow",
1746 .parent = &FOLDERID_Profile,
1747 .path = L"AppData\\LocalLow",
1748 .attributes = FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
1749 .flags = KFDF_LOCAL_REDIRECT_ONLY | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1751 { /* 0x4f */
1752 .id = &FOLDERID_MusicLibrary,
1753 .type = CSIDL_Type_Disallowed, /* FIXME */
1754 .category = KF_CATEGORY_PERUSER,
1755 .name = L"MusicLibrary",
1756 .parent = &FOLDERID_Libraries,
1757 .path = L"Music.library-ms",
1758 .parsing = L"::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\\{2112AB0A-C86A-4ffe-A368-0DE96E47012E}",
1759 .flags = KFDF_PRECREATE | KFDF_STREAM,
1761 { /* 0x50 */
1762 .id = &FOLDERID_OriginalImages,
1763 .type = CSIDL_Type_Disallowed, /* FIXME */
1764 .category = KF_CATEGORY_PERUSER,
1765 .name = L"Original Images",
1766 .parent = &FOLDERID_LocalAppData,
1767 .path = L"Microsoft\\Windows Photo Gallery\\Original Images",
1769 { /* 0x51 */
1770 .id = &FOLDERID_PhotoAlbums,
1771 .type = CSIDL_Type_User,
1772 .category = KF_CATEGORY_PERUSER,
1773 .name = L"PhotoAlbums",
1774 .parent = &FOLDERID_Pictures,
1775 .path = L"Slide Shows",
1776 .attributes = FILE_ATTRIBUTE_READONLY,
1778 { /* 0x52 */
1779 .id = &FOLDERID_PicturesLibrary,
1780 .type = CSIDL_Type_Disallowed, /* FIXME */
1781 .category = KF_CATEGORY_PERUSER,
1782 .name = L"PicturesLibrary",
1783 .parent = &FOLDERID_Libraries,
1784 .path = L"Pictures.library-ms",
1785 .parsing = L"::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\\{A990AE9F-A03B-4e80-94BC-9912D7504104}",
1786 .flags = KFDF_PRECREATE | KFDF_STREAM,
1788 { /* 0x53 */
1789 .id = &FOLDERID_Playlists,
1790 .type = CSIDL_Type_User,
1791 .category = KF_CATEGORY_PERUSER,
1792 .name = L"Playlists",
1793 .parent = &FOLDERID_Music,
1794 .path = L"Playlists",
1795 .attributes = FILE_ATTRIBUTE_READONLY,
1797 { /* 0x54 */
1798 .id = &FOLDERID_ProgramFilesX64,
1799 #ifdef _WIN64
1800 .type = CSIDL_Type_CurrVer,
1801 .value = L"ProgramFilesDir",
1802 .def_path = L"Program Files",
1803 #else
1804 .type = CSIDL_Type_NonExistent,
1805 #endif
1806 .category = KF_CATEGORY_FIXED,
1807 .name = L"ProgramFilesX64",
1809 { /* 0x55 */
1810 .id = &FOLDERID_ProgramFilesCommonX64,
1811 #ifdef _WIN64
1812 .type = CSIDL_Type_CurrVer,
1813 .value = L"ProgramFilesCommonX64",
1814 .def_path = L"Program Files\\Common Files",
1815 #else
1816 .type = CSIDL_Type_NonExistent,
1817 #endif
1818 .category = KF_CATEGORY_FIXED,
1819 .name = L"ProgramFilesCommonX64",
1821 { /* 0x56 */
1822 .id = &FOLDERID_Public,
1823 .type = CSIDL_Type_AllUsers,
1824 .category = KF_CATEGORY_FIXED,
1825 .name = L"Public",
1826 .parsing = L"::{4336a54d-038b-4685-ab02-99bb52d3fb8b}",
1827 .attributes = FILE_ATTRIBUTE_READONLY,
1828 .flags = KFDF_PRECREATE,
1830 { /* 0x57 */
1831 .id = &FOLDERID_PublicDownloads,
1832 .type = CSIDL_Type_AllUsers,
1833 .category = KF_CATEGORY_COMMON,
1834 .name = L"CommonDownloads",
1835 .parent = &FOLDERID_Public,
1836 .path = L"Downloads",
1837 .attributes = FILE_ATTRIBUTE_READONLY,
1838 .flags = KFDF_PRECREATE,
1840 { /* 0x58 */
1841 .id = &FOLDERID_PublicGameTasks,
1842 .type = CSIDL_Type_ProgramData,
1843 .category = KF_CATEGORY_COMMON,
1844 .name = L"PublicGameTasks",
1845 .parent = &FOLDERID_ProgramData,
1846 .path = L"Microsoft\\Windows\\GameExplorer",
1847 .flags = KFDF_LOCAL_REDIRECT_ONLY,
1849 { /* 0x59 */
1850 .id = &FOLDERID_PublicLibraries,
1851 .type = CSIDL_Type_AllUsers,
1852 .category = KF_CATEGORY_COMMON,
1853 .name = L"PublicLibraries",
1854 .parent = &FOLDERID_Public,
1855 .path = L"Libraries",
1856 .attributes = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN,
1857 .flags = KFDF_PRECREATE,
1859 { /* 0x5a */
1860 .id = &FOLDERID_PublicRingtones,
1861 .type = CSIDL_Type_ProgramData,
1862 .category = KF_CATEGORY_COMMON,
1863 .name = L"CommonRingtones",
1864 .parent = &FOLDERID_ProgramData,
1865 .path = L"Microsoft\\Windows\\Ringtones",
1866 .flags = KFDF_PRECREATE,
1868 { /* 0x5b */
1869 .id = &FOLDERID_QuickLaunch,
1870 .type = CSIDL_Type_Disallowed, /* FIXME */
1871 .category = KF_CATEGORY_PERUSER,
1872 .name = L"Quick Launch",
1873 .parent = &FOLDERID_RoamingAppData,
1874 .path = L"Microsoft\\Internet Explorer\\Quick Launch",
1876 { /* 0x5c */
1877 .id = &FOLDERID_RecordedTVLibrary,
1878 .type = CSIDL_Type_Disallowed, /* FIXME */
1879 .category = KF_CATEGORY_COMMON,
1880 .name = L"RecordedTVLibrary",
1881 .parent = &FOLDERID_PublicLibraries,
1882 .path = L"RecordedTV.library-ms",
1883 .flags = KFDF_PRECREATE | KFDF_STREAM,
1885 { /* 0x5d */
1886 .id = &FOLDERID_Ringtones,
1887 .type = CSIDL_Type_Disallowed, /* FIXME */
1888 .category = KF_CATEGORY_PERUSER,
1889 .name = L"Ringtones",
1890 .parent = &FOLDERID_LocalAppData,
1891 .path = L"Microsoft\\Windows\\Ringtones",
1892 .flags = KFDF_PRECREATE,
1894 { /* 0x5e */
1895 .id = &FOLDERID_SampleMusic,
1896 .type = CSIDL_Type_AllUsers,
1897 .category = KF_CATEGORY_COMMON,
1898 .name = L"SampleMusic",
1899 .parent = &FOLDERID_PublicMusic,
1900 .path = L"Sample Music",
1901 .attributes = FILE_ATTRIBUTE_READONLY,
1902 .flags = KFDF_PRECREATE,
1904 { /* 0x5f */
1905 .id = &FOLDERID_SamplePictures,
1906 .type = CSIDL_Type_AllUsers,
1907 .category = KF_CATEGORY_COMMON,
1908 .name = L"SamplePictures",
1909 .parent = &FOLDERID_PublicPictures,
1910 .path = L"Sample Pictures",
1911 .attributes = FILE_ATTRIBUTE_READONLY,
1912 .flags = KFDF_PRECREATE,
1914 { /* 0x60 */
1915 .id = &FOLDERID_SamplePlaylists,
1916 .type = CSIDL_Type_AllUsers,
1917 .category = KF_CATEGORY_COMMON,
1918 .name = L"SamplePlaylists",
1919 .parent = &FOLDERID_PublicMusic,
1920 .path = L"Sample Playlists",
1921 .attributes = FILE_ATTRIBUTE_READONLY,
1922 .flags = KFDF_PRECREATE,
1924 { /* 0x61 */
1925 .id = &FOLDERID_SampleVideos,
1926 .type = CSIDL_Type_AllUsers,
1927 .category = KF_CATEGORY_COMMON,
1928 .name = L"SampleVideos",
1929 .parent = &FOLDERID_PublicVideos,
1930 .path = L"Sample Videos",
1931 .attributes = FILE_ATTRIBUTE_READONLY,
1932 .flags = KFDF_PRECREATE,
1934 { /* 0x62 - CSIDL_SAVED_GAMES */
1935 .id = &FOLDERID_SavedGames,
1936 .type = CSIDL_Type_User,
1937 .category = KF_CATEGORY_PERUSER,
1938 .name = L"SavedGames",
1939 .parent = &FOLDERID_Profile,
1940 .path = L"Saved Games",
1941 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{4C5C32FF-BB9D-43b0-B5B4-2D72E54EAAA4}",
1942 .attributes = FILE_ATTRIBUTE_READONLY,
1943 .flags = KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1945 { /* 0x63 - CSIDL_SEARCHES */
1946 .id = &FOLDERID_SavedSearches,
1947 .type = CSIDL_Type_User,
1948 .category = KF_CATEGORY_PERUSER,
1949 .name = L"Searches",
1950 .parent = &FOLDERID_Profile,
1951 .path = L"Searches",
1952 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{7d1d3a04-debb-4115-95cf-2f29da2920da}",
1953 .attributes = FILE_ATTRIBUTE_READONLY,
1954 .flags = KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH,
1956 { /* 0x64 */
1957 .id = &FOLDERID_SEARCH_CSC,
1958 .type = CSIDL_Type_Disallowed,
1959 .category = KF_CATEGORY_VIRTUAL,
1960 .name = L"CSCFolder",
1961 .parsing = L"shell:::{BD7A2E7B-21CB-41b2-A086-B309680C6B7E}\\*",
1963 { /* 0x65 */
1964 .id = &FOLDERID_SEARCH_MAPI,
1965 .type = CSIDL_Type_Disallowed,
1966 .category = KF_CATEGORY_VIRTUAL,
1967 .name = L"MAPIFolder",
1968 .parsing = L"shell:::{89D83576-6BD1-4C86-9454-BEB04E94C819}\\*",
1970 { /* 0x66 */
1971 .id = &FOLDERID_SearchHome,
1972 .type = CSIDL_Type_Disallowed,
1973 .category = KF_CATEGORY_VIRTUAL,
1974 .name = L"SearchHomeFolder",
1975 .parsing = L"::{9343812e-1c37-4a49-a12e-4b2d810d956b}",
1977 { /* 0x67 */
1978 .id = &FOLDERID_SidebarDefaultParts,
1979 .type = CSIDL_Type_Disallowed, /* FIXME */
1980 .category = KF_CATEGORY_COMMON,
1981 .name = L"Default Gadgets",
1982 .parent = &FOLDERID_ProgramFiles,
1983 .path = L"Windows Sidebar\\Gadgets",
1985 { /* 0x68 */
1986 .id = &FOLDERID_SidebarParts,
1987 .type = CSIDL_Type_Disallowed, /* FIXME */
1988 .category = KF_CATEGORY_PERUSER,
1989 .name = L"Gadgets",
1990 .parent = &FOLDERID_LocalAppData,
1991 .path = L"Microsoft\\Windows Sidebar\\Gadgets",
1993 { /* 0x69 */
1994 .id = &FOLDERID_SyncManagerFolder,
1995 .type = CSIDL_Type_Disallowed,
1996 .category = KF_CATEGORY_VIRTUAL,
1997 .name = L"SyncCenterFolder",
1998 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}",
2000 { /* 0x6a */
2001 .id = &FOLDERID_SyncResultsFolder,
2002 .type = CSIDL_Type_Disallowed,
2003 .category = KF_CATEGORY_VIRTUAL,
2004 .name = L"SyncResultsFolder",
2005 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}\\::{BC48B32F-5910-47F5-8570-5074A8A5636A},",
2007 { /* 0x6b */
2008 .id = &FOLDERID_SyncSetupFolder,
2009 .type = CSIDL_Type_Disallowed,
2010 .category = KF_CATEGORY_VIRTUAL,
2011 .name = L"SyncSetupFolder",
2012 .parsing = L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}\\::{F1390A9A-A3F4-4E5D-9C5F-98F3BD8D935C},",
2014 { /* 0x6c */
2015 .id = &FOLDERID_UserPinned,
2016 .type = CSIDL_Type_Disallowed, /* FIXME */
2017 .category = KF_CATEGORY_PERUSER,
2018 .name = L"User Pinned",
2019 .parent = &FOLDERID_QuickLaunch,
2020 .path = L"User Pinned",
2021 .attributes = FILE_ATTRIBUTE_HIDDEN,
2022 .flags = KFDF_PRECREATE,
2024 { /* 0x6d */
2025 .id = &FOLDERID_UserProfiles,
2026 .type = CSIDL_Type_CurrVer,
2027 .value = L"Users",
2028 .def_path = L"Users",
2029 .category = KF_CATEGORY_FIXED,
2030 .name = L"UserProfiles",
2031 .attributes = FILE_ATTRIBUTE_READONLY,
2032 .flags = KFDF_PRECREATE,
2034 { /* 0x6e */
2035 .id = &FOLDERID_UserProgramFiles,
2036 .type = CSIDL_Type_Disallowed, /* FIXME */
2037 .category = KF_CATEGORY_PERUSER,
2038 .name = L"UserProgramFiles",
2039 .parent = &FOLDERID_LocalAppData,
2040 .path = L"Programs",
2042 { /* 0x6f */
2043 .id = &FOLDERID_UserProgramFilesCommon,
2044 .type = CSIDL_Type_Disallowed, /* FIXME */
2045 .category = KF_CATEGORY_PERUSER,
2046 .name = L"UserProgramFilesCommon",
2047 .parent = &FOLDERID_UserProgramFiles,
2048 .path = L"Common",
2050 { /* 0x70 */
2051 .id = &FOLDERID_UsersFiles,
2052 .type = CSIDL_Type_Disallowed,
2053 .category = KF_CATEGORY_VIRTUAL,
2054 .name = L"UsersFilesFolder",
2055 .parsing = L"::{59031a47-3f72-44a7-89c5-5595fe6b30ee}",
2057 { /* 0x71 */
2058 .id = &FOLDERID_UsersLibraries,
2059 .type = CSIDL_Type_Disallowed,
2060 .category = KF_CATEGORY_VIRTUAL,
2061 .name = L"UsersLibrariesFolder",
2062 .parsing = L"::{031E4825-7B94-4dc3-B131-E946B44C8DD5}",
2064 { /* 0x72 */
2065 .id = &FOLDERID_VideosLibrary,
2066 .type = CSIDL_Type_Disallowed, /* FIXME */
2067 .category = KF_CATEGORY_PERUSER,
2068 .name = L"VideosLibrary",
2069 .path = L"Videos.library-ms",
2070 .parsing = L"::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\\{491E922F-5643-4af4-A7EB-4E7A138D8174}",
2074 static int csidl_from_id( const KNOWNFOLDERID *id )
2076 int i;
2077 for (i = 0; i < ARRAY_SIZE(CSIDL_Data); i++)
2078 if (IsEqualGUID( CSIDL_Data[i].id, id )) return i;
2079 return -1;
2082 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest);
2084 /* Gets the value named value from the registry key
2085 * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
2086 * (or from rootKey\userPrefix\... if userPrefix is not NULL) into path, which
2087 * is assumed to be MAX_PATH WCHARs in length.
2088 * If it exists, expands the value and writes the expanded value to
2089 * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
2090 * Returns successful error code if the value was retrieved from the registry,
2091 * and a failure otherwise.
2093 static HRESULT _SHGetUserShellFolderPath(HKEY rootKey, LPCWSTR userPrefix,
2094 LPCWSTR value, LPWSTR path)
2096 HRESULT hr;
2097 WCHAR shellFolderPath[MAX_PATH], userShellFolderPath[MAX_PATH];
2098 LPCWSTR pShellFolderPath, pUserShellFolderPath;
2099 HKEY userShellFolderKey, shellFolderKey;
2100 DWORD dwType, dwPathLen;
2102 TRACE("%p,%s,%s,%p\n",rootKey, debugstr_w(userPrefix), debugstr_w(value),
2103 path);
2105 if (userPrefix)
2107 lstrcpyW(shellFolderPath, userPrefix);
2108 PathAddBackslashW(shellFolderPath);
2109 lstrcatW(shellFolderPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
2110 pShellFolderPath = shellFolderPath;
2111 lstrcpyW(userShellFolderPath, userPrefix);
2112 PathAddBackslashW(userShellFolderPath);
2113 lstrcatW(userShellFolderPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders");
2114 pUserShellFolderPath = userShellFolderPath;
2116 else
2118 pUserShellFolderPath = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders";
2119 pShellFolderPath = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
2122 if (RegCreateKeyW(rootKey, pShellFolderPath, &shellFolderKey))
2124 TRACE("Failed to create %s\n", debugstr_w(pShellFolderPath));
2125 return E_FAIL;
2127 if (RegCreateKeyW(rootKey, pUserShellFolderPath, &userShellFolderKey))
2129 TRACE("Failed to create %s\n",
2130 debugstr_w(pUserShellFolderPath));
2131 RegCloseKey(shellFolderKey);
2132 return E_FAIL;
2135 dwPathLen = MAX_PATH * sizeof(WCHAR);
2136 if (!RegQueryValueExW(userShellFolderKey, value, NULL, &dwType,
2137 (LPBYTE)path, &dwPathLen) && (dwType == REG_EXPAND_SZ || dwType == REG_SZ))
2139 LONG ret;
2141 path[dwPathLen / sizeof(WCHAR)] = '\0';
2142 if (dwType == REG_EXPAND_SZ && path[0] == '%')
2144 WCHAR szTemp[MAX_PATH];
2146 _SHExpandEnvironmentStrings(path, szTemp);
2147 lstrcpynW(path, szTemp, MAX_PATH);
2149 ret = RegSetValueExW(shellFolderKey, value, 0, REG_SZ, (LPBYTE)path,
2150 (lstrlenW(path) + 1) * sizeof(WCHAR));
2151 if (ret != ERROR_SUCCESS)
2152 hr = HRESULT_FROM_WIN32(ret);
2153 else
2154 hr = S_OK;
2156 else
2157 hr = E_FAIL;
2158 RegCloseKey(shellFolderKey);
2159 RegCloseKey(userShellFolderKey);
2160 TRACE("returning 0x%08x\n", hr);
2161 return hr;
2164 static void append_relative_path(BYTE folder, WCHAR *pszPath)
2166 if (CSIDL_Data[folder].path)
2168 PathAddBackslashW(pszPath);
2169 lstrcatW(pszPath, CSIDL_Data[folder].path);
2171 else if (CSIDL_Data[folder].def_path)
2173 PathAddBackslashW(pszPath);
2174 lstrcatW(pszPath, CSIDL_Data[folder].def_path);
2178 /* Gets a 'semi-expanded' default value of the CSIDL with index folder into
2179 * pszPath, based on the entries in CSIDL_Data. By semi-expanded, I mean:
2180 * - Depending on the entry's type, the path may begin with an (unexpanded)
2181 * environment variable name. The caller is responsible for expanding
2182 * environment strings if so desired.
2183 * The types that are prepended with environment variables are:
2184 * CSIDL_Type_User: %USERPROFILE%
2185 * CSIDL_Type_AllUsers: %ALLUSERSPROFILE%
2186 * CSIDL_Type_CurrVer: %SystemDrive%
2187 * (Others might make sense too, but as yet are unneeded.)
2189 static HRESULT _SHGetDefaultValue(BYTE folder, LPWSTR pszPath)
2191 HRESULT hr;
2193 TRACE("0x%02x,%p\n", folder, pszPath);
2195 if (folder >= ARRAY_SIZE(CSIDL_Data))
2196 return E_INVALIDARG;
2198 if (!pszPath)
2199 return E_INVALIDARG;
2201 if (!is_win64)
2203 BOOL is_wow64;
2205 switch (folder)
2207 case CSIDL_PROGRAM_FILES:
2208 case CSIDL_PROGRAM_FILESX86:
2209 IsWow64Process( GetCurrentProcess(), &is_wow64 );
2210 folder = is_wow64 ? CSIDL_PROGRAM_FILESX86 : CSIDL_PROGRAM_FILES;
2211 break;
2212 case CSIDL_PROGRAM_FILES_COMMON:
2213 case CSIDL_PROGRAM_FILES_COMMONX86:
2214 IsWow64Process( GetCurrentProcess(), &is_wow64 );
2215 folder = is_wow64 ? CSIDL_PROGRAM_FILES_COMMONX86 : CSIDL_PROGRAM_FILES_COMMON;
2216 break;
2220 if (!CSIDL_Data[folder].parent)
2222 /* hit the root, sub in env var */
2223 switch (CSIDL_Data[folder].type)
2225 case CSIDL_Type_User:
2226 lstrcpyW(pszPath, L"%USERPROFILE%");
2227 break;
2228 case CSIDL_Type_AllUsers:
2229 lstrcpyW(pszPath, L"%PUBLIC%");
2230 break;
2231 case CSIDL_Type_ProgramData:
2232 lstrcpyW(pszPath, L"%ProgramData%");
2233 break;
2234 case CSIDL_Type_CurrVer:
2235 lstrcpyW(pszPath, L"%SystemDrive%");
2236 break;
2237 default:
2238 ; /* no corresponding env. var, do nothing */
2240 hr = S_OK;
2241 }else{
2242 /* prepend with parent */
2243 hr = _SHGetDefaultValue(csidl_from_id(CSIDL_Data[folder].parent), pszPath);
2246 if (SUCCEEDED(hr))
2247 append_relative_path(folder, pszPath);
2249 TRACE("returning 0x%08x\n", hr);
2250 return hr;
2253 /* Gets the (unexpanded) value of the folder with index folder into pszPath.
2254 * The folder's type is assumed to be CSIDL_Type_CurrVer. Its default value
2255 * can be overridden in the HKLM\\Software\\Microsoft\\Windows\\CurrentVersion key.
2256 * If dwFlags has SHGFP_TYPE_DEFAULT set or if the value isn't overridden in
2257 * the registry, uses _SHGetDefaultValue to get the value.
2259 static HRESULT _SHGetCurrentVersionPath(DWORD dwFlags, BYTE folder,
2260 LPWSTR pszPath)
2262 HRESULT hr;
2264 TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
2266 if (folder >= ARRAY_SIZE(CSIDL_Data))
2267 return E_INVALIDARG;
2268 if (CSIDL_Data[folder].type != CSIDL_Type_CurrVer)
2269 return E_INVALIDARG;
2270 if (!pszPath)
2271 return E_INVALIDARG;
2273 if (dwFlags & SHGFP_TYPE_DEFAULT)
2274 hr = _SHGetDefaultValue(folder, pszPath);
2275 else
2277 HKEY hKey;
2279 if (RegCreateKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion", &hKey))
2280 hr = E_FAIL;
2281 else
2283 DWORD dwType, dwPathLen = MAX_PATH * sizeof(WCHAR);
2285 if (RegQueryValueExW(hKey, CSIDL_Data[folder].value, NULL,
2286 &dwType, (LPBYTE)pszPath, &dwPathLen) ||
2287 (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
2289 hr = _SHGetDefaultValue(folder, pszPath);
2290 dwType = REG_EXPAND_SZ;
2291 switch (folder)
2293 case CSIDL_PROGRAM_FILESX86:
2294 case CSIDL_PROGRAM_FILES_COMMONX86:
2295 /* these two should never be set on 32-bit setups */
2296 if (!is_win64)
2298 BOOL is_wow64;
2299 IsWow64Process( GetCurrentProcess(), &is_wow64 );
2300 if (!is_wow64) break;
2302 /* fall through */
2303 default:
2304 RegSetValueExW(hKey, CSIDL_Data[folder].value, 0, dwType,
2305 (LPBYTE)pszPath, (lstrlenW(pszPath)+1)*sizeof(WCHAR));
2308 else
2310 pszPath[dwPathLen / sizeof(WCHAR)] = '\0';
2311 hr = S_OK;
2313 RegCloseKey(hKey);
2316 TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
2317 return hr;
2320 static LPWSTR _GetUserSidStringFromToken(HANDLE Token)
2322 char InfoBuffer[64];
2323 PTOKEN_USER UserInfo;
2324 DWORD InfoSize;
2325 LPWSTR SidStr;
2327 UserInfo = (PTOKEN_USER) InfoBuffer;
2328 if (! GetTokenInformation(Token, TokenUser, InfoBuffer, sizeof(InfoBuffer),
2329 &InfoSize))
2331 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2332 return NULL;
2333 UserInfo = heap_alloc(InfoSize);
2334 if (UserInfo == NULL)
2335 return NULL;
2336 if (! GetTokenInformation(Token, TokenUser, UserInfo, InfoSize,
2337 &InfoSize))
2339 heap_free(UserInfo);
2340 return NULL;
2344 if (! ConvertSidToStringSidW(UserInfo->User.Sid, &SidStr))
2345 SidStr = NULL;
2347 if (UserInfo != (PTOKEN_USER) InfoBuffer)
2348 heap_free(UserInfo);
2350 return SidStr;
2353 /* Gets the user's path (unexpanded) for the CSIDL with index folder:
2354 * If SHGFP_TYPE_DEFAULT is set, calls _SHGetDefaultValue for it. Otherwise
2355 * calls _SHGetUserShellFolderPath for it. Where it looks depends on hToken:
2356 * - if hToken is -1, looks in HKEY_USERS\.Default
2357 * - otherwise looks first in HKEY_CURRENT_USER, followed by HKEY_LOCAL_MACHINE
2358 * if HKEY_CURRENT_USER doesn't contain any entries. If both fail, finally
2359 * calls _SHGetDefaultValue for it.
2361 static HRESULT _SHGetUserProfilePath(HANDLE hToken, DWORD dwFlags, BYTE folder,
2362 LPWSTR pszPath)
2364 const WCHAR *szValueName;
2365 WCHAR buffer[40];
2366 HRESULT hr;
2368 TRACE("%p,0x%08x,0x%02x,%p\n", hToken, dwFlags, folder, pszPath);
2370 if (folder >= ARRAY_SIZE(CSIDL_Data))
2371 return E_INVALIDARG;
2372 if (CSIDL_Data[folder].type != CSIDL_Type_User)
2373 return E_INVALIDARG;
2374 if (!pszPath)
2375 return E_INVALIDARG;
2377 if (dwFlags & SHGFP_TYPE_DEFAULT)
2379 if (hToken != NULL && hToken != (HANDLE)-1)
2381 FIXME("unsupported for user other than current or default\n");
2382 return E_FAIL;
2384 hr = _SHGetDefaultValue(folder, pszPath);
2386 else
2388 static const WCHAR DefaultW[] = L".Default";
2389 LPCWSTR userPrefix = NULL;
2390 HKEY hRootKey;
2392 if (hToken == (HANDLE)-1)
2394 hRootKey = HKEY_USERS;
2395 userPrefix = DefaultW;
2397 else if (hToken == NULL)
2398 hRootKey = HKEY_CURRENT_USER;
2399 else
2401 hRootKey = HKEY_USERS;
2402 userPrefix = _GetUserSidStringFromToken(hToken);
2403 if (userPrefix == NULL)
2405 hr = E_FAIL;
2406 goto error;
2410 /* For CSIDL_Type_User we also use the GUID if no szValueName is provided */
2411 szValueName = CSIDL_Data[folder].value;
2412 if (!szValueName)
2414 StringFromGUID2( CSIDL_Data[folder].id, buffer, 39 );
2415 szValueName = &buffer[0];
2418 hr = _SHGetUserShellFolderPath(hRootKey, userPrefix, szValueName, pszPath);
2419 if (FAILED(hr) && hRootKey != HKEY_LOCAL_MACHINE)
2420 hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL, szValueName, pszPath);
2421 if (FAILED(hr))
2422 hr = _SHGetDefaultValue(folder, pszPath);
2423 if (userPrefix != NULL && userPrefix != DefaultW)
2424 LocalFree((HLOCAL) userPrefix);
2426 error:
2427 TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
2428 return hr;
2431 /* Gets the (unexpanded) path for the CSIDL with index folder. If dwFlags has
2432 * SHGFP_TYPE_DEFAULT set, calls _SHGetDefaultValue. Otherwise calls
2433 * _SHGetUserShellFolderPath for it, looking only in HKEY_LOCAL_MACHINE.
2434 * If this fails, falls back to _SHGetDefaultValue.
2436 static HRESULT _SHGetAllUsersProfilePath(DWORD dwFlags, BYTE folder,
2437 LPWSTR pszPath)
2439 HRESULT hr;
2441 TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
2443 if (folder >= ARRAY_SIZE(CSIDL_Data))
2444 return E_INVALIDARG;
2445 if (CSIDL_Data[folder].type != CSIDL_Type_AllUsers && CSIDL_Data[folder].type != CSIDL_Type_ProgramData)
2446 return E_INVALIDARG;
2447 if (!pszPath)
2448 return E_INVALIDARG;
2450 if (dwFlags & SHGFP_TYPE_DEFAULT)
2451 hr = _SHGetDefaultValue(folder, pszPath);
2452 else
2454 hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL,
2455 CSIDL_Data[folder].value, pszPath);
2456 if (FAILED(hr))
2457 hr = _SHGetDefaultValue(folder, pszPath);
2459 TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
2460 return hr;
2463 static HRESULT _SHOpenProfilesKey(PHKEY pKey)
2465 LONG lRet;
2466 DWORD disp;
2468 lRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList", 0, NULL, 0,
2469 KEY_ALL_ACCESS, NULL, pKey, &disp);
2470 return HRESULT_FROM_WIN32(lRet);
2473 /* Reads the value named szValueName from the key profilesKey (assumed to be
2474 * opened by _SHOpenProfilesKey) into szValue, which is assumed to be MAX_PATH
2475 * WCHARs in length. If it doesn't exist, returns szDefault (and saves
2476 * szDefault to the registry).
2478 static HRESULT _SHGetProfilesValue(HKEY profilesKey, LPCWSTR szValueName,
2479 LPWSTR szValue, LPCWSTR szDefault)
2481 HRESULT hr;
2482 DWORD type, dwPathLen = MAX_PATH * sizeof(WCHAR);
2483 LONG lRet;
2485 TRACE("%p,%s,%p,%s\n", profilesKey, debugstr_w(szValueName), szValue,
2486 debugstr_w(szDefault));
2487 lRet = RegQueryValueExW(profilesKey, szValueName, NULL, &type,
2488 (LPBYTE)szValue, &dwPathLen);
2489 if (!lRet && (type == REG_SZ || type == REG_EXPAND_SZ) && dwPathLen
2490 && *szValue)
2492 dwPathLen /= sizeof(WCHAR);
2493 szValue[dwPathLen] = '\0';
2494 hr = S_OK;
2496 else
2498 /* Missing or invalid value, set a default */
2499 lstrcpynW(szValue, szDefault, MAX_PATH);
2500 TRACE("Setting missing value %s to %s\n", debugstr_w(szValueName),
2501 debugstr_w(szValue));
2502 lRet = RegSetValueExW(profilesKey, szValueName, 0, REG_EXPAND_SZ,
2503 (LPBYTE)szValue,
2504 (lstrlenW(szValue) + 1) * sizeof(WCHAR));
2505 if (lRet)
2506 hr = HRESULT_FROM_WIN32(lRet);
2507 else
2508 hr = S_OK;
2510 TRACE("returning 0x%08x (output value is %s)\n", hr, debugstr_w(szValue));
2511 return hr;
2514 /* Attempts to expand environment variables from szSrc into szDest, which is
2515 * assumed to be MAX_PATH characters in length. Before referring to the
2516 * environment, handles a few variables directly, because the environment
2517 * variables may not be set when this is called (as during Wine's installation
2518 * when default values are being written to the registry).
2519 * The directly handled environment variables, and their source, are:
2520 * - ALLUSERSPROFILE, USERPROFILE: reads from the registry
2521 * - SystemDrive: uses GetSystemDirectoryW and uses the drive portion of its
2522 * path
2523 * If one of the directly handled environment variables is expanded, only
2524 * expands a single variable, and only in the beginning of szSrc.
2526 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest)
2528 HRESULT hr;
2529 WCHAR szTemp[MAX_PATH], szProfilesPrefix[MAX_PATH] = { 0 };
2530 HKEY key = NULL;
2532 TRACE("%s, %p\n", debugstr_w(szSrc), szDest);
2534 if (!szSrc || !szDest) return E_INVALIDARG;
2536 /* short-circuit if there's nothing to expand */
2537 if (szSrc[0] != '%')
2539 lstrcpyW(szDest, szSrc);
2540 hr = S_OK;
2541 goto end;
2543 /* Get the profile prefix, we'll probably be needing it */
2544 hr = _SHOpenProfilesKey(&key);
2545 if (SUCCEEDED(hr))
2547 WCHAR def_val[MAX_PATH];
2549 /* get the system drive */
2550 GetSystemDirectoryW(def_val, MAX_PATH);
2551 lstrcpyW( def_val + 3, L"users" );
2553 hr = _SHGetProfilesValue(key, L"ProfilesDirectory", szProfilesPrefix, def_val );
2556 *szDest = 0;
2557 lstrcpyW(szTemp, szSrc);
2558 while (SUCCEEDED(hr) && szTemp[0] == '%')
2560 if (!wcsnicmp(szTemp, L"%ALLUSERSPROFILE%", lstrlenW(L"%ALLUSERSPROFILE%")))
2562 WCHAR szAllUsers[MAX_PATH], def_val[MAX_PATH];
2564 GetSystemDirectoryW(def_val, MAX_PATH);
2565 lstrcpyW( def_val + 3, L"users\\Public" );
2567 hr = _SHGetProfilesValue(key, L"Public", szAllUsers, def_val);
2568 PathAppendW(szDest, szAllUsers);
2569 PathAppendW(szDest, szTemp + lstrlenW(L"%ALLUSERSPROFILE%"));
2571 else if (!wcsnicmp(szTemp, L"%PUBLIC%", lstrlenW(L"%PUBLIC%")))
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"%PUBLIC%"));
2582 else if (!wcsnicmp(szTemp, L"%ProgramData%", lstrlenW(L"%ProgramData%")))
2584 WCHAR szProgramData[MAX_PATH], def_val[MAX_PATH];
2585 HKEY shellFolderKey;
2586 DWORD dwType, dwPathLen = sizeof(def_val);
2587 BOOL in_registry = FALSE;
2589 if (!RegCreateKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", &shellFolderKey))
2591 if (!RegQueryValueExW(shellFolderKey, L"Common AppData", NULL, &dwType,
2592 (LPBYTE)def_val, &dwPathLen) && (dwType == REG_EXPAND_SZ || dwType == REG_SZ))
2593 in_registry = TRUE;
2595 RegCloseKey(shellFolderKey);
2598 if (!in_registry)
2600 GetSystemDirectoryW(def_val, MAX_PATH);
2601 lstrcpyW( def_val + 3, L"ProgramData" );
2604 hr = _SHGetProfilesValue(key, L"ProgramData", szProgramData, def_val);
2605 PathAppendW(szDest, szProgramData);
2606 PathAppendW(szDest, szTemp + lstrlenW(L"%ProgramData%"));
2608 else if (!wcsnicmp(szTemp, L"%USERPROFILE%", lstrlenW(L"%USERPROFILE%")))
2610 WCHAR userName[MAX_PATH];
2611 DWORD userLen = MAX_PATH;
2613 lstrcpyW(szDest, szProfilesPrefix);
2614 GetUserNameW(userName, &userLen);
2615 PathAppendW(szDest, userName);
2616 PathAppendW(szDest, szTemp + lstrlenW(L"%USERPROFILE%"));
2618 else if (!wcsnicmp(szTemp, L"%SystemDrive%", lstrlenW(L"%SystemDrive%")))
2620 GetSystemDirectoryW(szDest, MAX_PATH);
2621 lstrcpyW(szDest + 3, szTemp + lstrlenW(L"%SystemDrive%") + 1);
2623 else
2625 DWORD ret = ExpandEnvironmentStringsW(szTemp, szDest, MAX_PATH);
2627 if (ret > MAX_PATH)
2628 hr = E_NOT_SUFFICIENT_BUFFER;
2629 else if (ret == 0)
2630 hr = HRESULT_FROM_WIN32(GetLastError());
2631 else if (!wcscmp( szTemp, szDest )) break; /* nothing expanded */
2633 if (SUCCEEDED(hr)) lstrcpyW(szTemp, szDest);
2635 end:
2636 if (key)
2637 RegCloseKey(key);
2638 TRACE("returning 0x%08x (input was %s, output is %s)\n", hr,
2639 debugstr_w(szSrc), debugstr_w(szDest));
2640 return hr;
2643 static char *xdg_config;
2644 static DWORD xdg_config_len;
2646 static BOOL WINAPI init_xdg_dirs( INIT_ONCE *once, void *param, void **context )
2648 const WCHAR *var, *fmt = L"\\??\\unix%s/user-dirs.dirs";
2649 char *p;
2650 WCHAR *name, *ptr;
2651 HANDLE file;
2652 DWORD len;
2654 if (!(var = _wgetenv( L"XDG_CONFIG_HOME" )) || var[0] != '/')
2656 if (!(var = _wgetenv( L"WINEHOMEDIR" ))) return TRUE;
2657 fmt = L"%s/.config/user-dirs.dirs";
2659 len = lstrlenW(var) + lstrlenW(fmt);
2660 name = heap_alloc( len * sizeof(WCHAR) );
2661 swprintf( name, len, fmt, var );
2662 name[1] = '\\'; /* change \??\ to \\?\ */
2663 for (ptr = name; *ptr; ptr++) if (*ptr == '/') *ptr = '\\';
2665 file = CreateFileW( name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
2666 heap_free( name );
2667 if (file != INVALID_HANDLE_VALUE)
2669 len = GetFileSize( file, NULL );
2670 if (!(xdg_config = heap_alloc( len + 1 ))) return TRUE;
2671 if (!ReadFile( file, xdg_config, len, &xdg_config_len, NULL ))
2673 heap_free( xdg_config );
2674 xdg_config = NULL;
2676 else
2678 for (p = xdg_config; p < xdg_config + xdg_config_len; p++) if (*p == '\n') *p = 0;
2679 *p = 0; /* append null to simplify string parsing */
2681 CloseHandle( file );
2683 return TRUE;
2686 static char *get_xdg_path( const char *var )
2688 static INIT_ONCE once;
2689 char *p, *ret = NULL;
2690 int i;
2692 InitOnceExecuteOnce( &once, init_xdg_dirs, NULL, NULL );
2693 if (!xdg_config) return NULL;
2695 for (p = xdg_config; p < xdg_config + xdg_config_len; p += strlen(p) + 1)
2697 while (*p == ' ' || *p == '\t') p++;
2698 if (strncmp( p, var, strlen(var) )) continue;
2699 p += strlen(var);
2700 while (*p == ' ' || *p == '\t') p++;
2701 if (*p != '=') continue;
2702 p++;
2703 while (*p == ' ' || *p == '\t') p++;
2704 if (*p != '"') continue;
2705 p++;
2706 if (*p != '/' && strncmp( p, "$HOME/", 6 )) continue;
2708 if (!(ret = heap_alloc( strlen(p) + 1 ))) break;
2709 for (i = 0; *p && *p != '"'; i++, p++)
2711 if (*p == '\\' && p[1]) p++;
2712 ret[i] = *p;
2714 ret[i] = 0;
2715 if (*p != '"')
2717 heap_free( ret );
2718 ret = NULL;
2720 break;
2722 return ret;
2725 static BOOL link_folder( HANDLE mgr, const UNICODE_STRING *path, const char *link )
2727 struct mountmgr_shell_folder *ioctl;
2728 DWORD len = sizeof(*ioctl) + path->Length + strlen(link) + 1;
2729 BOOL ret;
2731 if (!(ioctl = heap_alloc( len ))) return FALSE;
2732 ioctl->create_backup = FALSE;
2733 ioctl->folder_offset = sizeof(*ioctl);
2734 ioctl->folder_size = path->Length;
2735 memcpy( (char *)ioctl + ioctl->folder_offset, path->Buffer, ioctl->folder_size );
2736 ioctl->symlink_offset = ioctl->folder_offset + ioctl->folder_size;
2737 strcpy( (char *)ioctl + ioctl->symlink_offset, link );
2739 ret = DeviceIoControl( mgr, IOCTL_MOUNTMGR_DEFINE_SHELL_FOLDER, ioctl, len, NULL, 0, NULL, NULL );
2740 heap_free( ioctl );
2741 return ret;
2744 /******************************************************************************
2745 * create_link
2747 * Sets up a symbolic link for one of the 'My Whatever' shell folders to point
2748 * into the corresponding XDG directory.
2750 static void create_link( const WCHAR *path, const char *xdg_name, const char *default_name )
2752 UNICODE_STRING nt_name;
2753 char *target = NULL;
2754 HANDLE mgr;
2756 if ((mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,
2757 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
2758 0, 0 )) == INVALID_HANDLE_VALUE)
2760 FIXME( "failed to connect to mount manager\n" );
2761 return;
2764 nt_name.Buffer = NULL;
2765 if (!RtlDosPathNameToNtPathName_U( path, &nt_name, NULL, NULL )) goto done;
2767 if ((target = get_xdg_path( xdg_name )))
2769 if (link_folder( mgr, &nt_name, target )) goto done;
2771 if (link_folder( mgr, &nt_name, default_name )) goto done;
2773 /* fall back to HOME */
2774 link_folder( mgr, &nt_name, "$HOME" );
2776 done:
2777 RtlFreeUnicodeString( &nt_name );
2778 heap_free( target );
2779 CloseHandle( mgr );
2782 /******************************************************************************
2783 * _SHCreateSymbolicLink [Internal]
2785 * Sets up a symbolic link for one of the special shell folders to point into
2786 * the users home directory.
2788 * PARAMS
2789 * nFolder [I] CSIDL identifying the folder.
2791 static void _SHCreateSymbolicLink(int nFolder, const WCHAR *path)
2793 DWORD folder = nFolder & CSIDL_FOLDER_MASK;
2795 switch (folder) {
2796 case CSIDL_PERSONAL:
2797 create_link( path, "XDG_DOCUMENTS_DIR", "$HOME/Documents" );
2798 break;
2799 case CSIDL_DESKTOPDIRECTORY:
2800 create_link( path, "XDG_DESKTOP_DIR", "$HOME/Desktop" );
2801 break;
2802 case CSIDL_MYPICTURES:
2803 create_link( path, "XDG_PICTURES_DIR", "$HOME/Pictures" );
2804 break;
2805 case CSIDL_MYVIDEO:
2806 create_link( path, "XDG_VIDEOS_DIR", "$HOME/Movies" );
2807 break;
2808 case CSIDL_MYMUSIC:
2809 create_link( path, "XDG_MUSIC_DIR", "$HOME/Music" );
2810 break;
2811 case CSIDL_DOWNLOADS:
2812 create_link( path, "XDG_DOWNLOAD_DIR", "$HOME/Downloads" );
2813 break;
2814 case CSIDL_TEMPLATES:
2815 create_link( path, "XDG_TEMPLATES_DIR", "$HOME/Templates" );
2816 break;
2820 /******************************************************************************
2821 * SHGetFolderPathW [SHELL32.@]
2823 * Convert nFolder to path.
2825 * RETURNS
2826 * Success: S_OK
2827 * Failure: standard HRESULT error codes.
2829 * NOTES
2830 * Most values can be overridden in either
2831 * HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
2832 * or in the same location in HKLM.
2833 * The "Shell Folders" registry key was used in NT4 and earlier systems.
2834 * Beginning with Windows 2000, the "User Shell Folders" key is used, so
2835 * changes made to it are made to the former key too. This synchronization is
2836 * done on-demand: not until someone requests the value of one of these paths
2837 * (by calling one of the SHGet functions) is the value synchronized.
2838 * Furthermore, the HKCU paths take precedence over the HKLM paths.
2840 HRESULT WINAPI SHGetFolderPathW(
2841 HWND hwndOwner, /* [I] owner window */
2842 int nFolder, /* [I] CSIDL identifying the folder */
2843 HANDLE hToken, /* [I] access token */
2844 DWORD dwFlags, /* [I] which path to return */
2845 LPWSTR pszPath) /* [O] converted path */
2847 HRESULT hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, NULL, pszPath);
2848 if(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
2849 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
2850 return hr;
2853 HRESULT WINAPI SHGetFolderPathAndSubDirA(
2854 HWND hwndOwner, /* [I] owner window */
2855 int nFolder, /* [I] CSIDL identifying the folder */
2856 HANDLE hToken, /* [I] access token */
2857 DWORD dwFlags, /* [I] which path to return */
2858 LPCSTR pszSubPath, /* [I] sub directory of the specified folder */
2859 LPSTR pszPath) /* [O] converted path */
2861 int length;
2862 HRESULT hr = S_OK;
2863 LPWSTR pszSubPathW = NULL;
2864 LPWSTR pszPathW = NULL;
2866 TRACE("%p,%#x,%p,%#x,%s,%p\n", hwndOwner, nFolder, hToken, dwFlags, debugstr_a(pszSubPath), pszPath);
2868 if(pszPath) {
2869 pszPathW = heap_alloc(MAX_PATH * sizeof(WCHAR));
2870 if(!pszPathW) {
2871 hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
2872 goto cleanup;
2875 TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
2877 /* SHGetFolderPathAndSubDirW does not distinguish if pszSubPath isn't
2878 * set (null), or an empty string.therefore call it without the parameter set
2879 * if pszSubPath is an empty string
2881 if (pszSubPath && pszSubPath[0]) {
2882 length = MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, NULL, 0);
2883 pszSubPathW = heap_alloc(length * sizeof(WCHAR));
2884 if(!pszSubPathW) {
2885 hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
2886 goto cleanup;
2888 MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, pszSubPathW, length);
2891 hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, pszSubPathW, pszPathW);
2893 if (SUCCEEDED(hr) && pszPath)
2894 WideCharToMultiByte(CP_ACP, 0, pszPathW, -1, pszPath, MAX_PATH, NULL, NULL);
2896 cleanup:
2897 heap_free(pszPathW);
2898 heap_free(pszSubPathW);
2899 return hr;
2902 /*************************************************************************
2903 * SHGetFolderPathAndSubDirW [SHELL32.@]
2905 HRESULT WINAPI SHGetFolderPathAndSubDirW(
2906 HWND hwndOwner, /* [I] owner window */
2907 int nFolder, /* [I] CSIDL identifying the folder */
2908 HANDLE hToken, /* [I] access token */
2909 DWORD dwFlags, /* [I] which path to return */
2910 LPCWSTR pszSubPath,/* [I] sub directory of the specified folder */
2911 LPWSTR pszPath) /* [O] converted path */
2913 HRESULT hr;
2914 WCHAR szBuildPath[MAX_PATH], szTemp[MAX_PATH];
2915 DWORD folder = nFolder & CSIDL_FOLDER_MASK;
2916 CSIDL_Type type;
2917 int ret;
2919 TRACE("%p,%#x,%p,%#x,%s,%p\n", hwndOwner, nFolder, hToken, dwFlags, debugstr_w(pszSubPath), pszPath);
2921 /* Windows always NULL-terminates the resulting path regardless of success
2922 * or failure, so do so first
2924 if (pszPath)
2925 *pszPath = '\0';
2927 if (folder >= ARRAY_SIZE(CSIDL_Data))
2928 return E_INVALIDARG;
2929 if ((SHGFP_TYPE_CURRENT != dwFlags) && (SHGFP_TYPE_DEFAULT != dwFlags))
2930 return E_INVALIDARG;
2931 szTemp[0] = 0;
2932 type = CSIDL_Data[folder].type;
2933 switch (type)
2935 case CSIDL_Type_Disallowed:
2936 hr = E_INVALIDARG;
2937 break;
2938 case CSIDL_Type_NonExistent:
2939 hr = S_FALSE;
2940 break;
2941 case CSIDL_Type_WindowsPath:
2942 GetWindowsDirectoryW(szTemp, MAX_PATH);
2943 append_relative_path(folder, szTemp);
2944 hr = S_OK;
2945 break;
2946 case CSIDL_Type_SystemPath:
2947 GetSystemDirectoryW(szTemp, MAX_PATH);
2948 append_relative_path(folder, szTemp);
2949 hr = S_OK;
2950 break;
2951 case CSIDL_Type_SystemX86Path:
2952 if (!GetSystemWow64DirectoryW(szTemp, MAX_PATH)) GetSystemDirectoryW(szTemp, MAX_PATH);
2953 append_relative_path(folder, szTemp);
2954 hr = S_OK;
2955 break;
2956 case CSIDL_Type_CurrVer:
2957 hr = _SHGetCurrentVersionPath(dwFlags, folder, szTemp);
2958 break;
2959 case CSIDL_Type_User:
2960 hr = _SHGetUserProfilePath(hToken, dwFlags, folder, szTemp);
2961 break;
2962 case CSIDL_Type_AllUsers:
2963 case CSIDL_Type_ProgramData:
2964 hr = _SHGetAllUsersProfilePath(dwFlags, folder, szTemp);
2965 break;
2966 default:
2967 FIXME("bogus type %d, please fix\n", type);
2968 hr = E_INVALIDARG;
2969 break;
2972 /* Expand environment strings if necessary */
2973 if (*szTemp == '%')
2974 hr = _SHExpandEnvironmentStrings(szTemp, szBuildPath);
2975 else
2976 lstrcpyW(szBuildPath, szTemp);
2978 if (FAILED(hr)) goto end;
2980 if(pszSubPath) {
2981 /* make sure the new path does not exceed the buffer length
2982 * and remember to backslash and terminate it */
2983 if(MAX_PATH < (lstrlenW(szBuildPath) + lstrlenW(pszSubPath) + 2)) {
2984 hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
2985 goto end;
2987 PathAppendW(szBuildPath, pszSubPath);
2988 PathRemoveBackslashW(szBuildPath);
2990 /* Copy the path if it's available before we might return */
2991 if (SUCCEEDED(hr) && pszPath)
2992 lstrcpyW(pszPath, szBuildPath);
2994 /* if we don't care about existing directories we are ready */
2995 if(nFolder & CSIDL_FLAG_DONT_VERIFY) goto end;
2997 if (PathFileExistsW(szBuildPath)) goto end;
2999 /* not existing but we are not allowed to create it. The return value
3000 * is verified against shell32 version 6.0.
3002 if (!(nFolder & CSIDL_FLAG_CREATE))
3004 hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
3005 goto end;
3008 /* create symbolic links rather than directories for specific
3009 * user shell folders */
3010 _SHCreateSymbolicLink(folder, szBuildPath);
3012 /* create directory/directories */
3013 ret = SHCreateDirectoryExW(hwndOwner, szBuildPath, NULL);
3014 if (ret && ret != ERROR_ALREADY_EXISTS)
3016 ERR("Failed to create directory %s.\n", debugstr_w(szBuildPath));
3017 hr = E_FAIL;
3018 goto end;
3021 TRACE("Created missing system directory %s\n", debugstr_w(szBuildPath));
3022 end:
3023 TRACE("returning 0x%08x (final path is %s)\n", hr, debugstr_w(szBuildPath));
3024 return hr;
3027 /*************************************************************************
3028 * SHGetFolderPathA [SHELL32.@]
3030 * See SHGetFolderPathW.
3032 HRESULT WINAPI SHGetFolderPathA(
3033 HWND hwndOwner,
3034 int nFolder,
3035 HANDLE hToken,
3036 DWORD dwFlags,
3037 LPSTR pszPath)
3039 WCHAR szTemp[MAX_PATH];
3040 HRESULT hr;
3042 TRACE("%p,%d,%p,%#x,%p\n", hwndOwner, nFolder, hToken, dwFlags, pszPath);
3044 if (pszPath)
3045 *pszPath = '\0';
3046 hr = SHGetFolderPathW(hwndOwner, nFolder, hToken, dwFlags, szTemp);
3047 if (SUCCEEDED(hr) && pszPath)
3048 WideCharToMultiByte(CP_ACP, 0, szTemp, -1, pszPath, MAX_PATH, NULL,
3049 NULL);
3051 return hr;
3054 /* For each folder in folders, if its value has not been set in the registry,
3055 * calls _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the
3056 * folder's type) to get the unexpanded value first.
3057 * Writes the unexpanded value to User Shell Folders, and queries it with
3058 * SHGetFolderPathW to force the creation of the directory if it doesn't
3059 * already exist. SHGetFolderPathW also returns the expanded value, which
3060 * this then writes to Shell Folders.
3062 static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken,
3063 LPCWSTR szUserShellFolderPath, LPCWSTR szShellFolderPath, const UINT folders[],
3064 UINT foldersLen)
3066 const WCHAR *szValueName;
3067 WCHAR buffer[40];
3068 UINT i;
3069 WCHAR path[MAX_PATH];
3070 HRESULT hr = S_OK;
3071 HKEY hUserKey = NULL, hKey = NULL;
3072 DWORD dwType, dwPathLen;
3073 LONG ret;
3075 TRACE("%p,%p,%s,%p,%u\n", hRootKey, hToken,
3076 debugstr_w(szUserShellFolderPath), folders, foldersLen);
3078 ret = RegCreateKeyW(hRootKey, szUserShellFolderPath, &hUserKey);
3079 if (ret)
3080 hr = HRESULT_FROM_WIN32(ret);
3081 else
3083 ret = RegCreateKeyW(hRootKey, szShellFolderPath, &hKey);
3084 if (ret)
3085 hr = HRESULT_FROM_WIN32(ret);
3087 for (i = 0; SUCCEEDED(hr) && i < foldersLen; i++)
3089 dwPathLen = MAX_PATH * sizeof(WCHAR);
3091 /* For CSIDL_Type_User we also use the GUID if no szValueName is provided */
3092 szValueName = CSIDL_Data[folders[i]].value;
3093 if (!szValueName && CSIDL_Data[folders[i]].type == CSIDL_Type_User)
3095 StringFromGUID2( CSIDL_Data[folders[i]].id, buffer, 39 );
3096 szValueName = &buffer[0];
3099 if (RegQueryValueExW(hUserKey, szValueName, NULL,
3100 &dwType, (LPBYTE)path, &dwPathLen) || (dwType != REG_SZ &&
3101 dwType != REG_EXPAND_SZ))
3103 *path = '\0';
3104 if (CSIDL_Data[folders[i]].type == CSIDL_Type_User)
3105 _SHGetUserProfilePath(hToken, SHGFP_TYPE_DEFAULT, folders[i],
3106 path);
3107 else if (CSIDL_Data[folders[i]].type == CSIDL_Type_AllUsers ||
3108 CSIDL_Data[folders[i]].type == CSIDL_Type_ProgramData)
3109 _SHGetAllUsersProfilePath(SHGFP_TYPE_DEFAULT, folders[i], path);
3110 else if (CSIDL_Data[folders[i]].type == CSIDL_Type_WindowsPath)
3112 GetWindowsDirectoryW(path, MAX_PATH);
3113 append_relative_path(folders[i], path);
3115 else
3116 hr = E_FAIL;
3117 if (*path)
3119 ret = RegSetValueExW(hUserKey, szValueName, 0, REG_EXPAND_SZ,
3120 (LPBYTE)path, (lstrlenW(path) + 1) * sizeof(WCHAR));
3121 if (ret)
3122 hr = HRESULT_FROM_WIN32(ret);
3123 else
3125 hr = SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
3126 hToken, SHGFP_TYPE_DEFAULT, path);
3127 ret = RegSetValueExW(hKey, szValueName, 0, REG_SZ,
3128 (LPBYTE)path, (lstrlenW(path) + 1) * sizeof(WCHAR));
3129 if (ret)
3130 hr = HRESULT_FROM_WIN32(ret);
3134 else
3136 /* create the default dir, which may be different from the path
3137 * stored in the registry. */
3138 SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
3139 hToken, SHGFP_TYPE_DEFAULT, path);
3142 if (hUserKey)
3143 RegCloseKey(hUserKey);
3144 if (hKey)
3145 RegCloseKey(hKey);
3147 TRACE("returning 0x%08x\n", hr);
3148 return hr;
3151 static HRESULT _SHRegisterUserShellFolders(BOOL bDefault)
3153 static const UINT folders[] = {
3154 CSIDL_PROGRAMS,
3155 CSIDL_PERSONAL,
3156 CSIDL_FAVORITES,
3157 CSIDL_APPDATA,
3158 CSIDL_STARTUP,
3159 CSIDL_RECENT,
3160 CSIDL_SENDTO,
3161 CSIDL_STARTMENU,
3162 CSIDL_MYMUSIC,
3163 CSIDL_MYVIDEO,
3164 CSIDL_DESKTOPDIRECTORY,
3165 CSIDL_NETHOOD,
3166 CSIDL_TEMPLATES,
3167 CSIDL_PRINTHOOD,
3168 CSIDL_LOCAL_APPDATA,
3169 CSIDL_INTERNET_CACHE,
3170 CSIDL_COOKIES,
3171 CSIDL_HISTORY,
3172 CSIDL_MYPICTURES,
3173 CSIDL_FONTS,
3174 CSIDL_ADMINTOOLS,
3175 CSIDL_CONTACTS,
3176 CSIDL_DOWNLOADS,
3177 CSIDL_LINKS,
3178 CSIDL_APPDATA_LOCALLOW,
3179 CSIDL_SAVED_GAMES,
3180 CSIDL_SEARCHES
3182 WCHAR userShellFolderPath[MAX_PATH], shellFolderPath[MAX_PATH];
3183 LPCWSTR pUserShellFolderPath, pShellFolderPath;
3184 HRESULT hr = S_OK;
3185 HKEY hRootKey;
3186 HANDLE hToken;
3188 TRACE("%s\n", bDefault ? "TRUE" : "FALSE");
3189 if (bDefault)
3191 hToken = (HANDLE)-1;
3192 hRootKey = HKEY_USERS;
3193 lstrcpyW(userShellFolderPath, L".Default");
3194 PathAddBackslashW(userShellFolderPath);
3195 lstrcatW(userShellFolderPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders");
3196 pUserShellFolderPath = userShellFolderPath;
3197 lstrcpyW(shellFolderPath, L".Default");
3198 PathAddBackslashW(shellFolderPath);
3199 lstrcatW(shellFolderPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
3200 pShellFolderPath = shellFolderPath;
3202 else
3204 hToken = NULL;
3205 hRootKey = HKEY_CURRENT_USER;
3206 pUserShellFolderPath = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders";
3207 pShellFolderPath = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
3210 hr = _SHRegisterFolders(hRootKey, hToken, pUserShellFolderPath,
3211 pShellFolderPath, folders, ARRAY_SIZE(folders));
3212 TRACE("returning 0x%08x\n", hr);
3213 return hr;
3216 static HRESULT _SHRegisterCommonShellFolders(void)
3218 static const UINT folders[] = {
3219 CSIDL_COMMON_STARTMENU,
3220 CSIDL_COMMON_PROGRAMS,
3221 CSIDL_COMMON_STARTUP,
3222 CSIDL_COMMON_DESKTOPDIRECTORY,
3223 CSIDL_COMMON_FAVORITES,
3224 CSIDL_COMMON_APPDATA,
3225 CSIDL_COMMON_TEMPLATES,
3226 CSIDL_COMMON_DOCUMENTS,
3227 CSIDL_COMMON_ADMINTOOLS,
3228 CSIDL_COMMON_MUSIC,
3229 CSIDL_COMMON_PICTURES,
3230 CSIDL_COMMON_VIDEO,
3232 HRESULT hr;
3234 TRACE("\n");
3235 hr = _SHRegisterFolders(HKEY_LOCAL_MACHINE, NULL,
3236 L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders",
3237 L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
3238 folders, ARRAY_SIZE(folders));
3239 TRACE("returning 0x%08x\n", hr);
3240 return hr;
3243 /******************************************************************************
3244 * create_extra_folders [Internal]
3246 * Create some extra folders that don't have a standard CSIDL definition.
3248 static HRESULT create_extra_folders(void)
3250 WCHAR path[MAX_PATH+5];
3251 HRESULT hr;
3252 HKEY hkey;
3253 DWORD type, size, ret;
3255 ret = RegCreateKeyW( HKEY_CURRENT_USER, L"Environment", &hkey );
3256 if (ret) return HRESULT_FROM_WIN32( ret );
3258 /* FIXME: should be under AppData, but we don't want spaces in the temp path */
3259 hr = SHGetFolderPathAndSubDirW( 0, CSIDL_PROFILE | CSIDL_FLAG_CREATE, NULL,
3260 SHGFP_TYPE_DEFAULT, L"Temp", path );
3261 if (SUCCEEDED(hr))
3263 size = sizeof(path);
3264 if (RegQueryValueExW( hkey, L"TEMP", NULL, &type, (LPBYTE)path, &size ))
3265 RegSetValueExW( hkey, L"TEMP", 0, REG_SZ, (LPBYTE)path, (lstrlenW(path) + 1) * sizeof(WCHAR) );
3266 size = sizeof(path);
3267 if (RegQueryValueExW( hkey, L"TMP", NULL, &type, (LPBYTE)path, &size ))
3268 RegSetValueExW( hkey, L"TMP", 0, REG_SZ, (LPBYTE)path, (lstrlenW(path) + 1) * sizeof(WCHAR) );
3270 RegCloseKey( hkey );
3272 if (SUCCEEDED(hr))
3274 hr = SHGetFolderPathAndSubDirW( 0, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, NULL,
3275 SHGFP_TYPE_DEFAULT, L"Microsoft", path );
3277 if (SUCCEEDED(hr))
3279 hr = SHGetFolderPathAndSubDirW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL,
3280 SHGFP_TYPE_DEFAULT, L"Microsoft\\Windows\\Themes", path);
3282 return hr;
3286 /******************************************************************************
3287 * set_folder_attributes
3289 * Set the various folder attributes registry keys.
3291 static HRESULT set_folder_attributes(void)
3293 static const struct
3295 const CLSID *clsid;
3296 BOOL wfparsing : 1;
3297 BOOL wfdisplay : 1;
3298 BOOL hideasdel : 1;
3299 DWORD attr;
3300 DWORD call_for_attr;
3301 } folders[] =
3303 { &CLSID_UnixFolder, TRUE, FALSE, FALSE },
3304 { &CLSID_UnixDosFolder, TRUE, FALSE, FALSE,
3305 SFGAO_FILESYSANCESTOR|SFGAO_FOLDER|SFGAO_HASSUBFOLDER, SFGAO_FILESYSTEM },
3306 { &CLSID_FolderShortcut, FALSE, FALSE, FALSE,
3307 SFGAO_FILESYSTEM|SFGAO_FOLDER|SFGAO_LINK,
3308 SFGAO_HASSUBFOLDER|SFGAO_FILESYSTEM|SFGAO_FOLDER|SFGAO_FILESYSANCESTOR },
3309 { &CLSID_MyDocuments, TRUE, FALSE, FALSE,
3310 SFGAO_FILESYSANCESTOR|SFGAO_FOLDER|SFGAO_HASSUBFOLDER, SFGAO_FILESYSTEM },
3311 { &CLSID_RecycleBin, FALSE, FALSE, FALSE,
3312 SFGAO_FOLDER|SFGAO_DROPTARGET|SFGAO_HASPROPSHEET },
3313 { &CLSID_ControlPanel, FALSE, TRUE, TRUE,
3314 SFGAO_FOLDER|SFGAO_HASSUBFOLDER }
3317 unsigned int i;
3318 WCHAR buffer[39 + ARRAY_SIZE(L"CLSID\\") + ARRAY_SIZE(L"\\ShellFolder")];
3319 LONG res;
3320 HKEY hkey;
3322 for (i = 0; i < ARRAY_SIZE(folders); i++)
3324 lstrcpyW( buffer, L"CLSID\\" );
3325 StringFromGUID2( folders[i].clsid, buffer + lstrlenW(buffer), 39 );
3326 lstrcatW( buffer, L"\\ShellFolder" );
3327 res = RegCreateKeyExW( HKEY_CLASSES_ROOT, buffer, 0, NULL, 0,
3328 KEY_READ | KEY_WRITE, NULL, &hkey, NULL);
3329 if (res) return HRESULT_FROM_WIN32( res );
3330 if (folders[i].wfparsing)
3331 res = RegSetValueExW( hkey, L"WantsFORPARSING", 0, REG_SZ, (const BYTE *)L"", sizeof(WCHAR) );
3332 if (folders[i].wfdisplay)
3333 res = RegSetValueExW( hkey, L"WantsFORDISPLAY", 0, REG_SZ, (const BYTE *)L"", sizeof(WCHAR) );
3334 if (folders[i].hideasdel)
3335 res = RegSetValueExW( hkey, L"HideAsDeletePerUser", 0, REG_SZ, (const BYTE *)L"", sizeof(WCHAR) );
3336 if (folders[i].attr)
3337 res = RegSetValueExW( hkey, L"Attributes", 0, REG_DWORD,
3338 (const BYTE *)&folders[i].attr, sizeof(DWORD));
3339 if (folders[i].call_for_attr)
3340 res = RegSetValueExW( hkey, L"CallForAttributes", 0, REG_DWORD,
3341 (const BYTE *)&folders[i].call_for_attr, sizeof(DWORD));
3342 RegCloseKey( hkey );
3344 return S_OK;
3347 /*************************************************************************
3348 * SHGetSpecialFolderPathA [SHELL32.@]
3350 BOOL WINAPI SHGetSpecialFolderPathA (
3351 HWND hwndOwner,
3352 LPSTR szPath,
3353 int nFolder,
3354 BOOL bCreate)
3356 return SHGetFolderPathA(hwndOwner, nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0), NULL, 0,
3357 szPath) == S_OK;
3360 /*************************************************************************
3361 * SHGetSpecialFolderPathW
3363 BOOL WINAPI SHGetSpecialFolderPathW (
3364 HWND hwndOwner,
3365 LPWSTR szPath,
3366 int nFolder,
3367 BOOL bCreate)
3369 return SHGetFolderPathW(hwndOwner, nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0), NULL, 0,
3370 szPath) == S_OK;
3373 /*************************************************************************
3374 * SHGetSpecialFolderPath (SHELL32.175)
3376 BOOL WINAPI SHGetSpecialFolderPathAW (
3377 HWND hwndOwner,
3378 LPVOID szPath,
3379 int nFolder,
3380 BOOL bCreate)
3383 if (SHELL_OsIsUnicode())
3384 return SHGetSpecialFolderPathW (hwndOwner, szPath, nFolder, bCreate);
3385 return SHGetSpecialFolderPathA (hwndOwner, szPath, nFolder, bCreate);
3388 /*************************************************************************
3389 * SHGetFolderLocation [SHELL32.@]
3391 * Gets the folder locations from the registry and creates a pidl.
3393 * PARAMS
3394 * hwndOwner [I]
3395 * nFolder [I] CSIDL_xxxxx
3396 * hToken [I] token representing user, or NULL for current user, or -1 for
3397 * default user
3398 * dwReserved [I] must be zero
3399 * ppidl [O] PIDL of a special folder
3401 * RETURNS
3402 * Success: S_OK
3403 * Failure: Standard OLE-defined error result, S_FALSE or E_INVALIDARG
3405 * NOTES
3406 * Creates missing reg keys and directories.
3407 * Mostly forwards to SHGetFolderPathW, but a few values of nFolder return
3408 * virtual folders that are handled here.
3410 HRESULT WINAPI SHGetFolderLocation(
3411 HWND hwndOwner,
3412 int nFolder,
3413 HANDLE hToken,
3414 DWORD dwReserved,
3415 LPITEMIDLIST *ppidl)
3417 HRESULT hr = E_INVALIDARG;
3419 TRACE("%p 0x%08x %p 0x%08x %p\n",
3420 hwndOwner, nFolder, hToken, dwReserved, ppidl);
3422 if (!ppidl)
3423 return E_INVALIDARG;
3424 if (dwReserved)
3425 return E_INVALIDARG;
3427 /* The virtual folders' locations are not user-dependent */
3428 *ppidl = NULL;
3429 switch (nFolder & CSIDL_FOLDER_MASK)
3431 case CSIDL_DESKTOP:
3432 *ppidl = _ILCreateDesktop();
3433 break;
3435 case CSIDL_PERSONAL:
3436 *ppidl = _ILCreateMyDocuments();
3437 break;
3439 case CSIDL_INTERNET:
3440 *ppidl = _ILCreateIExplore();
3441 break;
3443 case CSIDL_CONTROLS:
3444 *ppidl = _ILCreateControlPanel();
3445 break;
3447 case CSIDL_PRINTERS:
3448 *ppidl = _ILCreatePrinters();
3449 break;
3451 case CSIDL_BITBUCKET:
3452 *ppidl = _ILCreateBitBucket();
3453 break;
3455 case CSIDL_DRIVES:
3456 *ppidl = _ILCreateMyComputer();
3457 break;
3459 case CSIDL_NETWORK:
3460 *ppidl = _ILCreateNetwork();
3461 break;
3463 default:
3465 WCHAR szPath[MAX_PATH];
3467 hr = SHGetFolderPathW(hwndOwner, nFolder, hToken,
3468 SHGFP_TYPE_CURRENT, szPath);
3469 if (SUCCEEDED(hr))
3471 DWORD attributes=0;
3473 TRACE("Value=%s\n", debugstr_w(szPath));
3474 hr = SHILCreateFromPathW(szPath, ppidl, &attributes);
3476 else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
3478 /* unlike SHGetFolderPath, SHGetFolderLocation in shell32
3479 * version 6.0 returns E_FAIL for nonexistent paths
3481 hr = E_FAIL;
3485 if(*ppidl)
3486 hr = S_OK;
3488 TRACE("-- (new pidl %p)\n",*ppidl);
3489 return hr;
3492 /*************************************************************************
3493 * SHGetSpecialFolderLocation [SHELL32.@]
3495 * NOTES
3496 * In NT5, SHGetSpecialFolderLocation needs the <winntdir>/Recent
3497 * directory.
3499 HRESULT WINAPI SHGetSpecialFolderLocation(
3500 HWND hwndOwner,
3501 INT nFolder,
3502 LPITEMIDLIST * ppidl)
3504 HRESULT hr = E_INVALIDARG;
3506 TRACE("(%p,0x%x,%p)\n", hwndOwner,nFolder,ppidl);
3508 if (!ppidl)
3509 return E_INVALIDARG;
3511 hr = SHGetFolderLocation(hwndOwner, nFolder, NULL, 0, ppidl);
3512 return hr;
3515 /*************************************************************************
3516 * SHGetKnownFolderPath [SHELL32.@]
3518 HRESULT WINAPI SHGetKnownFolderPath(REFKNOWNFOLDERID rfid, DWORD flags, HANDLE token, WCHAR **ret_path)
3520 WCHAR pathW[MAX_PATH];
3521 HRESULT hr;
3522 int folder = csidl_from_id(rfid), shgfp_flags;
3524 TRACE("%s, 0x%08x, %p, %p\n", debugstr_guid(rfid), flags, token, ret_path);
3526 *ret_path = NULL;
3528 if (folder < 0)
3529 return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
3531 if (flags & ~(KF_FLAG_CREATE|KF_FLAG_SIMPLE_IDLIST|KF_FLAG_DONT_UNEXPAND|
3532 KF_FLAG_DONT_VERIFY|KF_FLAG_NO_ALIAS|KF_FLAG_INIT|KF_FLAG_DEFAULT_PATH))
3534 FIXME("flags 0x%08x not supported\n", flags);
3535 return E_INVALIDARG;
3537 folder |= flags & CSIDL_FLAG_MASK;
3538 shgfp_flags = flags & KF_FLAG_DEFAULT_PATH ? SHGFP_TYPE_DEFAULT : SHGFP_TYPE_CURRENT;
3540 hr = SHGetFolderPathAndSubDirW( 0, folder, token, shgfp_flags, NULL, pathW );
3541 if (FAILED( hr ))
3543 TRACE("Failed to get folder path, %#x.\n", hr);
3544 return hr;
3547 TRACE("Final path is %s, %#x\n", debugstr_w(pathW), hr);
3549 *ret_path = CoTaskMemAlloc((lstrlenW(pathW) + 1) * sizeof(WCHAR));
3550 if (!*ret_path)
3551 return E_OUTOFMEMORY;
3552 lstrcpyW(*ret_path, pathW);
3554 return hr;
3557 /*************************************************************************
3558 * SHGetFolderPathEx [SHELL32.@]
3560 HRESULT WINAPI SHGetFolderPathEx(REFKNOWNFOLDERID rfid, DWORD flags, HANDLE token, LPWSTR path, DWORD len)
3562 HRESULT hr;
3563 WCHAR *buffer;
3565 TRACE("%s, 0x%08x, %p, %p, %u\n", debugstr_guid(rfid), flags, token, path, len);
3567 if (!path || !len) return E_INVALIDARG;
3569 hr = SHGetKnownFolderPath( rfid, flags, token, &buffer );
3570 if (SUCCEEDED( hr ))
3572 if (lstrlenW( buffer ) + 1 > len)
3574 CoTaskMemFree( buffer );
3575 return HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER );
3577 lstrcpyW( path, buffer );
3578 CoTaskMemFree( buffer );
3580 return hr;
3584 * Internal function to convert known folder identifier to path of registry key
3585 * associated with known folder.
3587 * Parameters:
3588 * rfid [I] pointer to known folder identifier (may be NULL)
3589 * lpStringGuid [I] string with known folder identifier (used when rfid is NULL)
3590 * lpPath [O] place to store string address. String should be
3591 * later freed using HeapFree(GetProcessHeap(),0, ... )
3593 static HRESULT get_known_folder_registry_path(
3594 REFKNOWNFOLDERID rfid,
3595 LPWSTR lpStringGuid,
3596 LPWSTR *lpPath)
3598 HRESULT hr = S_OK;
3599 int length;
3600 WCHAR sGuid[50];
3602 TRACE("(%s, %s, %p)\n", debugstr_guid(rfid), debugstr_w(lpStringGuid), lpPath);
3604 if(rfid)
3605 StringFromGUID2(rfid, sGuid, ARRAY_SIZE(sGuid));
3606 else
3607 lstrcpyW(sGuid, lpStringGuid);
3609 length = lstrlenW(L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FolderDescriptions")+51;
3610 *lpPath = heap_alloc(length*sizeof(WCHAR));
3611 if(!(*lpPath))
3612 hr = E_OUTOFMEMORY;
3614 if(SUCCEEDED(hr))
3616 lstrcpyW(*lpPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FolderDescriptions\\");
3617 lstrcatW(*lpPath, sGuid);
3620 return hr;
3623 static HRESULT get_known_folder_wstr(const WCHAR *regpath, const WCHAR *value, WCHAR **out)
3625 DWORD size = 0;
3626 HRESULT hr;
3628 size = 0;
3629 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, regpath, value, RRF_RT_REG_SZ, NULL, NULL, &size));
3630 if(FAILED(hr))
3631 return hr;
3633 *out = CoTaskMemAlloc(size);
3634 if(!*out)
3635 return E_OUTOFMEMORY;
3637 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, regpath, value, RRF_RT_REG_SZ, NULL, *out, &size));
3638 if(FAILED(hr)){
3639 CoTaskMemFree(*out);
3640 *out = NULL;
3643 return hr;
3646 static HRESULT get_known_folder_dword(const WCHAR *registryPath, const WCHAR *value, DWORD *out)
3648 DWORD dwSize = sizeof(DWORD);
3649 DWORD dwType;
3650 return HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, registryPath, value, RRF_RT_DWORD, &dwType, out, &dwSize));
3654 * Internal function to get place where folder redirection information are stored.
3656 * Parameters:
3657 * rfid [I] pointer to known folder identifier (may be NULL)
3658 * rootKey [O] root key where the redirection information are stored
3659 * It can be HKLM for COMMON folders, and HKCU for PERUSER folders.
3661 static HRESULT get_known_folder_redirection_place(
3662 REFKNOWNFOLDERID rfid,
3663 HKEY *rootKey)
3665 HRESULT hr;
3666 LPWSTR lpRegistryPath = NULL;
3667 KF_CATEGORY category;
3669 /* first, get known folder's category */
3670 hr = get_known_folder_registry_path(rfid, NULL, &lpRegistryPath);
3672 if(SUCCEEDED(hr))
3673 hr = get_known_folder_dword(lpRegistryPath, L"Category", &category);
3675 if(SUCCEEDED(hr))
3677 if(category == KF_CATEGORY_COMMON)
3679 *rootKey = HKEY_LOCAL_MACHINE;
3680 hr = S_OK;
3682 else if(category == KF_CATEGORY_PERUSER)
3684 *rootKey = HKEY_CURRENT_USER;
3685 hr = S_OK;
3687 else
3688 hr = E_FAIL;
3691 heap_free(lpRegistryPath);
3692 return hr;
3695 static HRESULT get_known_folder_path_by_id(REFKNOWNFOLDERID folderId, LPWSTR lpRegistryPath, DWORD dwFlags, LPWSTR *ppszPath);
3697 static HRESULT redirect_known_folder(
3698 REFKNOWNFOLDERID rfid,
3699 HWND hwnd,
3700 KF_REDIRECT_FLAGS flags,
3701 LPCWSTR pszTargetPath,
3702 UINT cFolders,
3703 KNOWNFOLDERID const *pExclusion,
3704 LPWSTR *ppszError)
3706 HRESULT hr;
3707 HKEY rootKey = HKEY_LOCAL_MACHINE, hKey;
3708 WCHAR sGuid[39];
3709 LPWSTR lpRegistryPath = NULL, lpSrcPath = NULL;
3710 TRACE("(%s, %p, 0x%08x, %s, %d, %p, %p)\n", debugstr_guid(rfid), hwnd, flags, debugstr_w(pszTargetPath), cFolders, pExclusion, ppszError);
3712 if (ppszError) *ppszError = NULL;
3714 hr = get_known_folder_registry_path(rfid, NULL, &lpRegistryPath);
3716 if(SUCCEEDED(hr))
3717 hr = get_known_folder_path_by_id(rfid, lpRegistryPath, 0, &lpSrcPath);
3719 heap_free(lpRegistryPath);
3721 /* get path to redirection storage */
3722 if(SUCCEEDED(hr))
3723 hr = get_known_folder_redirection_place(rfid, &rootKey);
3725 /* write redirection information */
3726 if(SUCCEEDED(hr))
3727 hr = HRESULT_FROM_WIN32(RegCreateKeyExW(rootKey, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL));
3729 if(SUCCEEDED(hr))
3731 StringFromGUID2(rfid, sGuid, ARRAY_SIZE(sGuid));
3733 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sGuid, 0, REG_SZ, (LPBYTE)pszTargetPath, (lstrlenW(pszTargetPath)+1)*sizeof(WCHAR)));
3735 RegCloseKey(hKey);
3738 /* make sure destination path exists */
3739 SHCreateDirectory(NULL, pszTargetPath);
3741 /* copy content if required */
3742 if(SUCCEEDED(hr) && (flags & KF_REDIRECT_COPY_CONTENTS) )
3744 WCHAR srcPath[MAX_PATH+1], dstPath[MAX_PATH+1];
3745 SHFILEOPSTRUCTW fileOp;
3747 ZeroMemory(srcPath, sizeof(srcPath));
3748 lstrcpyW(srcPath, lpSrcPath);
3749 lstrcatW(srcPath, L"\\*");
3751 ZeroMemory(dstPath, sizeof(dstPath));
3752 lstrcpyW(dstPath, pszTargetPath);
3754 ZeroMemory(&fileOp, sizeof(fileOp));
3756 if(flags & KF_REDIRECT_DEL_SOURCE_CONTENTS)
3757 fileOp.wFunc = FO_MOVE;
3758 else
3759 fileOp.wFunc = FO_COPY;
3761 fileOp.pFrom = srcPath;
3762 fileOp.pTo = dstPath;
3763 fileOp.fFlags = FOF_NO_UI;
3765 hr = (SHFileOperationW(&fileOp)==0 ? S_OK : E_FAIL);
3767 if(flags & KF_REDIRECT_DEL_SOURCE_CONTENTS)
3769 ZeroMemory(srcPath, sizeof(srcPath));
3770 lstrcpyW(srcPath, lpSrcPath);
3772 ZeroMemory(&fileOp, sizeof(fileOp));
3773 fileOp.wFunc = FO_DELETE;
3774 fileOp.pFrom = srcPath;
3775 fileOp.fFlags = FOF_NO_UI;
3777 hr = (SHFileOperationW(&fileOp)==0 ? S_OK : E_FAIL);
3781 CoTaskMemFree(lpSrcPath);
3783 return hr;
3787 struct knownfolder
3789 IKnownFolder IKnownFolder_iface;
3790 LONG refs;
3791 KNOWNFOLDERID id;
3792 LPWSTR registryPath;
3795 static inline struct knownfolder *impl_from_IKnownFolder( IKnownFolder *iface )
3797 return CONTAINING_RECORD( iface, struct knownfolder, IKnownFolder_iface );
3800 static ULONG WINAPI knownfolder_AddRef(
3801 IKnownFolder *iface )
3803 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
3804 return InterlockedIncrement( &knownfolder->refs );
3807 static ULONG WINAPI knownfolder_Release(
3808 IKnownFolder *iface )
3810 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
3811 LONG refs = InterlockedDecrement( &knownfolder->refs );
3812 if (!refs)
3814 TRACE("destroying %p\n", knownfolder);
3815 heap_free( knownfolder->registryPath );
3816 heap_free( knownfolder );
3818 return refs;
3821 static HRESULT WINAPI knownfolder_QueryInterface(
3822 IKnownFolder *iface,
3823 REFIID riid,
3824 void **ppv )
3826 struct knownfolder *This = impl_from_IKnownFolder( iface );
3828 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppv );
3830 *ppv = NULL;
3831 if ( IsEqualGUID( riid, &IID_IKnownFolder ) ||
3832 IsEqualGUID( riid, &IID_IUnknown ) )
3834 *ppv = iface;
3836 else if ( IsEqualGUID( riid, &IID_IMarshal ) )
3838 TRACE("IID_IMarshal returning NULL.\n");
3839 return E_NOINTERFACE;
3841 else
3843 FIXME("interface %s not implemented\n", debugstr_guid(riid));
3844 return E_NOINTERFACE;
3846 IKnownFolder_AddRef( iface );
3847 return S_OK;
3850 static HRESULT knownfolder_set_id(
3851 struct knownfolder *knownfolder,
3852 const KNOWNFOLDERID *kfid)
3854 HKEY hKey;
3855 HRESULT hr;
3857 TRACE("%s\n", debugstr_guid(kfid));
3859 knownfolder->id = *kfid;
3861 /* check is it registry-registered folder */
3862 hr = get_known_folder_registry_path(kfid, NULL, &knownfolder->registryPath);
3863 if(SUCCEEDED(hr))
3864 hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, knownfolder->registryPath, 0, 0, &hKey));
3866 if(SUCCEEDED(hr))
3868 hr = S_OK;
3869 RegCloseKey(hKey);
3871 else
3873 /* This known folder is not registered. To mark it, we set registryPath to NULL */
3874 heap_free(knownfolder->registryPath);
3875 knownfolder->registryPath = NULL;
3876 hr = S_OK;
3879 return hr;
3882 static HRESULT WINAPI knownfolder_GetId(
3883 IKnownFolder *iface,
3884 KNOWNFOLDERID *pkfid)
3886 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
3888 TRACE("%p\n", pkfid);
3890 *pkfid = knownfolder->id;
3891 return S_OK;
3894 static HRESULT WINAPI knownfolder_GetCategory(
3895 IKnownFolder *iface,
3896 KF_CATEGORY *pCategory)
3898 struct knownfolder *knownfolder = impl_from_IKnownFolder(iface);
3899 HRESULT hr = S_OK;
3901 TRACE("%p, %p\n", knownfolder, pCategory);
3903 /* we cannot get a category for a folder which is not registered */
3904 if(!knownfolder->registryPath)
3905 hr = E_FAIL;
3907 if(SUCCEEDED(hr))
3908 hr = get_known_folder_dword(knownfolder->registryPath, L"Category", pCategory);
3910 return hr;
3913 static HRESULT WINAPI knownfolder_GetShellItem(
3914 IKnownFolder *iface,
3915 DWORD flags,
3916 REFIID riid,
3917 void **ppv)
3919 struct knownfolder *knownfolder = impl_from_IKnownFolder(iface);
3920 TRACE("(%p, 0x%08x, %s, %p)\n", knownfolder, flags, debugstr_guid(riid), ppv);
3921 return SHGetKnownFolderItem(&knownfolder->id, flags, NULL, riid, ppv);
3924 static HRESULT get_known_folder_path(
3925 LPWSTR sFolderId,
3926 LPWSTR registryPath,
3927 LPWSTR *ppszPath)
3929 HRESULT hr;
3930 DWORD dwSize, dwType;
3931 WCHAR path[MAX_PATH] = {0};
3932 WCHAR parentGuid[39];
3933 KF_CATEGORY category;
3934 LPWSTR parentRegistryPath, parentPath;
3935 HKEY hRedirectionRootKey = NULL;
3937 TRACE("(%s, %p)\n", debugstr_w(registryPath), ppszPath);
3938 *ppszPath = NULL;
3940 /* check if folder has parent */
3941 dwSize = sizeof(parentGuid);
3942 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, registryPath, L"ParentFolder", RRF_RT_REG_SZ, &dwType, parentGuid, &dwSize));
3943 if(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) hr = S_FALSE;
3945 if(hr == S_OK)
3947 /* get parent's known folder path (recursive) */
3948 hr = get_known_folder_registry_path(NULL, parentGuid, &parentRegistryPath);
3949 if(FAILED(hr)) return hr;
3951 hr = get_known_folder_path(parentGuid, parentRegistryPath, &parentPath);
3952 if(FAILED(hr)) {
3953 heap_free(parentRegistryPath);
3954 return hr;
3957 lstrcatW(path, parentPath);
3958 lstrcatW(path, L"\\");
3960 heap_free(parentRegistryPath);
3961 heap_free(parentPath);
3964 /* check, if folder was redirected */
3965 if(SUCCEEDED(hr))
3966 hr = get_known_folder_dword(registryPath, L"Category", &category);
3968 if(SUCCEEDED(hr))
3970 if(category == KF_CATEGORY_COMMON)
3971 hRedirectionRootKey = HKEY_LOCAL_MACHINE;
3972 else if(category == KF_CATEGORY_PERUSER)
3973 hRedirectionRootKey = HKEY_CURRENT_USER;
3975 if(hRedirectionRootKey)
3977 hr = HRESULT_FROM_WIN32(RegGetValueW(hRedirectionRootKey, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders", sFolderId, RRF_RT_REG_SZ, NULL, NULL, &dwSize));
3979 if(SUCCEEDED(hr))
3981 *ppszPath = CoTaskMemAlloc(dwSize+(lstrlenW(path)+1)*sizeof(WCHAR));
3982 if(!*ppszPath) hr = E_OUTOFMEMORY;
3985 if(SUCCEEDED(hr))
3987 lstrcpyW(*ppszPath, path);
3988 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));
3992 if(!*ppszPath)
3994 /* no redirection, use previous way - read the relative path from folder definition */
3995 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, registryPath, L"RelativePath", RRF_RT_REG_SZ, &dwType, NULL, &dwSize));
3997 if(SUCCEEDED(hr))
3999 *ppszPath = CoTaskMemAlloc(dwSize+(lstrlenW(path)+1)*sizeof(WCHAR));
4000 if(!*ppszPath) hr = E_OUTOFMEMORY;
4003 if(SUCCEEDED(hr))
4005 lstrcpyW(*ppszPath, path);
4006 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, registryPath, L"RelativePath", RRF_RT_REG_SZ, &dwType, *ppszPath + lstrlenW(path), &dwSize));
4011 TRACE("returning path: %s\n", debugstr_w(*ppszPath));
4012 return hr;
4015 static HRESULT get_known_folder_path_by_id(
4016 REFKNOWNFOLDERID folderId,
4017 LPWSTR lpRegistryPath,
4018 DWORD dwFlags,
4019 LPWSTR *ppszPath)
4021 HRESULT hr = E_FAIL;
4022 WCHAR sGuid[39];
4023 DWORD dwAttributes;
4025 TRACE("(%s, %s, 0x%08x, %p)\n", debugstr_guid(folderId), debugstr_w(lpRegistryPath), dwFlags, ppszPath);
4027 /* if this is registry-registered known folder, get path from registry */
4028 if(lpRegistryPath)
4030 StringFromGUID2(folderId, sGuid, ARRAY_SIZE(sGuid));
4032 hr = get_known_folder_path(sGuid, lpRegistryPath, ppszPath);
4034 /* in other case, use older way */
4036 if(FAILED(hr))
4037 hr = SHGetKnownFolderPath( folderId, dwFlags, NULL, ppszPath );
4039 if (FAILED(hr)) return hr;
4041 /* check if known folder really exists */
4042 dwAttributes = GetFileAttributesW(*ppszPath);
4043 if(dwAttributes == INVALID_FILE_ATTRIBUTES || !(dwAttributes & FILE_ATTRIBUTE_DIRECTORY) )
4045 TRACE("directory %s not found\n", debugstr_w(*ppszPath));
4046 CoTaskMemFree(*ppszPath);
4047 *ppszPath = NULL;
4048 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
4051 return hr;
4054 static HRESULT WINAPI knownfolder_GetPath(
4055 IKnownFolder *iface,
4056 DWORD dwFlags,
4057 LPWSTR *ppszPath)
4059 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
4060 TRACE("(%p, 0x%08x, %p)\n", knownfolder, dwFlags, ppszPath);
4062 return get_known_folder_path_by_id(&knownfolder->id, knownfolder->registryPath, dwFlags, ppszPath);
4065 static HRESULT WINAPI knownfolder_SetPath(
4066 IKnownFolder *iface,
4067 DWORD dwFlags,
4068 LPCWSTR pszPath)
4070 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
4071 HRESULT hr = S_OK;
4073 TRACE("(%p, 0x%08x, %s)\n", knownfolder, dwFlags, debugstr_w(pszPath));
4075 /* check if the known folder is registered */
4076 if(!knownfolder->registryPath)
4077 hr = E_FAIL;
4079 if(SUCCEEDED(hr))
4080 hr = redirect_known_folder(&knownfolder->id, NULL, 0, pszPath, 0, NULL, NULL);
4082 return hr;
4085 static HRESULT WINAPI knownfolder_GetIDList(
4086 IKnownFolder *iface,
4087 DWORD flags,
4088 PIDLIST_ABSOLUTE *ppidl)
4090 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
4091 TRACE("(%p, 0x%08x, %p)\n", knownfolder, flags, ppidl);
4092 return SHGetKnownFolderIDList(&knownfolder->id, flags, NULL, ppidl);
4095 static HRESULT WINAPI knownfolder_GetFolderType(
4096 IKnownFolder *iface,
4097 FOLDERTYPEID *pftid)
4099 FIXME("%p\n", pftid);
4100 return E_NOTIMPL;
4103 static HRESULT WINAPI knownfolder_GetRedirectionCapabilities(
4104 IKnownFolder *iface,
4105 KF_REDIRECTION_CAPABILITIES *pCapabilities)
4107 FIXME("%p stub\n", pCapabilities);
4108 if(!pCapabilities) return E_INVALIDARG;
4109 *pCapabilities = KF_REDIRECTION_CAPABILITIES_DENY_ALL;
4110 return S_OK;
4113 static HRESULT WINAPI knownfolder_GetFolderDefinition(
4114 IKnownFolder *iface,
4115 KNOWNFOLDER_DEFINITION *pKFD)
4117 struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
4118 HRESULT hr;
4119 DWORD dwSize;
4120 WCHAR parentGuid[39];
4121 TRACE("(%p, %p)\n", knownfolder, pKFD);
4123 if(!pKFD) return E_INVALIDARG;
4125 ZeroMemory(pKFD, sizeof(*pKFD));
4127 /* required fields */
4128 hr = get_known_folder_dword(knownfolder->registryPath, L"Category", &pKFD->category);
4129 if(FAILED(hr))
4130 return hr;
4132 hr = get_known_folder_wstr(knownfolder->registryPath, L"Name", &pKFD->pszName);
4133 if(FAILED(hr))
4134 return hr;
4136 /* optional fields */
4137 dwSize = sizeof(parentGuid);
4138 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, knownfolder->registryPath, L"ParentFolder",
4139 RRF_RT_REG_SZ, NULL, parentGuid, &dwSize));
4140 if(SUCCEEDED(hr))
4142 hr = IIDFromString(parentGuid, &pKFD->fidParent);
4143 if(FAILED(hr))
4144 return hr;
4147 get_known_folder_dword(knownfolder->registryPath, L"Attributes", &pKFD->dwAttributes);
4149 get_known_folder_wstr(knownfolder->registryPath, L"RelativePath", &pKFD->pszRelativePath);
4151 get_known_folder_wstr(knownfolder->registryPath, L"ParsingName", &pKFD->pszParsingName);
4153 return S_OK;
4156 static const struct IKnownFolderVtbl knownfolder_vtbl =
4158 knownfolder_QueryInterface,
4159 knownfolder_AddRef,
4160 knownfolder_Release,
4161 knownfolder_GetId,
4162 knownfolder_GetCategory,
4163 knownfolder_GetShellItem,
4164 knownfolder_GetPath,
4165 knownfolder_SetPath,
4166 knownfolder_GetIDList,
4167 knownfolder_GetFolderType,
4168 knownfolder_GetRedirectionCapabilities,
4169 knownfolder_GetFolderDefinition
4172 static HRESULT knownfolder_create( struct knownfolder **knownfolder )
4174 struct knownfolder *kf;
4176 kf = heap_alloc( sizeof(*kf) );
4177 if (!kf) return E_OUTOFMEMORY;
4179 kf->IKnownFolder_iface.lpVtbl = &knownfolder_vtbl;
4180 kf->refs = 1;
4181 memset( &kf->id, 0, sizeof(kf->id) );
4182 kf->registryPath = NULL;
4184 *knownfolder = kf;
4186 TRACE("returning iface %p\n", &kf->IKnownFolder_iface);
4187 return S_OK;
4190 struct foldermanager
4192 IKnownFolderManager IKnownFolderManager_iface;
4193 LONG refs;
4194 UINT num_ids;
4195 KNOWNFOLDERID *ids;
4198 static inline struct foldermanager *impl_from_IKnownFolderManager( IKnownFolderManager *iface )
4200 return CONTAINING_RECORD( iface, struct foldermanager, IKnownFolderManager_iface );
4203 static ULONG WINAPI foldermanager_AddRef(
4204 IKnownFolderManager *iface )
4206 struct foldermanager *foldermanager = impl_from_IKnownFolderManager( iface );
4207 return InterlockedIncrement( &foldermanager->refs );
4210 static ULONG WINAPI foldermanager_Release(
4211 IKnownFolderManager *iface )
4213 struct foldermanager *foldermanager = impl_from_IKnownFolderManager( iface );
4214 LONG refs = InterlockedDecrement( &foldermanager->refs );
4215 if (!refs)
4217 TRACE("destroying %p\n", foldermanager);
4218 heap_free( foldermanager->ids );
4219 heap_free( foldermanager );
4221 return refs;
4224 static HRESULT WINAPI foldermanager_QueryInterface(
4225 IKnownFolderManager *iface,
4226 REFIID riid,
4227 void **ppv )
4229 struct foldermanager *This = impl_from_IKnownFolderManager( iface );
4231 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppv );
4233 *ppv = NULL;
4234 if ( IsEqualGUID( riid, &IID_IKnownFolderManager ) ||
4235 IsEqualGUID( riid, &IID_IUnknown ) )
4237 *ppv = iface;
4239 else if ( IsEqualGUID( riid, &IID_IMarshal ) )
4241 TRACE("IID_IMarshal returning NULL.\n");
4242 return E_NOINTERFACE;
4244 else
4246 FIXME("interface %s not implemented\n", debugstr_guid(riid));
4247 return E_NOINTERFACE;
4249 IKnownFolderManager_AddRef( iface );
4250 return S_OK;
4253 static HRESULT WINAPI foldermanager_FolderIdFromCsidl(
4254 IKnownFolderManager *iface,
4255 int nCsidl,
4256 KNOWNFOLDERID *pfid)
4258 TRACE("%d, %p\n", nCsidl, pfid);
4260 if (nCsidl >= ARRAY_SIZE(CSIDL_Data))
4261 return E_INVALIDARG;
4262 *pfid = *CSIDL_Data[nCsidl].id;
4263 return S_OK;
4266 static HRESULT WINAPI foldermanager_FolderIdToCsidl(
4267 IKnownFolderManager *iface,
4268 REFKNOWNFOLDERID rfid,
4269 int *pnCsidl)
4271 int csidl;
4273 TRACE("%s, %p\n", debugstr_guid(rfid), pnCsidl);
4275 csidl = csidl_from_id( rfid );
4276 if (csidl == -1) return E_INVALIDARG;
4277 *pnCsidl = csidl;
4278 return S_OK;
4281 static HRESULT WINAPI foldermanager_GetFolderIds(
4282 IKnownFolderManager *iface,
4283 KNOWNFOLDERID **ppKFId,
4284 UINT *pCount)
4286 struct foldermanager *fm = impl_from_IKnownFolderManager( iface );
4288 TRACE("%p, %p\n", ppKFId, pCount);
4290 *ppKFId = CoTaskMemAlloc(fm->num_ids * sizeof(KNOWNFOLDERID));
4291 memcpy(*ppKFId, fm->ids, fm->num_ids * sizeof(KNOWNFOLDERID));
4292 *pCount = fm->num_ids;
4293 return S_OK;
4296 static BOOL is_knownfolder( struct foldermanager *fm, const KNOWNFOLDERID *id )
4298 UINT i;
4299 HRESULT hr;
4300 LPWSTR registryPath = NULL;
4301 HKEY hKey;
4303 /* TODO: move all entries from "CSIDL_Data" static array to registry known folder descriptions */
4304 for (i = 0; i < fm->num_ids; i++)
4305 if (IsEqualGUID( &fm->ids[i], id )) return TRUE;
4307 hr = get_known_folder_registry_path(id, NULL, &registryPath);
4308 if(SUCCEEDED(hr))
4310 hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, registryPath, 0, 0, &hKey));
4311 heap_free(registryPath);
4314 if(SUCCEEDED(hr))
4316 hr = S_OK;
4317 RegCloseKey(hKey);
4320 return hr == S_OK;
4323 static HRESULT WINAPI foldermanager_GetFolder(
4324 IKnownFolderManager *iface,
4325 REFKNOWNFOLDERID rfid,
4326 IKnownFolder **ppkf)
4328 struct foldermanager *fm = impl_from_IKnownFolderManager( iface );
4329 struct knownfolder *kf;
4330 HRESULT hr;
4332 TRACE("%s, %p\n", debugstr_guid(rfid), ppkf);
4334 if (!is_knownfolder( fm, rfid ))
4336 WARN("unknown folder\n");
4337 return E_INVALIDARG;
4339 hr = knownfolder_create( &kf );
4340 if (SUCCEEDED( hr ))
4342 hr = knownfolder_set_id( kf, rfid );
4343 *ppkf = &kf->IKnownFolder_iface;
4345 else
4346 *ppkf = NULL;
4348 return hr;
4351 static HRESULT WINAPI foldermanager_GetFolderByName(
4352 IKnownFolderManager *iface,
4353 LPCWSTR pszCanonicalName,
4354 IKnownFolder **ppkf)
4356 struct foldermanager *fm = impl_from_IKnownFolderManager( iface );
4357 struct knownfolder *kf;
4358 BOOL found = FALSE;
4359 HRESULT hr;
4360 UINT i;
4362 TRACE( "%s, %p\n", debugstr_w(pszCanonicalName), ppkf );
4364 for (i = 0; i < fm->num_ids; i++)
4366 WCHAR *path, *name;
4367 hr = get_known_folder_registry_path( &fm->ids[i], NULL, &path );
4368 if (FAILED( hr )) return hr;
4370 hr = get_known_folder_wstr( path, L"Name", &name );
4371 heap_free( path );
4372 if (FAILED( hr )) return hr;
4374 found = !wcsicmp( pszCanonicalName, name );
4375 CoTaskMemFree( name );
4376 if (found) break;
4379 if (found)
4381 hr = knownfolder_create( &kf );
4382 if (FAILED( hr )) return hr;
4384 hr = knownfolder_set_id( kf, &fm->ids[i] );
4385 if (FAILED( hr ))
4387 IKnownFolder_Release( &kf->IKnownFolder_iface );
4388 return hr;
4390 *ppkf = &kf->IKnownFolder_iface;
4392 else
4394 hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
4395 *ppkf = NULL;
4398 return hr;
4401 static HRESULT register_folder(const KNOWNFOLDERID *rfid, const KNOWNFOLDER_DEFINITION *pKFD)
4403 HRESULT hr;
4404 HKEY hKey = NULL;
4405 DWORD dwDisp;
4406 LPWSTR registryPath = NULL;
4408 hr = get_known_folder_registry_path(rfid, NULL, &registryPath);
4409 TRACE("registry path: %s\n", debugstr_w(registryPath));
4411 if(SUCCEEDED(hr))
4412 hr = HRESULT_FROM_WIN32(RegCreateKeyExW(HKEY_LOCAL_MACHINE, registryPath, 0, NULL, 0, KEY_WRITE, 0, &hKey, &dwDisp));
4414 if(SUCCEEDED(hr))
4416 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"Category", 0, REG_DWORD, (LPBYTE)&pKFD->category, sizeof(pKFD->category)));
4418 if(SUCCEEDED(hr) && pKFD->dwAttributes != 0)
4419 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"Attributes", 0, REG_DWORD, (LPBYTE)&pKFD->dwAttributes, sizeof(pKFD->dwAttributes)));
4421 if(SUCCEEDED(hr))
4422 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"Name", 0, REG_SZ, (LPBYTE)pKFD->pszName, (lstrlenW(pKFD->pszName)+1)*sizeof(WCHAR) ));
4424 if(SUCCEEDED(hr) && pKFD->pszParsingName)
4425 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"ParsingName", 0, REG_SZ, (LPBYTE)pKFD->pszParsingName, (lstrlenW(pKFD->pszParsingName)+1)*sizeof(WCHAR) ));
4427 if(SUCCEEDED(hr) && !IsEqualGUID(&pKFD->fidParent, &GUID_NULL))
4429 WCHAR sParentGuid[39];
4430 StringFromGUID2(&pKFD->fidParent, sParentGuid, ARRAY_SIZE(sParentGuid));
4432 /* this known folder has parent folder */
4433 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"ParentFolder", 0, REG_SZ, (LPBYTE)sParentGuid, sizeof(sParentGuid)));
4436 if(SUCCEEDED(hr) && pKFD->category != KF_CATEGORY_VIRTUAL && pKFD->pszRelativePath)
4437 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"RelativePath", 0, REG_SZ, (LPBYTE)pKFD->pszRelativePath, (lstrlenW(pKFD->pszRelativePath)+1)*sizeof(WCHAR) ));
4439 RegCloseKey(hKey);
4441 if(FAILED(hr))
4442 SHDeleteKeyW(HKEY_LOCAL_MACHINE, registryPath);
4445 heap_free(registryPath);
4446 return hr;
4449 static HRESULT WINAPI foldermanager_RegisterFolder(
4450 IKnownFolderManager *iface,
4451 REFKNOWNFOLDERID rfid,
4452 KNOWNFOLDER_DEFINITION const *pKFD)
4454 TRACE("(%p, %s, %p)\n", iface, debugstr_guid(rfid), pKFD);
4455 return register_folder(rfid, pKFD);
4458 static HRESULT WINAPI foldermanager_UnregisterFolder(
4459 IKnownFolderManager *iface,
4460 REFKNOWNFOLDERID rfid)
4462 HRESULT hr;
4463 LPWSTR registryPath = NULL;
4464 TRACE("(%p, %s)\n", iface, debugstr_guid(rfid));
4466 hr = get_known_folder_registry_path(rfid, NULL, &registryPath);
4468 if(SUCCEEDED(hr))
4469 hr = HRESULT_FROM_WIN32(SHDeleteKeyW(HKEY_LOCAL_MACHINE, registryPath));
4471 heap_free(registryPath);
4472 return hr;
4475 static HRESULT WINAPI foldermanager_FindFolderFromPath(
4476 IKnownFolderManager *iface,
4477 LPCWSTR pszPath,
4478 FFFP_MODE mode,
4479 IKnownFolder **ppkf)
4481 FIXME("%s, 0x%08x, %p\n", debugstr_w(pszPath), mode, ppkf);
4482 return E_NOTIMPL;
4485 static HRESULT WINAPI foldermanager_FindFolderFromIDList(
4486 IKnownFolderManager *iface,
4487 PCIDLIST_ABSOLUTE pidl,
4488 IKnownFolder **ppkf)
4490 FIXME("%p, %p\n", pidl, ppkf);
4491 return E_NOTIMPL;
4494 static HRESULT WINAPI foldermanager_Redirect(
4495 IKnownFolderManager *iface,
4496 REFKNOWNFOLDERID rfid,
4497 HWND hwnd,
4498 KF_REDIRECT_FLAGS flags,
4499 LPCWSTR pszTargetPath,
4500 UINT cFolders,
4501 KNOWNFOLDERID const *pExclusion,
4502 LPWSTR *ppszError)
4504 return redirect_known_folder(rfid, hwnd, flags, pszTargetPath, cFolders, pExclusion, ppszError);
4507 static const struct IKnownFolderManagerVtbl foldermanager_vtbl =
4509 foldermanager_QueryInterface,
4510 foldermanager_AddRef,
4511 foldermanager_Release,
4512 foldermanager_FolderIdFromCsidl,
4513 foldermanager_FolderIdToCsidl,
4514 foldermanager_GetFolderIds,
4515 foldermanager_GetFolder,
4516 foldermanager_GetFolderByName,
4517 foldermanager_RegisterFolder,
4518 foldermanager_UnregisterFolder,
4519 foldermanager_FindFolderFromPath,
4520 foldermanager_FindFolderFromIDList,
4521 foldermanager_Redirect
4524 static HRESULT foldermanager_create( void **ppv )
4526 UINT i, j;
4527 struct foldermanager *fm;
4529 fm = heap_alloc( sizeof(*fm) );
4530 if (!fm) return E_OUTOFMEMORY;
4532 fm->IKnownFolderManager_iface.lpVtbl = &foldermanager_vtbl;
4533 fm->refs = 1;
4534 fm->num_ids = 0;
4536 for (i = 0; i < ARRAY_SIZE(CSIDL_Data); i++)
4538 if (!IsEqualGUID( CSIDL_Data[i].id, &GUID_NULL )) fm->num_ids++;
4540 fm->ids = heap_alloc( fm->num_ids * sizeof(KNOWNFOLDERID) );
4541 if (!fm->ids)
4543 heap_free( fm );
4544 return E_OUTOFMEMORY;
4546 for (i = j = 0; i < ARRAY_SIZE(CSIDL_Data); i++)
4548 if (!IsEqualGUID( CSIDL_Data[i].id, &GUID_NULL ))
4550 fm->ids[j] = *CSIDL_Data[i].id;
4551 j++;
4554 TRACE("found %u known folders\n", fm->num_ids);
4555 *ppv = &fm->IKnownFolderManager_iface;
4557 TRACE("returning iface %p\n", *ppv);
4558 return S_OK;
4561 HRESULT WINAPI KnownFolderManager_Constructor( IUnknown *punk, REFIID riid, void **ppv )
4563 TRACE("%p, %s, %p\n", punk, debugstr_guid(riid), ppv);
4565 if (!ppv)
4566 return E_POINTER;
4567 if (punk)
4568 return CLASS_E_NOAGGREGATION;
4570 return foldermanager_create( ppv );
4573 HRESULT WINAPI SHGetKnownFolderIDList(REFKNOWNFOLDERID rfid, DWORD flags, HANDLE token, PIDLIST_ABSOLUTE *pidl)
4575 TRACE("%s, 0x%08x, %p, %p\n", debugstr_guid(rfid), flags, token, pidl);
4577 if (!pidl)
4578 return E_INVALIDARG;
4580 if (flags)
4581 FIXME("unsupported flags: 0x%08x\n", flags);
4583 if (token)
4584 FIXME("user token is not used.\n");
4586 *pidl = NULL;
4587 if (IsEqualIID(rfid, &FOLDERID_Desktop))
4588 *pidl = _ILCreateDesktop();
4589 else if (IsEqualIID(rfid, &FOLDERID_RecycleBinFolder))
4590 *pidl = _ILCreateBitBucket();
4591 else if (IsEqualIID(rfid, &FOLDERID_ComputerFolder))
4592 *pidl = _ILCreateMyComputer();
4593 else if (IsEqualIID(rfid, &FOLDERID_PrintersFolder))
4594 *pidl = _ILCreatePrinters();
4595 else if (IsEqualIID(rfid, &FOLDERID_ControlPanelFolder))
4596 *pidl = _ILCreateControlPanel();
4597 else if (IsEqualIID(rfid, &FOLDERID_NetworkFolder))
4598 *pidl = _ILCreateNetwork();
4599 else if (IsEqualIID(rfid, &FOLDERID_Documents))
4600 *pidl = _ILCreateMyDocuments();
4601 else
4603 DWORD attributes = 0;
4604 WCHAR *pathW;
4605 HRESULT hr;
4607 hr = SHGetKnownFolderPath(rfid, flags, token, &pathW);
4608 if (FAILED(hr))
4609 return hr;
4611 hr = SHILCreateFromPathW(pathW, pidl, &attributes);
4612 CoTaskMemFree(pathW);
4613 return hr;
4616 return *pidl ? S_OK : E_FAIL;
4619 HRESULT WINAPI SHGetKnownFolderItem(REFKNOWNFOLDERID rfid, KNOWN_FOLDER_FLAG flags, HANDLE hToken,
4620 REFIID riid, void **ppv)
4622 PIDLIST_ABSOLUTE pidl;
4623 HRESULT hr;
4625 TRACE("%s, 0x%08x, %p, %s, %p\n", debugstr_guid(rfid), flags, hToken, debugstr_guid(riid), ppv);
4627 hr = SHGetKnownFolderIDList(rfid, flags, hToken, &pidl);
4628 if (FAILED(hr))
4630 *ppv = NULL;
4631 return hr;
4634 hr = SHCreateItemFromIDList(pidl, riid, ppv);
4635 CoTaskMemFree(pidl);
4636 return hr;
4639 static void register_system_knownfolders(void)
4641 int i;
4643 for (i = 0; i < ARRAY_SIZE(CSIDL_Data); ++i)
4645 const CSIDL_DATA *folder = &CSIDL_Data[i];
4646 if(folder->name){
4647 KNOWNFOLDER_DEFINITION kfd;
4649 /* register_folder won't modify kfd, so cast away const instead of
4650 * reallocating */
4651 kfd.category = folder->category;
4652 kfd.pszName = (WCHAR *)folder->name;
4653 kfd.pszDescription = NULL;
4654 kfd.fidParent = folder->parent ? *folder->parent : GUID_NULL;
4655 kfd.pszRelativePath = (WCHAR *)folder->path;
4656 kfd.pszParsingName = (WCHAR *)folder->parsing;
4657 kfd.pszTooltip = NULL;
4658 kfd.pszLocalizedName = NULL;
4659 kfd.pszIcon = NULL;
4660 kfd.pszSecurity = NULL;
4661 kfd.dwAttributes = folder->attributes;
4662 kfd.kfdFlags = folder->flags;
4663 kfd.ftidType = folder->typeid ? *folder->typeid : GUID_NULL;
4664 register_folder(folder->id, &kfd);
4669 HRESULT SHELL_RegisterShellFolders(void)
4671 HRESULT hr;
4673 hr = _SHRegisterUserShellFolders(TRUE);
4674 if (SUCCEEDED(hr))
4675 hr = _SHRegisterUserShellFolders(FALSE);
4676 if (SUCCEEDED(hr))
4677 hr = _SHRegisterCommonShellFolders();
4678 if (SUCCEEDED(hr))
4679 hr = create_extra_folders();
4680 if (SUCCEEDED(hr))
4681 hr = set_folder_attributes();
4682 if (SUCCEEDED(hr))
4683 register_system_knownfolders();
4684 return hr;