shell32: Remove WINAPI on static functions where not needed.
[wine] / dlls / shell32 / shellpath.c
1 /*
2  * Path Functions
3  *
4  * Copyright 1998, 1999, 2000 Juergen Schmied
5  * Copyright 2004 Juan Lang
6  *
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.
11  *
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.
16  *
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
20  *
21  * NOTES:
22  *
23  * Many of these functions are in SHLWAPI.DLL also
24  *
25  */
26
27 #define COBJMACROS
28
29 #include "config.h"
30 #include "wine/port.h"
31
32 #include <stdio.h>
33 #include <stdarg.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include "wine/debug.h"
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winnls.h"
40 #include "winreg.h"
41 #include "wingdi.h"
42 #include "winuser.h"
43
44 #include "shlobj.h"
45 #include "shtypes.h"
46 #include "shresdef.h"
47 #include "shell32_main.h"
48 #include "undocshell.h"
49 #include "pidl.h"
50 #include "wine/unicode.h"
51 #include "shlwapi.h"
52 #include "xdg.h"
53 #include "sddl.h"
54 #define INITGUID
55 #include "knownfolders.h"
56 #include "shobjidl.h"
57
58 WINE_DEFAULT_DEBUG_CHANNEL(shell);
59
60 static const BOOL is_win64 = sizeof(void *) > sizeof(int);
61
62 /*
63         ########## Combining and Constructing paths ##########
64 */
65
66 /*************************************************************************
67  * PathAppend           [SHELL32.36]
68  */
69 BOOL WINAPI PathAppendAW(
70         LPVOID lpszPath1,
71         LPCVOID lpszPath2)
72 {
73         if (SHELL_OsIsUnicode())
74           return PathAppendW(lpszPath1, lpszPath2);
75         return PathAppendA(lpszPath1, lpszPath2);
76 }
77
78 /*************************************************************************
79  * PathCombine   [SHELL32.37]
80  */
81 LPVOID WINAPI PathCombineAW(
82         LPVOID szDest,
83         LPCVOID lpszDir,
84         LPCVOID lpszFile)
85 {
86         if (SHELL_OsIsUnicode())
87           return PathCombineW( szDest, lpszDir, lpszFile );
88         return PathCombineA( szDest, lpszDir, lpszFile );
89 }
90
91 /*************************************************************************
92  * PathAddBackslash             [SHELL32.32]
93  */
94 LPVOID WINAPI PathAddBackslashAW(LPVOID lpszPath)
95 {
96         if(SHELL_OsIsUnicode())
97           return PathAddBackslashW(lpszPath);
98         return PathAddBackslashA(lpszPath);
99 }
100
101 /*************************************************************************
102  * PathBuildRoot                [SHELL32.30]
103  */
104 LPVOID WINAPI PathBuildRootAW(LPVOID lpszPath, int drive)
105 {
106         if(SHELL_OsIsUnicode())
107           return PathBuildRootW(lpszPath, drive);
108         return PathBuildRootA(lpszPath, drive);
109 }
110
111 /*
112         Extracting Component Parts
113 */
114
115 /*************************************************************************
116  * PathFindFileName     [SHELL32.34]
117  */
118 LPVOID WINAPI PathFindFileNameAW(LPCVOID lpszPath)
119 {
120         if(SHELL_OsIsUnicode())
121           return PathFindFileNameW(lpszPath);
122         return PathFindFileNameA(lpszPath);
123 }
124
125 /*************************************************************************
126  * PathFindExtension            [SHELL32.31]
127  */
128 LPVOID WINAPI PathFindExtensionAW(LPCVOID lpszPath)
129 {
130         if (SHELL_OsIsUnicode())
131           return PathFindExtensionW(lpszPath);
132         return PathFindExtensionA(lpszPath);
133
134 }
135
136 /*************************************************************************
137  * PathGetExtensionA            [internal]
138  *
139  * NOTES
140  *  exported by ordinal
141  *  return value points to the first char after the dot
142  */
143 static LPSTR PathGetExtensionA(LPCSTR lpszPath)
144 {
145         TRACE("(%s)\n",lpszPath);
146
147         lpszPath = PathFindExtensionA(lpszPath);
148         return (LPSTR)(*lpszPath?(lpszPath+1):lpszPath);
149 }
150
151 /*************************************************************************
152  * PathGetExtensionW            [internal]
153  */
154 static LPWSTR PathGetExtensionW(LPCWSTR lpszPath)
155 {
156         TRACE("(%s)\n",debugstr_w(lpszPath));
157
158         lpszPath = PathFindExtensionW(lpszPath);
159         return (LPWSTR)(*lpszPath?(lpszPath+1):lpszPath);
160 }
161
162 /*************************************************************************
163  * PathGetExtension             [SHELL32.158]
164  */
165 LPVOID WINAPI PathGetExtensionAW(LPCVOID lpszPath,DWORD void1, DWORD void2)
166 {
167         if (SHELL_OsIsUnicode())
168           return PathGetExtensionW(lpszPath);
169         return PathGetExtensionA(lpszPath);
170 }
171
172 /*************************************************************************
173  * PathGetArgs  [SHELL32.52]
174  */
175 LPVOID WINAPI PathGetArgsAW(LPVOID lpszPath)
176 {
177         if (SHELL_OsIsUnicode())
178           return PathGetArgsW(lpszPath);
179         return PathGetArgsA(lpszPath);
180 }
181
182 /*************************************************************************
183  * PathGetDriveNumber   [SHELL32.57]
184  */
185 int WINAPI PathGetDriveNumberAW(LPVOID lpszPath)
186 {
187         if (SHELL_OsIsUnicode())
188           return PathGetDriveNumberW(lpszPath);
189         return PathGetDriveNumberA(lpszPath);
190 }
191
192 /*************************************************************************
193  * PathRemoveFileSpec [SHELL32.35]
194  */
195 BOOL WINAPI PathRemoveFileSpecAW(LPVOID lpszPath)
196 {
197         if (SHELL_OsIsUnicode())
198           return PathRemoveFileSpecW(lpszPath);
199         return PathRemoveFileSpecA(lpszPath);
200 }
201
202 /*************************************************************************
203  * PathStripPath        [SHELL32.38]
204  */
205 void WINAPI PathStripPathAW(LPVOID lpszPath)
206 {
207         if (SHELL_OsIsUnicode())
208             PathStripPathW(lpszPath);
209         else
210             PathStripPathA(lpszPath);
211 }
212
213 /*************************************************************************
214  * PathStripToRoot      [SHELL32.50]
215  */
216 BOOL WINAPI PathStripToRootAW(LPVOID lpszPath)
217 {
218         if (SHELL_OsIsUnicode())
219           return PathStripToRootW(lpszPath);
220         return PathStripToRootA(lpszPath);
221 }
222
223 /*************************************************************************
224  * PathRemoveArgs       [SHELL32.251]
225  */
226 void WINAPI PathRemoveArgsAW(LPVOID lpszPath)
227 {
228         if (SHELL_OsIsUnicode())
229             PathRemoveArgsW(lpszPath);
230         else
231             PathRemoveArgsA(lpszPath);
232 }
233
234 /*************************************************************************
235  * PathRemoveExtension  [SHELL32.250]
236  */
237 void WINAPI PathRemoveExtensionAW(LPVOID lpszPath)
238 {
239         if (SHELL_OsIsUnicode())
240             PathRemoveExtensionW(lpszPath);
241         else
242             PathRemoveExtensionA(lpszPath);
243 }
244
245
246 /*
247         Path Manipulations
248 */
249
250 /*************************************************************************
251  * PathGetShortPathA [internal]
252  */
253 static void PathGetShortPathA(LPSTR pszPath)
254 {
255         CHAR path[MAX_PATH];
256
257         TRACE("%s\n", pszPath);
258
259         if (GetShortPathNameA(pszPath, path, MAX_PATH))
260         {
261           lstrcpyA(pszPath, path);
262         }
263 }
264
265 /*************************************************************************
266  * PathGetShortPathW [internal]
267  */
268 static void PathGetShortPathW(LPWSTR pszPath)
269 {
270         WCHAR path[MAX_PATH];
271
272         TRACE("%s\n", debugstr_w(pszPath));
273
274         if (GetShortPathNameW(pszPath, path, MAX_PATH))
275         {
276           lstrcpyW(pszPath, path);
277         }
278 }
279
280 /*************************************************************************
281  * PathGetShortPath [SHELL32.92]
282  */
283 VOID WINAPI PathGetShortPathAW(LPVOID pszPath)
284 {
285         if(SHELL_OsIsUnicode())
286           PathGetShortPathW(pszPath);
287         PathGetShortPathA(pszPath);
288 }
289
290 /*************************************************************************
291  * PathRemoveBlanks [SHELL32.33]
292  */
293 void WINAPI PathRemoveBlanksAW(LPVOID str)
294 {
295         if(SHELL_OsIsUnicode())
296             PathRemoveBlanksW(str);
297         else
298             PathRemoveBlanksA(str);
299 }
300
301 /*************************************************************************
302  * PathQuoteSpaces [SHELL32.55]
303  */
304 VOID WINAPI PathQuoteSpacesAW (LPVOID lpszPath)
305 {
306         if(SHELL_OsIsUnicode())
307             PathQuoteSpacesW(lpszPath);
308         else
309             PathQuoteSpacesA(lpszPath);
310 }
311
312 /*************************************************************************
313  * PathUnquoteSpaces [SHELL32.56]
314  */
315 VOID WINAPI PathUnquoteSpacesAW(LPVOID str)
316 {
317         if(SHELL_OsIsUnicode())
318           PathUnquoteSpacesW(str);
319         else
320           PathUnquoteSpacesA(str);
321 }
322
323 /*************************************************************************
324  * PathParseIconLocation        [SHELL32.249]
325  */
326 int WINAPI PathParseIconLocationAW (LPVOID lpszPath)
327 {
328         if(SHELL_OsIsUnicode())
329           return PathParseIconLocationW(lpszPath);
330         return PathParseIconLocationA(lpszPath);
331 }
332
333 /*
334         ########## Path Testing ##########
335 */
336 /*************************************************************************
337  * PathIsUNC            [SHELL32.39]
338  */
339 BOOL WINAPI PathIsUNCAW (LPCVOID lpszPath)
340 {
341         if (SHELL_OsIsUnicode())
342           return PathIsUNCW( lpszPath );
343         return PathIsUNCA( lpszPath );
344 }
345
346 /*************************************************************************
347  *  PathIsRelative      [SHELL32.40]
348  */
349 BOOL WINAPI PathIsRelativeAW (LPCVOID lpszPath)
350 {
351         if (SHELL_OsIsUnicode())
352           return PathIsRelativeW( lpszPath );
353         return PathIsRelativeA( lpszPath );
354 }
355
356 /*************************************************************************
357  * PathIsRoot           [SHELL32.29]
358  */
359 BOOL WINAPI PathIsRootAW(LPCVOID lpszPath)
360 {
361         if (SHELL_OsIsUnicode())
362           return PathIsRootW(lpszPath);
363         return PathIsRootA(lpszPath);
364 }
365
366 /*************************************************************************
367  *  PathIsExeA          [internal]
368  */
369 static BOOL PathIsExeA (LPCSTR lpszPath)
370 {
371         LPCSTR lpszExtension = PathGetExtensionA(lpszPath);
372         int i;
373         static const char * const lpszExtensions[] =
374             {"exe", "com", "pif", "cmd", "bat", "scf", "scr", NULL };
375
376         TRACE("path=%s\n",lpszPath);
377
378         for(i=0; lpszExtensions[i]; i++)
379           if (!lstrcmpiA(lpszExtension,lpszExtensions[i])) return TRUE;
380
381         return FALSE;
382 }
383
384 /*************************************************************************
385  *  PathIsExeW          [internal]
386  */
387 static BOOL PathIsExeW (LPCWSTR lpszPath)
388 {
389         LPCWSTR lpszExtension = PathGetExtensionW(lpszPath);
390         int i;
391         static const WCHAR lpszExtensions[][4] =
392             {{'e','x','e','\0'}, {'c','o','m','\0'}, {'p','i','f','\0'},
393              {'c','m','d','\0'}, {'b','a','t','\0'}, {'s','c','f','\0'},
394              {'s','c','r','\0'}, {'\0'} };
395
396         TRACE("path=%s\n",debugstr_w(lpszPath));
397
398         for(i=0; lpszExtensions[i][0]; i++)
399           if (!strcmpiW(lpszExtension,lpszExtensions[i])) return TRUE;
400
401         return FALSE;
402 }
403
404 /*************************************************************************
405  *  PathIsExe           [SHELL32.43]
406  */
407 BOOL WINAPI PathIsExeAW (LPCVOID path)
408 {
409         if (SHELL_OsIsUnicode())
410           return PathIsExeW (path);
411         return PathIsExeA(path);
412 }
413
414 /*************************************************************************
415  * PathIsDirectory      [SHELL32.159]
416  */
417 BOOL WINAPI PathIsDirectoryAW (LPCVOID lpszPath)
418 {
419         if (SHELL_OsIsUnicode())
420           return PathIsDirectoryW (lpszPath);
421         return PathIsDirectoryA (lpszPath);
422 }
423
424 /*************************************************************************
425  * PathFileExists       [SHELL32.45]
426  */
427 BOOL WINAPI PathFileExistsAW (LPCVOID lpszPath)
428 {
429         if (SHELL_OsIsUnicode())
430           return PathFileExistsW (lpszPath);
431         return PathFileExistsA (lpszPath);
432 }
433
434 /*************************************************************************
435  * PathMatchSpec        [SHELL32.46]
436  */
437 BOOL WINAPI PathMatchSpecAW(LPVOID name, LPVOID mask)
438 {
439         if (SHELL_OsIsUnicode())
440           return PathMatchSpecW( name, mask );
441         return PathMatchSpecA( name, mask );
442 }
443
444 /*************************************************************************
445  * PathIsSameRoot       [SHELL32.650]
446  */
447 BOOL WINAPI PathIsSameRootAW(LPCVOID lpszPath1, LPCVOID lpszPath2)
448 {
449         if (SHELL_OsIsUnicode())
450           return PathIsSameRootW(lpszPath1, lpszPath2);
451         return PathIsSameRootA(lpszPath1, lpszPath2);
452 }
453
454 /*************************************************************************
455  * IsLFNDriveA          [SHELL32.41]
456  */
457 BOOL WINAPI IsLFNDriveA(LPCSTR lpszPath)
458 {
459     DWORD       fnlen;
460
461     if (!GetVolumeInformationA(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
462         return FALSE;
463     return fnlen > 12;
464 }
465
466 /*************************************************************************
467  * IsLFNDriveW          [SHELL32.42]
468  */
469 BOOL WINAPI IsLFNDriveW(LPCWSTR lpszPath)
470 {
471     DWORD       fnlen;
472
473     if (!GetVolumeInformationW(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
474         return FALSE;
475     return fnlen > 12;
476 }
477
478 /*************************************************************************
479  * IsLFNDrive           [SHELL32.119]
480  */
481 BOOL WINAPI IsLFNDriveAW(LPCVOID lpszPath)
482 {
483         if (SHELL_OsIsUnicode())
484           return IsLFNDriveW(lpszPath);
485         return IsLFNDriveA(lpszPath);
486 }
487
488 /*
489         ########## Creating Something Unique ##########
490 */
491 /*************************************************************************
492  * PathMakeUniqueNameA  [internal]
493  */
494 static BOOL PathMakeUniqueNameA(
495         LPSTR lpszBuffer,
496         DWORD dwBuffSize,
497         LPCSTR lpszShortName,
498         LPCSTR lpszLongName,
499         LPCSTR lpszPathName)
500 {
501         FIXME("%p %u %s %s %s stub\n",
502          lpszBuffer, dwBuffSize, debugstr_a(lpszShortName),
503          debugstr_a(lpszLongName), debugstr_a(lpszPathName));
504         return TRUE;
505 }
506
507 /*************************************************************************
508  * PathMakeUniqueNameW  [internal]
509  */
510 static BOOL PathMakeUniqueNameW(
511         LPWSTR lpszBuffer,
512         DWORD dwBuffSize,
513         LPCWSTR lpszShortName,
514         LPCWSTR lpszLongName,
515         LPCWSTR lpszPathName)
516 {
517         FIXME("%p %u %s %s %s stub\n",
518          lpszBuffer, dwBuffSize, debugstr_w(lpszShortName),
519          debugstr_w(lpszLongName), debugstr_w(lpszPathName));
520         return TRUE;
521 }
522
523 /*************************************************************************
524  * PathMakeUniqueName   [SHELL32.47]
525  */
526 BOOL WINAPI PathMakeUniqueNameAW(
527         LPVOID lpszBuffer,
528         DWORD dwBuffSize,
529         LPCVOID lpszShortName,
530         LPCVOID lpszLongName,
531         LPCVOID lpszPathName)
532 {
533         if (SHELL_OsIsUnicode())
534           return PathMakeUniqueNameW(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
535         return PathMakeUniqueNameA(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
536 }
537
538 /*************************************************************************
539  * PathYetAnotherMakeUniqueName [SHELL32.75]
540  *
541  * NOTES
542  *     exported by ordinal
543  */
544 BOOL WINAPI PathYetAnotherMakeUniqueName(
545         LPWSTR lpszBuffer,
546         LPCWSTR lpszPathName,
547         LPCWSTR lpszShortName,
548         LPCWSTR lpszLongName)
549 {
550     FIXME("(%p, %s, %s ,%s):stub.\n",
551           lpszBuffer, debugstr_w(lpszPathName), debugstr_w(lpszShortName), debugstr_w(lpszLongName));
552     return TRUE;
553 }
554
555
556 /*
557         ########## cleaning and resolving paths ##########
558  */
559
560 /*************************************************************************
561  * PathFindOnPath       [SHELL32.145]
562  */
563 BOOL WINAPI PathFindOnPathAW(LPVOID sFile, LPCVOID *sOtherDirs)
564 {
565         if (SHELL_OsIsUnicode())
566           return PathFindOnPathW(sFile, (LPCWSTR *)sOtherDirs);
567         return PathFindOnPathA(sFile, (LPCSTR *)sOtherDirs);
568 }
569
570 /*************************************************************************
571  * PathCleanupSpec      [SHELL32.171]
572  *
573  * lpszFile is changed in place.
574  */
575 int WINAPI PathCleanupSpec( LPCWSTR lpszPathW, LPWSTR lpszFileW )
576 {
577     int i = 0;
578     DWORD rc = 0;
579     int length = 0;
580
581     if (SHELL_OsIsUnicode())
582     {
583         LPWSTR p = lpszFileW;
584
585         TRACE("Cleanup %s\n",debugstr_w(lpszFileW));
586
587         if (lpszPathW)
588             length = strlenW(lpszPathW);
589
590         while (*p)
591         {
592             int gct = PathGetCharTypeW(*p);
593             if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
594             {
595                 lpszFileW[i]='-';
596                 rc |= PCS_REPLACEDCHAR;
597             }
598             else
599                 lpszFileW[i]=*p;
600             i++;
601             p++;
602             if (length + i == MAX_PATH)
603             {
604                 rc |= PCS_FATAL | PCS_PATHTOOLONG;
605                 break;
606             }
607         }
608         lpszFileW[i]=0;
609     }
610     else
611     {
612         LPSTR lpszFileA = (LPSTR)lpszFileW;
613         LPCSTR lpszPathA = (LPCSTR)lpszPathW;
614         LPSTR p = lpszFileA;
615
616         TRACE("Cleanup %s\n",debugstr_a(lpszFileA));
617
618         if (lpszPathA)
619             length = strlen(lpszPathA);
620
621         while (*p)
622         {
623             int gct = PathGetCharTypeA(*p);
624             if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
625             {
626                 lpszFileA[i]='-';
627                 rc |= PCS_REPLACEDCHAR;
628             }
629             else
630                 lpszFileA[i]=*p;
631             i++;
632             p++;
633             if (length + i == MAX_PATH)
634             {
635                 rc |= PCS_FATAL | PCS_PATHTOOLONG;
636                 break;
637             }
638         }
639         lpszFileA[i]=0;
640     }
641     return rc;
642 }
643
644 /*************************************************************************
645  * PathQualifyA         [SHELL32]
646  */
647 static BOOL PathQualifyA(LPCSTR pszPath)
648 {
649         FIXME("%s\n",pszPath);
650         return 0;
651 }
652
653 /*************************************************************************
654  * PathQualifyW         [SHELL32]
655  */
656 static BOOL PathQualifyW(LPCWSTR pszPath)
657 {
658         FIXME("%s\n",debugstr_w(pszPath));
659         return 0;
660 }
661
662 /*************************************************************************
663  * PathQualify  [SHELL32.49]
664  */
665 BOOL WINAPI PathQualifyAW(LPCVOID pszPath)
666 {
667         if (SHELL_OsIsUnicode())
668           return PathQualifyW(pszPath);
669         return PathQualifyA(pszPath);
670 }
671
672 static BOOL PathResolveA(
673         LPSTR lpszPath,
674         LPCSTR *alpszPaths,
675         DWORD dwFlags)
676 {
677         FIXME("(%s,%p,0x%08x),stub!\n",
678           lpszPath, *alpszPaths, dwFlags);
679         return 0;
680 }
681
682 static BOOL PathResolveW(
683         LPWSTR lpszPath,
684         LPCWSTR *alpszPaths,
685         DWORD dwFlags)
686 {
687         FIXME("(%s,%p,0x%08x),stub!\n",
688           debugstr_w(lpszPath), debugstr_w(*alpszPaths), dwFlags);
689         return 0;
690 }
691
692 /*************************************************************************
693  * PathResolve [SHELL32.51]
694  */
695 BOOL WINAPI PathResolveAW(
696         LPVOID lpszPath,
697         LPCVOID *alpszPaths,
698         DWORD dwFlags)
699 {
700         if (SHELL_OsIsUnicode())
701           return PathResolveW(lpszPath, (LPCWSTR*)alpszPaths, dwFlags);
702         return PathResolveA(lpszPath, (LPCSTR*)alpszPaths, dwFlags);
703 }
704
705 /*************************************************************************
706 *       PathProcessCommandA
707 */
708 static LONG PathProcessCommandA (
709         LPCSTR lpszPath,
710         LPSTR lpszBuff,
711         DWORD dwBuffSize,
712         DWORD dwFlags)
713 {
714         FIXME("%s %p 0x%04x 0x%04x stub\n",
715         lpszPath, lpszBuff, dwBuffSize, dwFlags);
716         if(!lpszPath) return -1;
717         if(lpszBuff) strcpy(lpszBuff, lpszPath);
718         return strlen(lpszPath);
719 }
720
721 /*************************************************************************
722 *       PathProcessCommandW
723 */
724 static LONG PathProcessCommandW (
725         LPCWSTR lpszPath,
726         LPWSTR lpszBuff,
727         DWORD dwBuffSize,
728         DWORD dwFlags)
729 {
730         FIXME("(%s, %p, 0x%04x, 0x%04x) stub\n",
731         debugstr_w(lpszPath), lpszBuff, dwBuffSize, dwFlags);
732         if(!lpszPath) return -1;
733         if(lpszBuff) strcpyW(lpszBuff, lpszPath);
734         return strlenW(lpszPath);
735 }
736
737 /*************************************************************************
738 *       PathProcessCommand (SHELL32.653)
739 */
740 LONG WINAPI PathProcessCommandAW (
741         LPCVOID lpszPath,
742         LPVOID lpszBuff,
743         DWORD dwBuffSize,
744         DWORD dwFlags)
745 {
746         if (SHELL_OsIsUnicode())
747           return PathProcessCommandW(lpszPath, lpszBuff, dwBuffSize, dwFlags);
748         return PathProcessCommandA(lpszPath, lpszBuff, dwBuffSize, dwFlags);
749 }
750
751 /*
752         ########## special ##########
753 */
754
755 /*************************************************************************
756  * PathSetDlgItemPath (SHELL32.48)
757  */
758 VOID WINAPI PathSetDlgItemPathAW(HWND hDlg, int id, LPCVOID pszPath)
759 {
760         if (SHELL_OsIsUnicode())
761             PathSetDlgItemPathW(hDlg, id, pszPath);
762         else
763             PathSetDlgItemPathA(hDlg, id, pszPath);
764 }
765
766 static const WCHAR szCurrentVersion[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\0'};
767 static const WCHAR Administrative_ToolsW[] = {'A','d','m','i','n','i','s','t','r','a','t','i','v','e',' ','T','o','o','l','s','\0'};
768 static const WCHAR AppDataW[] = {'A','p','p','D','a','t','a','\0'};
769 static const WCHAR CacheW[] = {'C','a','c','h','e','\0'};
770 static const WCHAR CD_BurningW[] = {'C','D',' ','B','u','r','n','i','n','g','\0'};
771 static const WCHAR Common_Administrative_ToolsW[] = {'C','o','m','m','o','n',' ','A','d','m','i','n','i','s','t','r','a','t','i','v','e',' ','T','o','o','l','s','\0'};
772 static const WCHAR Common_AppDataW[] = {'C','o','m','m','o','n',' ','A','p','p','D','a','t','a','\0'};
773 static const WCHAR Common_DesktopW[] = {'C','o','m','m','o','n',' ','D','e','s','k','t','o','p','\0'};
774 static const WCHAR Common_DocumentsW[] = {'C','o','m','m','o','n',' ','D','o','c','u','m','e','n','t','s','\0'};
775 static const WCHAR Common_FavoritesW[] = {'C','o','m','m','o','n',' ','F','a','v','o','r','i','t','e','s','\0'};
776 static const WCHAR CommonFilesDirW[] = {'C','o','m','m','o','n','F','i','l','e','s','D','i','r','\0'};
777 static const WCHAR CommonFilesDirX86W[] = {'C','o','m','m','o','n','F','i','l','e','s','D','i','r',' ','(','x','8','6',')','\0'};
778 static const WCHAR CommonMusicW[] = {'C','o','m','m','o','n','M','u','s','i','c','\0'};
779 static const WCHAR CommonPicturesW[] = {'C','o','m','m','o','n','P','i','c','t','u','r','e','s','\0'};
780 static const WCHAR Common_ProgramsW[] = {'C','o','m','m','o','n',' ','P','r','o','g','r','a','m','s','\0'};
781 static const WCHAR Common_StartUpW[] = {'C','o','m','m','o','n',' ','S','t','a','r','t','U','p','\0'};
782 static const WCHAR Common_Start_MenuW[] = {'C','o','m','m','o','n',' ','S','t','a','r','t',' ','M','e','n','u','\0'};
783 static const WCHAR Common_TemplatesW[] = {'C','o','m','m','o','n',' ','T','e','m','p','l','a','t','e','s','\0'};
784 static const WCHAR CommonVideoW[] = {'C','o','m','m','o','n','V','i','d','e','o','\0'};
785 static const WCHAR ContactsW[] = {'C','o','n','t','a','c','t','s','\0'};
786 static const WCHAR CookiesW[] = {'C','o','o','k','i','e','s','\0'};
787 static const WCHAR DesktopW[] = {'D','e','s','k','t','o','p','\0'};
788 static const WCHAR FavoritesW[] = {'F','a','v','o','r','i','t','e','s','\0'};
789 static const WCHAR FontsW[] = {'F','o','n','t','s','\0'};
790 static const WCHAR HistoryW[] = {'H','i','s','t','o','r','y','\0'};
791 static const WCHAR Local_AppDataW[] = {'L','o','c','a','l',' ','A','p','p','D','a','t','a','\0'};
792 static const WCHAR My_MusicW[] = {'M','y',' ','M','u','s','i','c','\0'};
793 static const WCHAR My_PicturesW[] = {'M','y',' ','P','i','c','t','u','r','e','s','\0'};
794 static const WCHAR My_VideoW[] = {'M','y',' ','V','i','d','e','o','s','\0'};
795 static const WCHAR NetHoodW[] = {'N','e','t','H','o','o','d','\0'};
796 static const WCHAR PersonalW[] = {'P','e','r','s','o','n','a','l','\0'};
797 static const WCHAR PrintHoodW[] = {'P','r','i','n','t','H','o','o','d','\0'};
798 static const WCHAR ProgramFilesDirW[] = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r','\0'};
799 static const WCHAR ProgramFilesDirX86W[] = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r',' ','(','x','8','6',')','\0'};
800 static const WCHAR ProgramsW[] = {'P','r','o','g','r','a','m','s','\0'};
801 static const WCHAR RecentW[] = {'R','e','c','e','n','t','\0'};
802 static const WCHAR ResourcesW[] = {'R','e','s','o','u','r','c','e','s','\0'};
803 static const WCHAR SendToW[] = {'S','e','n','d','T','o','\0'};
804 static const WCHAR StartUpW[] = {'S','t','a','r','t','U','p','\0'};
805 static const WCHAR Start_MenuW[] = {'S','t','a','r','t',' ','M','e','n','u','\0'};
806 static const WCHAR TemplatesW[] = {'T','e','m','p','l','a','t','e','s','\0'};
807 static const WCHAR UsersW[] = {'U','s','e','r','s','\0'};
808 static const WCHAR UsersPublicW[] = {'U','s','e','r','s','\\','P','u','b','l','i','c','\0'};
809 static const WCHAR DefaultW[] = {'.','D','e','f','a','u','l','t','\0'};
810 static const WCHAR AllUsersProfileW[] = {'%','A','L','L','U','S','E','R','S','P','R','O','F','I','L','E','%','\0'};
811 static const WCHAR UserProfileW[] = {'%','U','S','E','R','P','R','O','F','I','L','E','%','\0'};
812 static const WCHAR SystemDriveW[] = {'%','S','y','s','t','e','m','D','r','i','v','e','%','\0'};
813 static const WCHAR ProfileListW[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s',' ','N','T','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','P','r','o','f','i','l','e','L','i','s','t',0};
814 static const WCHAR ProfilesDirectoryW[] = {'P','r','o','f','i','l','e','s','D','i','r','e','c','t','o','r','y',0};
815 static const WCHAR AllUsersProfileValueW[] = {'A','l','l','U','s','e','r','s','P','r','o','f','i','l','e','\0'};
816 static const WCHAR szSHFolders[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l','o','r','e','r','\\','S','h','e','l','l',' ','F','o','l','d','e','r','s','\0'};
817 static const WCHAR szSHUserFolders[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l','o','r','e','r','\\','U','s','e','r',' ','S','h','e','l','l',' ','F','o','l','d','e','r','s','\0'};
818 static const WCHAR szDefaultProfileDirW[] = {'u','s','e','r','s',0};
819 static const WCHAR szKnownFolderDescriptions[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l','o','r','e','r','\\','F','o','l','d','e','r','D','e','s','c','r','i','p','t','i','o','n','s','\0'};
820 static const WCHAR szKnownFolderRedirections[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','E','x','p','l','o','r','e','r','\\','U','s','e','r',' ','S','h','e','l','l',' ','F','o','l','d','e','r','s',0};
821 static const WCHAR AllUsersW[] = {'P','u','b','l','i','c',0};
822
823 typedef enum _CSIDL_Type {
824     CSIDL_Type_User,
825     CSIDL_Type_AllUsers,
826     CSIDL_Type_CurrVer,
827     CSIDL_Type_Disallowed,
828     CSIDL_Type_NonExistent,
829     CSIDL_Type_WindowsPath,
830     CSIDL_Type_SystemPath,
831     CSIDL_Type_SystemX86Path,
832 } CSIDL_Type;
833
834 typedef struct
835 {
836     const KNOWNFOLDERID *id;
837     CSIDL_Type type;
838     LPCWSTR    szValueName;
839     LPCWSTR    szDefaultPath; /* fallback string or resource ID */
840 } CSIDL_DATA;
841
842 static const CSIDL_DATA CSIDL_Data[] =
843 {
844     { /* 0x00 - CSIDL_DESKTOP */
845         &FOLDERID_Desktop,
846         CSIDL_Type_User,
847         DesktopW,
848         MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
849     },
850     { /* 0x01 - CSIDL_INTERNET */
851         &FOLDERID_InternetFolder,
852         CSIDL_Type_Disallowed,
853         NULL,
854         NULL
855     },
856     { /* 0x02 - CSIDL_PROGRAMS */
857         &FOLDERID_Programs,
858         CSIDL_Type_User,
859         ProgramsW,
860         MAKEINTRESOURCEW(IDS_PROGRAMS)
861     },
862     { /* 0x03 - CSIDL_CONTROLS (.CPL files) */
863         &FOLDERID_ControlPanelFolder,
864         CSIDL_Type_SystemPath,
865         NULL,
866         NULL
867     },
868     { /* 0x04 - CSIDL_PRINTERS */
869         &FOLDERID_PrintersFolder,
870         CSIDL_Type_SystemPath,
871         NULL,
872         NULL
873     },
874     { /* 0x05 - CSIDL_PERSONAL */
875         &GUID_NULL,
876         CSIDL_Type_User,
877         PersonalW,
878         MAKEINTRESOURCEW(IDS_PERSONAL)
879     },
880     { /* 0x06 - CSIDL_FAVORITES */
881         &FOLDERID_Favorites,
882         CSIDL_Type_User,
883         FavoritesW,
884         MAKEINTRESOURCEW(IDS_FAVORITES)
885     },
886     { /* 0x07 - CSIDL_STARTUP */
887         &FOLDERID_Startup,
888         CSIDL_Type_User,
889         StartUpW,
890         MAKEINTRESOURCEW(IDS_STARTUP)
891     },
892     { /* 0x08 - CSIDL_RECENT */
893         &FOLDERID_Recent,
894         CSIDL_Type_User,
895         RecentW,
896         MAKEINTRESOURCEW(IDS_RECENT)
897     },
898     { /* 0x09 - CSIDL_SENDTO */
899         &FOLDERID_SendTo,
900         CSIDL_Type_User,
901         SendToW,
902         MAKEINTRESOURCEW(IDS_SENDTO)
903     },
904     { /* 0x0a - CSIDL_BITBUCKET - Recycle Bin */
905         &FOLDERID_RecycleBinFolder,
906         CSIDL_Type_Disallowed,
907         NULL,
908         NULL,
909     },
910     { /* 0x0b - CSIDL_STARTMENU */
911         &FOLDERID_StartMenu,
912         CSIDL_Type_User,
913         Start_MenuW,
914         MAKEINTRESOURCEW(IDS_STARTMENU)
915     },
916     { /* 0x0c - CSIDL_MYDOCUMENTS */
917         &GUID_NULL,
918         CSIDL_Type_Disallowed, /* matches WinXP--can't get its path */
919         NULL,
920         NULL
921     },
922     { /* 0x0d - CSIDL_MYMUSIC */
923         &FOLDERID_Music,
924         CSIDL_Type_User,
925         My_MusicW,
926         MAKEINTRESOURCEW(IDS_MYMUSIC)
927     },
928     { /* 0x0e - CSIDL_MYVIDEO */
929         &FOLDERID_Videos,
930         CSIDL_Type_User,
931         My_VideoW,
932         MAKEINTRESOURCEW(IDS_MYVIDEO)
933     },
934     { /* 0x0f - unassigned */
935         &GUID_NULL,
936         CSIDL_Type_Disallowed,
937         NULL,
938         NULL,
939     },
940     { /* 0x10 - CSIDL_DESKTOPDIRECTORY */
941         &FOLDERID_Desktop,
942         CSIDL_Type_User,
943         DesktopW,
944         MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
945     },
946     { /* 0x11 - CSIDL_DRIVES */
947         &FOLDERID_ComputerFolder,
948         CSIDL_Type_Disallowed,
949         NULL,
950         NULL,
951     },
952     { /* 0x12 - CSIDL_NETWORK */
953         &FOLDERID_NetworkFolder,
954         CSIDL_Type_Disallowed,
955         NULL,
956         NULL,
957     },
958     { /* 0x13 - CSIDL_NETHOOD */
959         &FOLDERID_NetHood,
960         CSIDL_Type_User,
961         NetHoodW,
962         MAKEINTRESOURCEW(IDS_NETHOOD)
963     },
964     { /* 0x14 - CSIDL_FONTS */
965         &FOLDERID_Fonts,
966         CSIDL_Type_WindowsPath,
967         FontsW,
968         FontsW
969     },
970     { /* 0x15 - CSIDL_TEMPLATES */
971         &FOLDERID_Templates,
972         CSIDL_Type_User,
973         TemplatesW,
974         MAKEINTRESOURCEW(IDS_TEMPLATES)
975     },
976     { /* 0x16 - CSIDL_COMMON_STARTMENU */
977         &FOLDERID_CommonStartMenu,
978         CSIDL_Type_AllUsers,
979         Common_Start_MenuW,
980         MAKEINTRESOURCEW(IDS_STARTMENU)
981     },
982     { /* 0x17 - CSIDL_COMMON_PROGRAMS */
983         &FOLDERID_CommonPrograms,
984         CSIDL_Type_AllUsers,
985         Common_ProgramsW,
986         MAKEINTRESOURCEW(IDS_PROGRAMS)
987     },
988     { /* 0x18 - CSIDL_COMMON_STARTUP */
989         &FOLDERID_CommonStartup,
990         CSIDL_Type_AllUsers,
991         Common_StartUpW,
992         MAKEINTRESOURCEW(IDS_STARTUP)
993     },
994     { /* 0x19 - CSIDL_COMMON_DESKTOPDIRECTORY */
995         &FOLDERID_PublicDesktop,
996         CSIDL_Type_AllUsers,
997         Common_DesktopW,
998         MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
999     },
1000     { /* 0x1a - CSIDL_APPDATA */
1001         &FOLDERID_RoamingAppData,
1002         CSIDL_Type_User,
1003         AppDataW,
1004         MAKEINTRESOURCEW(IDS_APPDATA)
1005     },
1006     { /* 0x1b - CSIDL_PRINTHOOD */
1007         &FOLDERID_PrintHood,
1008         CSIDL_Type_User,
1009         PrintHoodW,
1010         MAKEINTRESOURCEW(IDS_PRINTHOOD)
1011     },
1012     { /* 0x1c - CSIDL_LOCAL_APPDATA */
1013         &FOLDERID_LocalAppData,
1014         CSIDL_Type_User,
1015         Local_AppDataW,
1016         MAKEINTRESOURCEW(IDS_LOCAL_APPDATA)
1017     },
1018     { /* 0x1d - CSIDL_ALTSTARTUP */
1019         &GUID_NULL,
1020         CSIDL_Type_NonExistent,
1021         NULL,
1022         NULL
1023     },
1024     { /* 0x1e - CSIDL_COMMON_ALTSTARTUP */
1025         &GUID_NULL,
1026         CSIDL_Type_NonExistent,
1027         NULL,
1028         NULL
1029     },
1030     { /* 0x1f - CSIDL_COMMON_FAVORITES */
1031         &FOLDERID_Favorites,
1032         CSIDL_Type_AllUsers,
1033         Common_FavoritesW,
1034         MAKEINTRESOURCEW(IDS_FAVORITES)
1035     },
1036     { /* 0x20 - CSIDL_INTERNET_CACHE */
1037         &FOLDERID_InternetCache,
1038         CSIDL_Type_User,
1039         CacheW,
1040         MAKEINTRESOURCEW(IDS_INTERNET_CACHE)
1041     },
1042     { /* 0x21 - CSIDL_COOKIES */
1043         &FOLDERID_Cookies,
1044         CSIDL_Type_User,
1045         CookiesW,
1046         MAKEINTRESOURCEW(IDS_COOKIES)
1047     },
1048     { /* 0x22 - CSIDL_HISTORY */
1049         &FOLDERID_History,
1050         CSIDL_Type_User,
1051         HistoryW,
1052         MAKEINTRESOURCEW(IDS_HISTORY)
1053     },
1054     { /* 0x23 - CSIDL_COMMON_APPDATA */
1055         &FOLDERID_ProgramData,
1056         CSIDL_Type_AllUsers,
1057         Common_AppDataW,
1058         MAKEINTRESOURCEW(IDS_APPDATA)
1059     },
1060     { /* 0x24 - CSIDL_WINDOWS */
1061         &FOLDERID_Windows,
1062         CSIDL_Type_WindowsPath,
1063         NULL,
1064         NULL
1065     },
1066     { /* 0x25 - CSIDL_SYSTEM */
1067         &FOLDERID_System,
1068         CSIDL_Type_SystemPath,
1069         NULL,
1070         NULL
1071     },
1072     { /* 0x26 - CSIDL_PROGRAM_FILES */
1073         &FOLDERID_ProgramFiles,
1074         CSIDL_Type_CurrVer,
1075         ProgramFilesDirW,
1076         MAKEINTRESOURCEW(IDS_PROGRAM_FILES)
1077     },
1078     { /* 0x27 - CSIDL_MYPICTURES */
1079         &FOLDERID_Pictures,
1080         CSIDL_Type_User,
1081         My_PicturesW,
1082         MAKEINTRESOURCEW(IDS_MYPICTURES)
1083     },
1084     { /* 0x28 - CSIDL_PROFILE */
1085         &FOLDERID_Profile,
1086         CSIDL_Type_User,
1087         NULL,
1088         NULL
1089     },
1090     { /* 0x29 - CSIDL_SYSTEMX86 */
1091         &FOLDERID_SystemX86,
1092         CSIDL_Type_SystemX86Path,
1093         NULL,
1094         NULL
1095     },
1096     { /* 0x2a - CSIDL_PROGRAM_FILESX86 */
1097         &FOLDERID_ProgramFilesX86,
1098         CSIDL_Type_CurrVer,
1099         ProgramFilesDirX86W,
1100         MAKEINTRESOURCEW(IDS_PROGRAM_FILESX86)
1101     },
1102     { /* 0x2b - CSIDL_PROGRAM_FILES_COMMON */
1103         &FOLDERID_ProgramFilesCommon,
1104         CSIDL_Type_CurrVer,
1105         CommonFilesDirW,
1106         MAKEINTRESOURCEW(IDS_PROGRAM_FILES_COMMON)
1107     },
1108     { /* 0x2c - CSIDL_PROGRAM_FILES_COMMONX86 */
1109         &FOLDERID_ProgramFilesCommonX86,
1110         CSIDL_Type_CurrVer,
1111         CommonFilesDirX86W,
1112         MAKEINTRESOURCEW(IDS_PROGRAM_FILES_COMMONX86)
1113     },
1114     { /* 0x2d - CSIDL_COMMON_TEMPLATES */
1115         &FOLDERID_CommonTemplates,
1116         CSIDL_Type_AllUsers,
1117         Common_TemplatesW,
1118         MAKEINTRESOURCEW(IDS_TEMPLATES)
1119     },
1120     { /* 0x2e - CSIDL_COMMON_DOCUMENTS */
1121         &FOLDERID_PublicDocuments,
1122         CSIDL_Type_AllUsers,
1123         Common_DocumentsW,
1124         MAKEINTRESOURCEW(IDS_COMMON_DOCUMENTS)
1125     },
1126     { /* 0x2f - CSIDL_COMMON_ADMINTOOLS */
1127         &FOLDERID_CommonAdminTools,
1128         CSIDL_Type_AllUsers,
1129         Common_Administrative_ToolsW,
1130         MAKEINTRESOURCEW(IDS_ADMINTOOLS)
1131     },
1132     { /* 0x30 - CSIDL_ADMINTOOLS */
1133         &FOLDERID_AdminTools,
1134         CSIDL_Type_User,
1135         Administrative_ToolsW,
1136         MAKEINTRESOURCEW(IDS_ADMINTOOLS)
1137     },
1138     { /* 0x31 - CSIDL_CONNECTIONS */
1139         &FOLDERID_ConnectionsFolder,
1140         CSIDL_Type_Disallowed,
1141         NULL,
1142         NULL
1143     },
1144     { /* 0x32 - unassigned */
1145         &GUID_NULL,
1146         CSIDL_Type_Disallowed,
1147         NULL,
1148         NULL
1149     },
1150     { /* 0x33 - unassigned */
1151         &GUID_NULL,
1152         CSIDL_Type_Disallowed,
1153         NULL,
1154         NULL
1155     },
1156     { /* 0x34 - unassigned */
1157         &GUID_NULL,
1158         CSIDL_Type_Disallowed,
1159         NULL,
1160         NULL
1161     },
1162     { /* 0x35 - CSIDL_COMMON_MUSIC */
1163         &FOLDERID_PublicMusic,
1164         CSIDL_Type_AllUsers,
1165         CommonMusicW,
1166         MAKEINTRESOURCEW(IDS_COMMON_MUSIC)
1167     },
1168     { /* 0x36 - CSIDL_COMMON_PICTURES */
1169         &FOLDERID_PublicPictures,
1170         CSIDL_Type_AllUsers,
1171         CommonPicturesW,
1172         MAKEINTRESOURCEW(IDS_COMMON_PICTURES)
1173     },
1174     { /* 0x37 - CSIDL_COMMON_VIDEO */
1175         &FOLDERID_PublicVideos,
1176         CSIDL_Type_AllUsers,
1177         CommonVideoW,
1178         MAKEINTRESOURCEW(IDS_COMMON_VIDEO)
1179     },
1180     { /* 0x38 - CSIDL_RESOURCES */
1181         &FOLDERID_ResourceDir,
1182         CSIDL_Type_WindowsPath,
1183         NULL,
1184         ResourcesW
1185     },
1186     { /* 0x39 - CSIDL_RESOURCES_LOCALIZED */
1187         &FOLDERID_LocalizedResourcesDir,
1188         CSIDL_Type_NonExistent,
1189         NULL,
1190         NULL
1191     },
1192     { /* 0x3a - CSIDL_COMMON_OEM_LINKS */
1193         &FOLDERID_CommonOEMLinks,
1194         CSIDL_Type_AllUsers,
1195         NULL,
1196         MAKEINTRESOURCEW(IDS_COMMON_OEM_LINKS)
1197     },
1198     { /* 0x3b - CSIDL_CDBURN_AREA */
1199         &FOLDERID_CDBurning,
1200         CSIDL_Type_User,
1201         CD_BurningW,
1202         MAKEINTRESOURCEW(IDS_CDBURN_AREA)
1203     },
1204     { /* 0x3c unassigned */
1205         &GUID_NULL,
1206         CSIDL_Type_Disallowed,
1207         NULL,
1208         NULL
1209     },
1210     { /* 0x3d - CSIDL_COMPUTERSNEARME */
1211         &GUID_NULL,
1212         CSIDL_Type_Disallowed, /* FIXME */
1213         NULL,
1214         NULL
1215     },
1216     { /* 0x3e - CSIDL_PROFILES */
1217         &GUID_NULL,
1218         CSIDL_Type_Disallowed, /* oddly, this matches WinXP */
1219         NULL,
1220         NULL
1221     },
1222     { /* 0x3f */
1223         &FOLDERID_AddNewPrograms,
1224         CSIDL_Type_Disallowed,
1225         NULL,
1226         NULL
1227     },
1228     { /* 0x40 */
1229         &FOLDERID_AppUpdates,
1230         CSIDL_Type_Disallowed,
1231         NULL,
1232         NULL
1233     },
1234     { /* 0x41 */
1235         &FOLDERID_ChangeRemovePrograms,
1236         CSIDL_Type_Disallowed,
1237         NULL,
1238         NULL
1239     },
1240     { /* 0x42 */
1241         &FOLDERID_ConflictFolder,
1242         CSIDL_Type_Disallowed,
1243         NULL,
1244         NULL
1245     },
1246     { /* 0x43 */
1247         &FOLDERID_Contacts,
1248         CSIDL_Type_User,
1249         ContactsW,
1250         MAKEINTRESOURCEW(IDS_CONTACTS)
1251     },
1252     { /* 0x44 */
1253         &FOLDERID_DeviceMetadataStore,
1254         CSIDL_Type_Disallowed, /* FIXME */
1255         NULL,
1256         NULL
1257     },
1258     { /* 0x45 */
1259         &FOLDERID_Documents,
1260         CSIDL_Type_User,
1261         NULL,
1262         MAKEINTRESOURCEW(IDS_DOCUMENTS)
1263     },
1264     { /* 0x46 */
1265         &FOLDERID_DocumentsLibrary,
1266         CSIDL_Type_Disallowed, /* FIXME */
1267         NULL,
1268         NULL
1269     },
1270     { /* 0x47 */
1271         &FOLDERID_Downloads,
1272         CSIDL_Type_User,
1273         NULL,
1274         MAKEINTRESOURCEW(IDS_DOWNLOADS)
1275     },
1276     { /* 0x48 */
1277         &FOLDERID_Games,
1278         CSIDL_Type_Disallowed,
1279         NULL,
1280         NULL
1281     },
1282     { /* 0x49 */
1283         &FOLDERID_GameTasks,
1284         CSIDL_Type_Disallowed, /* FIXME */
1285         NULL,
1286         NULL
1287     },
1288     { /* 0x4a */
1289         &FOLDERID_HomeGroup,
1290         CSIDL_Type_Disallowed,
1291         NULL,
1292         NULL
1293     },
1294     { /* 0x4b */
1295         &FOLDERID_ImplicitAppShortcuts,
1296         CSIDL_Type_Disallowed, /* FIXME */
1297         NULL,
1298         NULL
1299     },
1300     { /* 0x4c */
1301         &FOLDERID_Libraries,
1302         CSIDL_Type_Disallowed, /* FIXME */
1303         NULL,
1304         NULL
1305     },
1306     { /* 0x4d */
1307         &FOLDERID_Links,
1308         CSIDL_Type_User,
1309         NULL,
1310         MAKEINTRESOURCEW(IDS_LINKS)
1311     },
1312     { /* 0x4e */
1313         &FOLDERID_LocalAppDataLow,
1314         CSIDL_Type_User,
1315         NULL,
1316         MAKEINTRESOURCEW(IDS_LOCAL_APPDATA_LOW)
1317     },
1318     { /* 0x4f */
1319         &FOLDERID_MusicLibrary,
1320         CSIDL_Type_Disallowed, /* FIXME */
1321         NULL,
1322         NULL
1323     },
1324     { /* 0x50 */
1325         &FOLDERID_OriginalImages,
1326         CSIDL_Type_Disallowed, /* FIXME */
1327         NULL,
1328         NULL
1329     },
1330     { /* 0x51 */
1331         &FOLDERID_PhotoAlbums,
1332         CSIDL_Type_User,
1333         NULL,
1334         MAKEINTRESOURCEW(IDS_PHOTO_ALBUMS)
1335     },
1336     { /* 0x52 */
1337         &FOLDERID_PicturesLibrary,
1338         CSIDL_Type_Disallowed, /* FIXME */
1339         NULL,
1340         NULL
1341     },
1342     { /* 0x53 */
1343         &FOLDERID_Playlists,
1344         CSIDL_Type_User,
1345         NULL,
1346         MAKEINTRESOURCEW(IDS_PLAYLISTS)
1347     },
1348     { /* 0x54 */
1349         &FOLDERID_ProgramFilesX64,
1350         CSIDL_Type_NonExistent,
1351         NULL,
1352         NULL
1353     },
1354     { /* 0x55 */
1355         &FOLDERID_ProgramFilesCommonX64,
1356         CSIDL_Type_NonExistent,
1357         NULL,
1358         NULL
1359     },
1360     { /* 0x56 */
1361         &FOLDERID_Public,
1362         CSIDL_Type_CurrVer, /* FIXME */
1363         NULL,
1364         UsersPublicW
1365     },
1366     { /* 0x57 */
1367         &FOLDERID_PublicDownloads,
1368         CSIDL_Type_AllUsers,
1369         NULL,
1370         MAKEINTRESOURCEW(IDS_PUBLIC_DOWNLOADS)
1371     },
1372     { /* 0x58 */
1373         &FOLDERID_PublicGameTasks,
1374         CSIDL_Type_AllUsers,
1375         NULL,
1376         MAKEINTRESOURCEW(IDS_PUBLIC_GAME_TASKS)
1377     },
1378     { /* 0x59 */
1379         &FOLDERID_PublicLibraries,
1380         CSIDL_Type_AllUsers,
1381         NULL,
1382         MAKEINTRESOURCEW(IDS_PUBLIC_LIBRARIES)
1383     },
1384     { /* 0x5a */
1385         &FOLDERID_PublicRingtones,
1386         CSIDL_Type_AllUsers,
1387         NULL,
1388         MAKEINTRESOURCEW(IDS_PUBLIC_RINGTONES)
1389     },
1390     { /* 0x5b */
1391         &FOLDERID_QuickLaunch,
1392         CSIDL_Type_Disallowed, /* FIXME */
1393         NULL,
1394         NULL
1395     },
1396     { /* 0x5c */
1397         &FOLDERID_RecordedTVLibrary,
1398         CSIDL_Type_Disallowed, /* FIXME */
1399         NULL,
1400         NULL
1401     },
1402     { /* 0x5d */
1403         &FOLDERID_Ringtones,
1404         CSIDL_Type_Disallowed, /* FIXME */
1405         NULL,
1406         NULL
1407     },
1408     { /* 0x5e */
1409         &FOLDERID_SampleMusic,
1410         CSIDL_Type_AllUsers,
1411         NULL,
1412         MAKEINTRESOURCEW(IDS_SAMPLE_MUSIC)
1413     },
1414     { /* 0x5f */
1415         &FOLDERID_SamplePictures,
1416         CSIDL_Type_AllUsers,
1417         NULL,
1418         MAKEINTRESOURCEW(IDS_SAMPLE_PICTURES)
1419     },
1420     { /* 0x60 */
1421         &FOLDERID_SamplePlaylists,
1422         CSIDL_Type_AllUsers,
1423         NULL,
1424         MAKEINTRESOURCEW(IDS_SAMPLE_PLAYLISTS)
1425     },
1426     { /* 0x61 */
1427         &FOLDERID_SampleVideos,
1428         CSIDL_Type_AllUsers,
1429         NULL,
1430         MAKEINTRESOURCEW(IDS_SAMPLE_VIDEOS)
1431     },
1432     { /* 0x62 */
1433         &FOLDERID_SavedGames,
1434         CSIDL_Type_User,
1435         NULL,
1436         MAKEINTRESOURCEW(IDS_SAVED_GAMES)
1437     },
1438     { /* 0x63 */
1439         &FOLDERID_SavedSearches,
1440         CSIDL_Type_User,
1441         NULL,
1442         MAKEINTRESOURCEW(IDS_SAVED_SEARCHES)
1443     },
1444     { /* 0x64 */
1445         &FOLDERID_SEARCH_CSC,
1446         CSIDL_Type_Disallowed,
1447         NULL,
1448         NULL
1449     },
1450     { /* 0x65 */
1451         &FOLDERID_SEARCH_MAPI,
1452         CSIDL_Type_Disallowed,
1453         NULL,
1454         NULL
1455     },
1456     { /* 0x66 */
1457         &FOLDERID_SearchHome,
1458         CSIDL_Type_Disallowed,
1459         NULL,
1460         NULL
1461     },
1462     { /* 0x67 */
1463         &FOLDERID_SidebarDefaultParts,
1464         CSIDL_Type_Disallowed, /* FIXME */
1465         NULL,
1466         NULL
1467     },
1468     { /* 0x68 */
1469         &FOLDERID_SidebarParts,
1470         CSIDL_Type_Disallowed, /* FIXME */
1471         NULL,
1472         NULL
1473     },
1474     { /* 0x69 */
1475         &FOLDERID_SyncManagerFolder,
1476         CSIDL_Type_Disallowed,
1477         NULL,
1478         NULL
1479     },
1480     { /* 0x6a */
1481         &FOLDERID_SyncResultsFolder,
1482         CSIDL_Type_Disallowed,
1483         NULL,
1484         NULL
1485     },
1486     { /* 0x6b */
1487         &FOLDERID_SyncSetupFolder,
1488         CSIDL_Type_Disallowed,
1489         NULL,
1490         NULL
1491     },
1492     { /* 0x6c */
1493         &FOLDERID_UserPinned,
1494         CSIDL_Type_Disallowed, /* FIXME */
1495         NULL,
1496         NULL
1497     },
1498     { /* 0x6d */
1499         &FOLDERID_UserProfiles,
1500         CSIDL_Type_CurrVer,
1501         UsersW,
1502         MAKEINTRESOURCEW(IDS_USER_PROFILES)
1503     },
1504     { /* 0x6e */
1505         &FOLDERID_UserProgramFiles,
1506         CSIDL_Type_Disallowed, /* FIXME */
1507         NULL,
1508         NULL
1509     },
1510     { /* 0x6f */
1511         &FOLDERID_UserProgramFilesCommon,
1512         CSIDL_Type_Disallowed, /* FIXME */
1513         NULL,
1514         NULL
1515     },
1516     { /* 0x70 */
1517         &FOLDERID_UsersFiles,
1518         CSIDL_Type_Disallowed,
1519         NULL,
1520         NULL
1521     },
1522     { /* 0x71 */
1523         &FOLDERID_UsersLibraries,
1524         CSIDL_Type_Disallowed,
1525         NULL,
1526         NULL
1527     },
1528     { /* 0x72 */
1529         &FOLDERID_VideosLibrary,
1530         CSIDL_Type_Disallowed, /* FIXME */
1531         NULL,
1532         NULL
1533     }
1534 };
1535
1536 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest);
1537
1538 /* Gets the value named value from the registry key
1539  * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
1540  * (or from rootKey\userPrefix\... if userPrefix is not NULL) into path, which
1541  * is assumed to be MAX_PATH WCHARs in length.
1542  * If it exists, expands the value and writes the expanded value to
1543  * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
1544  * Returns successful error code if the value was retrieved from the registry,
1545  * and a failure otherwise.
1546  */
1547 static HRESULT _SHGetUserShellFolderPath(HKEY rootKey, LPCWSTR userPrefix,
1548  LPCWSTR value, LPWSTR path)
1549 {
1550     HRESULT hr;
1551     WCHAR shellFolderPath[MAX_PATH], userShellFolderPath[MAX_PATH];
1552     LPCWSTR pShellFolderPath, pUserShellFolderPath;
1553     DWORD dwType, dwPathLen = MAX_PATH;
1554     HKEY userShellFolderKey, shellFolderKey;
1555
1556     TRACE("%p,%s,%s,%p\n",rootKey, debugstr_w(userPrefix), debugstr_w(value),
1557      path);
1558
1559     if (userPrefix)
1560     {
1561         strcpyW(shellFolderPath, userPrefix);
1562         PathAddBackslashW(shellFolderPath);
1563         strcatW(shellFolderPath, szSHFolders);
1564         pShellFolderPath = shellFolderPath;
1565         strcpyW(userShellFolderPath, userPrefix);
1566         PathAddBackslashW(userShellFolderPath);
1567         strcatW(userShellFolderPath, szSHUserFolders);
1568         pUserShellFolderPath = userShellFolderPath;
1569     }
1570     else
1571     {
1572         pUserShellFolderPath = szSHUserFolders;
1573         pShellFolderPath = szSHFolders;
1574     }
1575
1576     if (RegCreateKeyW(rootKey, pShellFolderPath, &shellFolderKey))
1577     {
1578         TRACE("Failed to create %s\n", debugstr_w(pShellFolderPath));
1579         return E_FAIL;
1580     }
1581     if (RegCreateKeyW(rootKey, pUserShellFolderPath, &userShellFolderKey))
1582     {
1583         TRACE("Failed to create %s\n",
1584          debugstr_w(pUserShellFolderPath));
1585         RegCloseKey(shellFolderKey);
1586         return E_FAIL;
1587     }
1588
1589     if (!RegQueryValueExW(userShellFolderKey, value, NULL, &dwType,
1590      (LPBYTE)path, &dwPathLen) && (dwType == REG_EXPAND_SZ || dwType == REG_SZ))
1591     {
1592         LONG ret;
1593
1594         path[dwPathLen / sizeof(WCHAR)] = '\0';
1595         if (dwType == REG_EXPAND_SZ && path[0] == '%')
1596         {
1597             WCHAR szTemp[MAX_PATH];
1598
1599             _SHExpandEnvironmentStrings(path, szTemp);
1600             lstrcpynW(path, szTemp, MAX_PATH);
1601         }
1602         ret = RegSetValueExW(shellFolderKey, value, 0, REG_SZ, (LPBYTE)path,
1603          (strlenW(path) + 1) * sizeof(WCHAR));
1604         if (ret != ERROR_SUCCESS)
1605             hr = HRESULT_FROM_WIN32(ret);
1606         else
1607             hr = S_OK;
1608     }
1609     else
1610         hr = E_FAIL;
1611     RegCloseKey(shellFolderKey);
1612     RegCloseKey(userShellFolderKey);
1613     TRACE("returning 0x%08x\n", hr);
1614     return hr;
1615 }
1616
1617 /* Gets a 'semi-expanded' default value of the CSIDL with index folder into
1618  * pszPath, based on the entries in CSIDL_Data.  By semi-expanded, I mean:
1619  * - The entry's szDefaultPath may be either a string value or an integer
1620  *   resource identifier.  In the latter case, the string value of the resource
1621  *   is written.
1622  * - Depending on the entry's type, the path may begin with an (unexpanded)
1623  *   environment variable name.  The caller is responsible for expanding
1624  *   environment strings if so desired.
1625  *   The types that are prepended with environment variables are:
1626  *   CSIDL_Type_User:     %USERPROFILE%
1627  *   CSIDL_Type_AllUsers: %ALLUSERSPROFILE%
1628  *   CSIDL_Type_CurrVer:  %SystemDrive%
1629  *   (Others might make sense too, but as yet are unneeded.)
1630  */
1631 static HRESULT _SHGetDefaultValue(BYTE folder, LPWSTR pszPath)
1632 {
1633     HRESULT hr;
1634     WCHAR resourcePath[MAX_PATH];
1635     LPCWSTR pDefaultPath = NULL;
1636
1637     TRACE("0x%02x,%p\n", folder, pszPath);
1638
1639     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1640         return E_INVALIDARG;
1641     if (!pszPath)
1642         return E_INVALIDARG;
1643
1644     if (!is_win64)
1645     {
1646         BOOL is_wow64;
1647
1648         switch (folder)
1649         {
1650         case CSIDL_PROGRAM_FILES:
1651         case CSIDL_PROGRAM_FILESX86:
1652             IsWow64Process( GetCurrentProcess(), &is_wow64 );
1653             folder = is_wow64 ? CSIDL_PROGRAM_FILESX86 : CSIDL_PROGRAM_FILES;
1654             break;
1655         case CSIDL_PROGRAM_FILES_COMMON:
1656         case CSIDL_PROGRAM_FILES_COMMONX86:
1657             IsWow64Process( GetCurrentProcess(), &is_wow64 );
1658             folder = is_wow64 ? CSIDL_PROGRAM_FILES_COMMONX86 : CSIDL_PROGRAM_FILES_COMMON;
1659             break;
1660         }
1661     }
1662
1663     if (CSIDL_Data[folder].szDefaultPath &&
1664      IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath))
1665     {
1666         if (LoadStringW(shell32_hInstance,
1667          LOWORD(CSIDL_Data[folder].szDefaultPath), resourcePath, MAX_PATH))
1668         {
1669             hr = S_OK;
1670             pDefaultPath = resourcePath;
1671         }
1672         else
1673         {
1674             FIXME("(%d,%s), LoadString failed, missing translation?\n", folder,
1675              debugstr_w(pszPath));
1676             hr = E_FAIL;
1677         }
1678     }
1679     else
1680     {
1681         hr = S_OK;
1682         pDefaultPath = CSIDL_Data[folder].szDefaultPath;
1683     }
1684     if (SUCCEEDED(hr))
1685     {
1686         switch (CSIDL_Data[folder].type)
1687         {
1688             case CSIDL_Type_User:
1689                 strcpyW(pszPath, UserProfileW);
1690                 break;
1691             case CSIDL_Type_AllUsers:
1692                 strcpyW(pszPath, AllUsersProfileW);
1693                 break;
1694             case CSIDL_Type_CurrVer:
1695                 strcpyW(pszPath, SystemDriveW);
1696                 break;
1697             default:
1698                 ; /* no corresponding env. var, do nothing */
1699         }
1700         if (pDefaultPath)
1701         {
1702             PathAddBackslashW(pszPath);
1703             strcatW(pszPath, pDefaultPath);
1704         }
1705     }
1706     TRACE("returning 0x%08x\n", hr);
1707     return hr;
1708 }
1709
1710 /* Gets the (unexpanded) value of the folder with index folder into pszPath.
1711  * The folder's type is assumed to be CSIDL_Type_CurrVer.  Its default value
1712  * can be overridden in the HKLM\\szCurrentVersion key.
1713  * If dwFlags has SHGFP_TYPE_DEFAULT set or if the value isn't overridden in
1714  * the registry, uses _SHGetDefaultValue to get the value.
1715  */
1716 static HRESULT _SHGetCurrentVersionPath(DWORD dwFlags, BYTE folder,
1717  LPWSTR pszPath)
1718 {
1719     HRESULT hr;
1720
1721     TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
1722
1723     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1724         return E_INVALIDARG;
1725     if (CSIDL_Data[folder].type != CSIDL_Type_CurrVer)
1726         return E_INVALIDARG;
1727     if (!pszPath)
1728         return E_INVALIDARG;
1729
1730     if (dwFlags & SHGFP_TYPE_DEFAULT)
1731         hr = _SHGetDefaultValue(folder, pszPath);
1732     else
1733     {
1734         HKEY hKey;
1735
1736         if (RegCreateKeyW(HKEY_LOCAL_MACHINE, szCurrentVersion, &hKey))
1737             hr = E_FAIL;
1738         else
1739         {
1740             DWORD dwType, dwPathLen = MAX_PATH * sizeof(WCHAR);
1741
1742             if (RegQueryValueExW(hKey, CSIDL_Data[folder].szValueName, NULL,
1743              &dwType, (LPBYTE)pszPath, &dwPathLen) ||
1744              (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
1745             {
1746                 hr = _SHGetDefaultValue(folder, pszPath);
1747                 dwType = REG_EXPAND_SZ;
1748                 switch (folder)
1749                 {
1750                 case CSIDL_PROGRAM_FILESX86:
1751                 case CSIDL_PROGRAM_FILES_COMMONX86:
1752                     /* these two should never be set on 32-bit setups */
1753                     if (!is_win64)
1754                     {
1755                         BOOL is_wow64;
1756                         IsWow64Process( GetCurrentProcess(), &is_wow64 );
1757                         if (!is_wow64) break;
1758                     }
1759                     /* fall through */
1760                 default:
1761                     RegSetValueExW(hKey, CSIDL_Data[folder].szValueName, 0, dwType,
1762                                    (LPBYTE)pszPath, (strlenW(pszPath)+1)*sizeof(WCHAR));
1763                 }
1764             }
1765             else
1766             {
1767                 pszPath[dwPathLen / sizeof(WCHAR)] = '\0';
1768                 hr = S_OK;
1769             }
1770             RegCloseKey(hKey);
1771         }
1772     }
1773     TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1774     return hr;
1775 }
1776
1777 static LPWSTR _GetUserSidStringFromToken(HANDLE Token)
1778 {
1779     char InfoBuffer[64];
1780     PTOKEN_USER UserInfo;
1781     DWORD InfoSize;
1782     LPWSTR SidStr;
1783
1784     UserInfo = (PTOKEN_USER) InfoBuffer;
1785     if (! GetTokenInformation(Token, TokenUser, InfoBuffer, sizeof(InfoBuffer),
1786                               &InfoSize))
1787     {
1788         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1789             return NULL;
1790         UserInfo = HeapAlloc(GetProcessHeap(), 0, InfoSize);
1791         if (UserInfo == NULL)
1792             return NULL;
1793         if (! GetTokenInformation(Token, TokenUser, UserInfo, InfoSize,
1794                                   &InfoSize))
1795         {
1796             HeapFree(GetProcessHeap(), 0, UserInfo);
1797             return NULL;
1798         }
1799     }
1800
1801     if (! ConvertSidToStringSidW(UserInfo->User.Sid, &SidStr))
1802         SidStr = NULL;
1803
1804     if (UserInfo != (PTOKEN_USER) InfoBuffer)
1805         HeapFree(GetProcessHeap(), 0, UserInfo);
1806
1807     return SidStr;
1808 }
1809
1810 /* Gets the user's path (unexpanded) for the CSIDL with index folder:
1811  * If SHGFP_TYPE_DEFAULT is set, calls _SHGetDefaultValue for it.  Otherwise
1812  * calls _SHGetUserShellFolderPath for it.  Where it looks depends on hToken:
1813  * - if hToken is -1, looks in HKEY_USERS\.Default
1814  * - otherwise looks first in HKEY_CURRENT_USER, followed by HKEY_LOCAL_MACHINE
1815  *   if HKEY_CURRENT_USER doesn't contain any entries.  If both fail, finally
1816  *   calls _SHGetDefaultValue for it.
1817  */
1818 static HRESULT _SHGetUserProfilePath(HANDLE hToken, DWORD dwFlags, BYTE folder,
1819  LPWSTR pszPath)
1820 {
1821     HRESULT hr;
1822
1823     TRACE("%p,0x%08x,0x%02x,%p\n", hToken, dwFlags, folder, pszPath);
1824
1825     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1826         return E_INVALIDARG;
1827     if (CSIDL_Data[folder].type != CSIDL_Type_User)
1828         return E_INVALIDARG;
1829     if (!pszPath)
1830         return E_INVALIDARG;
1831
1832     if (dwFlags & SHGFP_TYPE_DEFAULT)
1833     {
1834         if (hToken != NULL && hToken != (HANDLE)-1)
1835         {
1836             FIXME("unsupported for user other than current or default\n");
1837             return E_FAIL;
1838         }
1839         hr = _SHGetDefaultValue(folder, pszPath);
1840     }
1841     else
1842     {
1843         LPCWSTR userPrefix = NULL;
1844         HKEY hRootKey;
1845
1846         if (hToken == (HANDLE)-1)
1847         {
1848             hRootKey = HKEY_USERS;
1849             userPrefix = DefaultW;
1850         }
1851         else if (hToken == NULL)
1852             hRootKey = HKEY_CURRENT_USER;
1853         else
1854         {
1855             hRootKey = HKEY_USERS;
1856             userPrefix = _GetUserSidStringFromToken(hToken);
1857             if (userPrefix == NULL)
1858             {
1859                 hr = E_FAIL;
1860                 goto error;
1861             }
1862         }
1863         hr = _SHGetUserShellFolderPath(hRootKey, userPrefix,
1864          CSIDL_Data[folder].szValueName, pszPath);
1865         if (FAILED(hr) && hRootKey != HKEY_LOCAL_MACHINE)
1866             hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL,
1867              CSIDL_Data[folder].szValueName, pszPath);
1868         if (FAILED(hr))
1869             hr = _SHGetDefaultValue(folder, pszPath);
1870         if (userPrefix != NULL && userPrefix != DefaultW)
1871             LocalFree((HLOCAL) userPrefix);
1872     }
1873 error:
1874     TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1875     return hr;
1876 }
1877
1878 /* Gets the (unexpanded) path for the CSIDL with index folder.  If dwFlags has
1879  * SHGFP_TYPE_DEFAULT set, calls _SHGetDefaultValue.  Otherwise calls
1880  * _SHGetUserShellFolderPath for it, looking only in HKEY_LOCAL_MACHINE.
1881  * If this fails, falls back to _SHGetDefaultValue.
1882  */
1883 static HRESULT _SHGetAllUsersProfilePath(DWORD dwFlags, BYTE folder,
1884  LPWSTR pszPath)
1885 {
1886     HRESULT hr;
1887
1888     TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
1889
1890     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1891         return E_INVALIDARG;
1892     if (CSIDL_Data[folder].type != CSIDL_Type_AllUsers)
1893         return E_INVALIDARG;
1894     if (!pszPath)
1895         return E_INVALIDARG;
1896
1897     if (dwFlags & SHGFP_TYPE_DEFAULT)
1898         hr = _SHGetDefaultValue(folder, pszPath);
1899     else
1900     {
1901         hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL,
1902          CSIDL_Data[folder].szValueName, pszPath);
1903         if (FAILED(hr))
1904             hr = _SHGetDefaultValue(folder, pszPath);
1905     }
1906     TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1907     return hr;
1908 }
1909
1910 static HRESULT _SHOpenProfilesKey(PHKEY pKey)
1911 {
1912     LONG lRet;
1913     DWORD disp;
1914
1915     lRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, ProfileListW, 0, NULL, 0,
1916      KEY_ALL_ACCESS, NULL, pKey, &disp);
1917     return HRESULT_FROM_WIN32(lRet);
1918 }
1919
1920 /* Reads the value named szValueName from the key profilesKey (assumed to be
1921  * opened by _SHOpenProfilesKey) into szValue, which is assumed to be MAX_PATH
1922  * WCHARs in length.  If it doesn't exist, returns szDefault (and saves
1923  * szDefault to the registry).
1924  */
1925 static HRESULT _SHGetProfilesValue(HKEY profilesKey, LPCWSTR szValueName,
1926  LPWSTR szValue, LPCWSTR szDefault)
1927 {
1928     HRESULT hr;
1929     DWORD type, dwPathLen = MAX_PATH * sizeof(WCHAR);
1930     LONG lRet;
1931
1932     TRACE("%p,%s,%p,%s\n", profilesKey, debugstr_w(szValueName), szValue,
1933      debugstr_w(szDefault));
1934     lRet = RegQueryValueExW(profilesKey, szValueName, NULL, &type,
1935      (LPBYTE)szValue, &dwPathLen);
1936     if (!lRet && (type == REG_SZ || type == REG_EXPAND_SZ) && dwPathLen
1937      && *szValue)
1938     {
1939         dwPathLen /= sizeof(WCHAR);
1940         szValue[dwPathLen] = '\0';
1941         hr = S_OK;
1942     }
1943     else
1944     {
1945         /* Missing or invalid value, set a default */
1946         lstrcpynW(szValue, szDefault, MAX_PATH);
1947         TRACE("Setting missing value %s to %s\n", debugstr_w(szValueName),
1948                                                   debugstr_w(szValue));
1949         lRet = RegSetValueExW(profilesKey, szValueName, 0, REG_EXPAND_SZ,
1950                               (LPBYTE)szValue,
1951                               (strlenW(szValue) + 1) * sizeof(WCHAR));
1952         if (lRet)
1953             hr = HRESULT_FROM_WIN32(lRet);
1954         else
1955             hr = S_OK;
1956     }
1957     TRACE("returning 0x%08x (output value is %s)\n", hr, debugstr_w(szValue));
1958     return hr;
1959 }
1960
1961 /* Attempts to expand environment variables from szSrc into szDest, which is
1962  * assumed to be MAX_PATH characters in length.  Before referring to the
1963  * environment, handles a few variables directly, because the environment
1964  * variables may not be set when this is called (as during Wine's installation
1965  * when default values are being written to the registry).
1966  * The directly handled environment variables, and their source, are:
1967  * - ALLUSERSPROFILE, USERPROFILE: reads from the registry
1968  * - SystemDrive: uses GetSystemDirectoryW and uses the drive portion of its
1969  *   path
1970  * If one of the directly handled environment variables is expanded, only
1971  * expands a single variable, and only in the beginning of szSrc.
1972  */
1973 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest)
1974 {
1975     HRESULT hr;
1976     WCHAR szTemp[MAX_PATH], szProfilesPrefix[MAX_PATH] = { 0 };
1977     HKEY key = NULL;
1978
1979     TRACE("%s, %p\n", debugstr_w(szSrc), szDest);
1980
1981     if (!szSrc || !szDest) return E_INVALIDARG;
1982
1983     /* short-circuit if there's nothing to expand */
1984     if (szSrc[0] != '%')
1985     {
1986         strcpyW(szDest, szSrc);
1987         hr = S_OK;
1988         goto end;
1989     }
1990     /* Get the profile prefix, we'll probably be needing it */
1991     hr = _SHOpenProfilesKey(&key);
1992     if (SUCCEEDED(hr))
1993     {
1994         WCHAR def_val[MAX_PATH];
1995
1996         /* get the system drive */
1997         GetSystemDirectoryW(def_val, MAX_PATH);
1998         if (def_val[1] == ':') strcpyW( def_val + 3, szDefaultProfileDirW );
1999         else FIXME("non-drive system paths unsupported\n");
2000
2001         hr = _SHGetProfilesValue(key, ProfilesDirectoryW, szProfilesPrefix, def_val );
2002     }
2003
2004     *szDest = 0;
2005     strcpyW(szTemp, szSrc);
2006     while (SUCCEEDED(hr) && szTemp[0] == '%')
2007     {
2008         if (!strncmpiW(szTemp, AllUsersProfileW, strlenW(AllUsersProfileW)))
2009         {
2010             WCHAR szAllUsers[MAX_PATH];
2011
2012             strcpyW(szDest, szProfilesPrefix);
2013             hr = _SHGetProfilesValue(key, AllUsersProfileValueW,
2014              szAllUsers, AllUsersW);
2015             PathAppendW(szDest, szAllUsers);
2016             PathAppendW(szDest, szTemp + strlenW(AllUsersProfileW));
2017         }
2018         else if (!strncmpiW(szTemp, UserProfileW, strlenW(UserProfileW)))
2019         {
2020             WCHAR userName[MAX_PATH];
2021             DWORD userLen = MAX_PATH;
2022
2023             strcpyW(szDest, szProfilesPrefix);
2024             GetUserNameW(userName, &userLen);
2025             PathAppendW(szDest, userName);
2026             PathAppendW(szDest, szTemp + strlenW(UserProfileW));
2027         }
2028         else if (!strncmpiW(szTemp, SystemDriveW, strlenW(SystemDriveW)))
2029         {
2030             GetSystemDirectoryW(szDest, MAX_PATH);
2031             if (szDest[1] != ':')
2032             {
2033                 FIXME("non-drive system paths unsupported\n");
2034                 hr = E_FAIL;
2035             }
2036             else
2037             {
2038                 strcpyW(szDest + 3, szTemp + strlenW(SystemDriveW) + 1);
2039                 hr = S_OK;
2040             }
2041         }
2042         else
2043         {
2044             DWORD ret = ExpandEnvironmentStringsW(szSrc, szDest, MAX_PATH);
2045
2046             if (ret > MAX_PATH)
2047                 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
2048             else if (ret == 0)
2049                 hr = HRESULT_FROM_WIN32(GetLastError());
2050             else
2051                 hr = S_OK;
2052         }
2053         if (SUCCEEDED(hr) && szDest[0] == '%')
2054             strcpyW(szTemp, szDest);
2055         else
2056         {
2057             /* terminate loop */
2058             szTemp[0] = '\0';
2059         }
2060     }
2061 end:
2062     if (key)
2063         RegCloseKey(key);
2064     TRACE("returning 0x%08x (input was %s, output is %s)\n", hr,
2065      debugstr_w(szSrc), debugstr_w(szDest));
2066     return hr;
2067 }
2068
2069 /*************************************************************************
2070  * SHGetFolderPathW                     [SHELL32.@]
2071  *
2072  * Convert nFolder to path.  
2073  *
2074  * RETURNS
2075  *  Success: S_OK
2076  *  Failure: standard HRESULT error codes.
2077  *
2078  * NOTES
2079  * Most values can be overridden in either
2080  * HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
2081  * or in the same location in HKLM.
2082  * The "Shell Folders" registry key was used in NT4 and earlier systems.
2083  * Beginning with Windows 2000, the "User Shell Folders" key is used, so
2084  * changes made to it are made to the former key too.  This synchronization is
2085  * done on-demand: not until someone requests the value of one of these paths
2086  * (by calling one of the SHGet functions) is the value synchronized.
2087  * Furthermore, the HKCU paths take precedence over the HKLM paths.
2088  */
2089 HRESULT WINAPI SHGetFolderPathW(
2090         HWND hwndOwner,    /* [I] owner window */
2091         int nFolder,       /* [I] CSIDL identifying the folder */
2092         HANDLE hToken,     /* [I] access token */
2093         DWORD dwFlags,     /* [I] which path to return */
2094         LPWSTR pszPath)    /* [O] converted path */
2095 {
2096     HRESULT hr =  SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, NULL, pszPath);
2097     if(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
2098         hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
2099     return hr;
2100 }
2101
2102 HRESULT WINAPI SHGetFolderPathAndSubDirA(
2103         HWND hwndOwner,    /* [I] owner window */
2104         int nFolder,       /* [I] CSIDL identifying the folder */
2105         HANDLE hToken,     /* [I] access token */
2106         DWORD dwFlags,     /* [I] which path to return */
2107         LPCSTR pszSubPath, /* [I] sub directory of the specified folder */
2108         LPSTR pszPath)     /* [O] converted path */
2109 {
2110     int length;
2111     HRESULT hr = S_OK;
2112     LPWSTR pszSubPathW = NULL;
2113     LPWSTR pszPathW = NULL;
2114     TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
2115
2116     if(pszPath) {
2117         pszPathW = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
2118         if(!pszPathW) {
2119             hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
2120             goto cleanup;
2121         }
2122     }
2123     TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
2124
2125     /* SHGetFolderPathAndSubDirW does not distinguish if pszSubPath isn't
2126      * set (null), or an empty string.therefore call it without the parameter set
2127      * if pszSubPath is an empty string
2128      */
2129     if (pszSubPath && pszSubPath[0]) {
2130         length = MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, NULL, 0);
2131         pszSubPathW = HeapAlloc(GetProcessHeap(), 0, length * sizeof(WCHAR));
2132         if(!pszSubPathW) {
2133             hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
2134             goto cleanup;
2135         }
2136         MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, pszSubPathW, length);
2137     }
2138
2139     hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, pszSubPathW, pszPathW);
2140
2141     if (SUCCEEDED(hr) && pszPath)
2142         WideCharToMultiByte(CP_ACP, 0, pszPathW, -1, pszPath, MAX_PATH, NULL, NULL);
2143
2144 cleanup:
2145     HeapFree(GetProcessHeap(), 0, pszPathW);
2146     HeapFree(GetProcessHeap(), 0, pszSubPathW);
2147     return hr;
2148 }
2149
2150 /*************************************************************************
2151  * SHGetFolderPathAndSubDirW            [SHELL32.@]
2152  */
2153 HRESULT WINAPI SHGetFolderPathAndSubDirW(
2154         HWND hwndOwner,    /* [I] owner window */
2155         int nFolder,       /* [I] CSIDL identifying the folder */
2156         HANDLE hToken,     /* [I] access token */
2157         DWORD dwFlags,     /* [I] which path to return */
2158         LPCWSTR pszSubPath,/* [I] sub directory of the specified folder */
2159         LPWSTR pszPath)    /* [O] converted path */
2160 {
2161     HRESULT    hr;
2162     WCHAR      szBuildPath[MAX_PATH], szTemp[MAX_PATH];
2163     DWORD      folder = nFolder & CSIDL_FOLDER_MASK;
2164     CSIDL_Type type;
2165     int        ret;
2166     
2167     TRACE("%p,%p,nFolder=0x%04x,%s\n", hwndOwner,pszPath,nFolder,debugstr_w(pszSubPath));
2168
2169     /* Windows always NULL-terminates the resulting path regardless of success
2170      * or failure, so do so first
2171      */
2172     if (pszPath)
2173         *pszPath = '\0';
2174
2175     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
2176         return E_INVALIDARG;
2177     if ((SHGFP_TYPE_CURRENT != dwFlags) && (SHGFP_TYPE_DEFAULT != dwFlags))
2178         return E_INVALIDARG;
2179     szTemp[0] = 0;
2180     type = CSIDL_Data[folder].type;
2181     switch (type)
2182     {
2183         case CSIDL_Type_Disallowed:
2184             hr = E_INVALIDARG;
2185             break;
2186         case CSIDL_Type_NonExistent:
2187             hr = S_FALSE;
2188             break;
2189         case CSIDL_Type_WindowsPath:
2190             GetWindowsDirectoryW(szTemp, MAX_PATH);
2191             if (CSIDL_Data[folder].szDefaultPath &&
2192              !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
2193              *CSIDL_Data[folder].szDefaultPath)
2194             {
2195                 PathAddBackslashW(szTemp);
2196                 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
2197             }
2198             hr = S_OK;
2199             break;
2200         case CSIDL_Type_SystemPath:
2201             GetSystemDirectoryW(szTemp, MAX_PATH);
2202             if (CSIDL_Data[folder].szDefaultPath &&
2203              !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
2204              *CSIDL_Data[folder].szDefaultPath)
2205             {
2206                 PathAddBackslashW(szTemp);
2207                 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
2208             }
2209             hr = S_OK;
2210             break;
2211         case CSIDL_Type_SystemX86Path:
2212             if (!GetSystemWow64DirectoryW(szTemp, MAX_PATH)) GetSystemDirectoryW(szTemp, MAX_PATH);
2213             if (CSIDL_Data[folder].szDefaultPath &&
2214              !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
2215              *CSIDL_Data[folder].szDefaultPath)
2216             {
2217                 PathAddBackslashW(szTemp);
2218                 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
2219             }
2220             hr = S_OK;
2221             break;
2222         case CSIDL_Type_CurrVer:
2223             hr = _SHGetCurrentVersionPath(dwFlags, folder, szTemp);
2224             break;
2225         case CSIDL_Type_User:
2226             hr = _SHGetUserProfilePath(hToken, dwFlags, folder, szTemp);
2227             break;
2228         case CSIDL_Type_AllUsers:
2229             hr = _SHGetAllUsersProfilePath(dwFlags, folder, szTemp);
2230             break;
2231         default:
2232             FIXME("bogus type %d, please fix\n", type);
2233             hr = E_INVALIDARG;
2234             break;
2235     }
2236
2237     /* Expand environment strings if necessary */
2238     if (*szTemp == '%')
2239         hr = _SHExpandEnvironmentStrings(szTemp, szBuildPath);
2240     else
2241         strcpyW(szBuildPath, szTemp);
2242
2243     if (FAILED(hr)) goto end;
2244
2245     if(pszSubPath) {
2246         /* make sure the new path does not exceed th bufferlength
2247          * rememebr to backslash and the termination */
2248         if(MAX_PATH < (lstrlenW(szBuildPath) + lstrlenW(pszSubPath) + 2)) {
2249             hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
2250             goto end;
2251         }
2252         PathAppendW(szBuildPath, pszSubPath);
2253         PathRemoveBackslashW(szBuildPath);
2254     }
2255     /* Copy the path if it's available before we might return */
2256     if (SUCCEEDED(hr) && pszPath)
2257         strcpyW(pszPath, szBuildPath);
2258
2259     /* if we don't care about existing directories we are ready */
2260     if(nFolder & CSIDL_FLAG_DONT_VERIFY) goto end;
2261
2262     if (PathFileExistsW(szBuildPath)) goto end;
2263
2264     /* not existing but we are not allowed to create it.  The return value
2265      * is verified against shell32 version 6.0.
2266      */
2267     if (!(nFolder & CSIDL_FLAG_CREATE))
2268     {
2269         hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
2270         goto end;
2271     }
2272
2273     /* create directory/directories */
2274     ret = SHCreateDirectoryExW(hwndOwner, szBuildPath, NULL);
2275     if (ret && ret != ERROR_ALREADY_EXISTS)
2276     {
2277         ERR("Failed to create directory %s.\n", debugstr_w(szBuildPath));
2278         hr = E_FAIL;
2279         goto end;
2280     }
2281
2282     TRACE("Created missing system directory %s\n", debugstr_w(szBuildPath));
2283 end:
2284     TRACE("returning 0x%08x (final path is %s)\n", hr, debugstr_w(szBuildPath));
2285     return hr;
2286 }
2287
2288 /*************************************************************************
2289  * SHGetFolderPathA                     [SHELL32.@]
2290  *
2291  * See SHGetFolderPathW.
2292  */
2293 HRESULT WINAPI SHGetFolderPathA(
2294         HWND hwndOwner,
2295         int nFolder,
2296         HANDLE hToken,
2297         DWORD dwFlags,
2298         LPSTR pszPath)
2299 {
2300     WCHAR szTemp[MAX_PATH];
2301     HRESULT hr;
2302
2303     TRACE("%p,%p,nFolder=0x%04x\n",hwndOwner,pszPath,nFolder);
2304
2305     if (pszPath)
2306         *pszPath = '\0';
2307     hr = SHGetFolderPathW(hwndOwner, nFolder, hToken, dwFlags, szTemp);
2308     if (SUCCEEDED(hr) && pszPath)
2309         WideCharToMultiByte(CP_ACP, 0, szTemp, -1, pszPath, MAX_PATH, NULL,
2310          NULL);
2311
2312     return hr;
2313 }
2314
2315 /* For each folder in folders, if its value has not been set in the registry,
2316  * calls _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the
2317  * folder's type) to get the unexpanded value first.
2318  * Writes the unexpanded value to User Shell Folders, and queries it with
2319  * SHGetFolderPathW to force the creation of the directory if it doesn't
2320  * already exist.  SHGetFolderPathW also returns the expanded value, which
2321  * this then writes to Shell Folders.
2322  */
2323 static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken,
2324  LPCWSTR szUserShellFolderPath, LPCWSTR szShellFolderPath, const UINT folders[],
2325  UINT foldersLen)
2326 {
2327     UINT i;
2328     WCHAR path[MAX_PATH];
2329     HRESULT hr = S_OK;
2330     HKEY hUserKey = NULL, hKey = NULL;
2331     DWORD dwType, dwPathLen;
2332     LONG ret;
2333
2334     TRACE("%p,%p,%s,%p,%u\n", hRootKey, hToken,
2335      debugstr_w(szUserShellFolderPath), folders, foldersLen);
2336
2337     ret = RegCreateKeyW(hRootKey, szUserShellFolderPath, &hUserKey);
2338     if (ret)
2339         hr = HRESULT_FROM_WIN32(ret);
2340     else
2341     {
2342         ret = RegCreateKeyW(hRootKey, szShellFolderPath, &hKey);
2343         if (ret)
2344             hr = HRESULT_FROM_WIN32(ret);
2345     }
2346     for (i = 0; SUCCEEDED(hr) && i < foldersLen; i++)
2347     {
2348         dwPathLen = MAX_PATH * sizeof(WCHAR);
2349         if (RegQueryValueExW(hUserKey, CSIDL_Data[folders[i]].szValueName, NULL,
2350          &dwType, (LPBYTE)path, &dwPathLen) || (dwType != REG_SZ &&
2351          dwType != REG_EXPAND_SZ))
2352         {
2353             *path = '\0';
2354             if (CSIDL_Data[folders[i]].type == CSIDL_Type_User)
2355                 _SHGetUserProfilePath(hToken, SHGFP_TYPE_DEFAULT, folders[i],
2356                  path);
2357             else if (CSIDL_Data[folders[i]].type == CSIDL_Type_AllUsers)
2358                 _SHGetAllUsersProfilePath(SHGFP_TYPE_DEFAULT, folders[i], path);
2359             else if (CSIDL_Data[folders[i]].type == CSIDL_Type_WindowsPath)
2360             {
2361                 GetWindowsDirectoryW(path, MAX_PATH);
2362                 if (CSIDL_Data[folders[i]].szDefaultPath &&
2363                     !IS_INTRESOURCE(CSIDL_Data[folders[i]].szDefaultPath))
2364                 {
2365                     PathAddBackslashW(path);
2366                     strcatW(path, CSIDL_Data[folders[i]].szDefaultPath);
2367                 }
2368             }
2369             else
2370                 hr = E_FAIL;
2371             if (*path)
2372             {
2373                 ret = RegSetValueExW(hUserKey,
2374                  CSIDL_Data[folders[i]].szValueName, 0, REG_EXPAND_SZ,
2375                  (LPBYTE)path, (strlenW(path) + 1) * sizeof(WCHAR));
2376                 if (ret)
2377                     hr = HRESULT_FROM_WIN32(ret);
2378                 else
2379                 {
2380                     hr = SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
2381                      hToken, SHGFP_TYPE_DEFAULT, path);
2382                     ret = RegSetValueExW(hKey,
2383                      CSIDL_Data[folders[i]].szValueName, 0, REG_SZ,
2384                      (LPBYTE)path, (strlenW(path) + 1) * sizeof(WCHAR));
2385                     if (ret)
2386                         hr = HRESULT_FROM_WIN32(ret);
2387                 }
2388             }
2389         }
2390     }
2391     if (hUserKey)
2392         RegCloseKey(hUserKey);
2393     if (hKey)
2394         RegCloseKey(hKey);
2395
2396     TRACE("returning 0x%08x\n", hr);
2397     return hr;
2398 }
2399
2400 static HRESULT _SHRegisterUserShellFolders(BOOL bDefault)
2401 {
2402     static const UINT folders[] = {
2403      CSIDL_PROGRAMS,
2404      CSIDL_PERSONAL,
2405      CSIDL_FAVORITES,
2406      CSIDL_APPDATA,
2407      CSIDL_STARTUP,
2408      CSIDL_RECENT,
2409      CSIDL_SENDTO,
2410      CSIDL_STARTMENU,
2411      CSIDL_MYMUSIC,
2412      CSIDL_MYVIDEO,
2413      CSIDL_DESKTOPDIRECTORY,
2414      CSIDL_NETHOOD,
2415      CSIDL_TEMPLATES,
2416      CSIDL_PRINTHOOD,
2417      CSIDL_LOCAL_APPDATA,
2418      CSIDL_INTERNET_CACHE,
2419      CSIDL_COOKIES,
2420      CSIDL_HISTORY,
2421      CSIDL_MYPICTURES,
2422      CSIDL_FONTS
2423     };
2424     WCHAR userShellFolderPath[MAX_PATH], shellFolderPath[MAX_PATH];
2425     LPCWSTR pUserShellFolderPath, pShellFolderPath;
2426     HRESULT hr = S_OK;
2427     HKEY hRootKey;
2428     HANDLE hToken;
2429
2430     TRACE("%s\n", bDefault ? "TRUE" : "FALSE");
2431     if (bDefault)
2432     {
2433         hToken = (HANDLE)-1;
2434         hRootKey = HKEY_USERS;
2435         strcpyW(userShellFolderPath, DefaultW);
2436         PathAddBackslashW(userShellFolderPath);
2437         strcatW(userShellFolderPath, szSHUserFolders);
2438         pUserShellFolderPath = userShellFolderPath;
2439         strcpyW(shellFolderPath, DefaultW);
2440         PathAddBackslashW(shellFolderPath);
2441         strcatW(shellFolderPath, szSHFolders);
2442         pShellFolderPath = shellFolderPath;
2443     }
2444     else
2445     {
2446         hToken = NULL;
2447         hRootKey = HKEY_CURRENT_USER;
2448         pUserShellFolderPath = szSHUserFolders;
2449         pShellFolderPath = szSHFolders;
2450     }
2451
2452     hr = _SHRegisterFolders(hRootKey, hToken, pUserShellFolderPath,
2453      pShellFolderPath, folders, sizeof(folders) / sizeof(folders[0]));
2454     TRACE("returning 0x%08x\n", hr);
2455     return hr;
2456 }
2457
2458 static HRESULT _SHRegisterCommonShellFolders(void)
2459 {
2460     static const UINT folders[] = {
2461      CSIDL_COMMON_STARTMENU,
2462      CSIDL_COMMON_PROGRAMS,
2463      CSIDL_COMMON_STARTUP,
2464      CSIDL_COMMON_DESKTOPDIRECTORY,
2465      CSIDL_COMMON_FAVORITES,
2466      CSIDL_COMMON_APPDATA,
2467      CSIDL_COMMON_TEMPLATES,
2468      CSIDL_COMMON_DOCUMENTS,
2469      CSIDL_COMMON_ADMINTOOLS,
2470      CSIDL_COMMON_MUSIC,
2471      CSIDL_COMMON_PICTURES,
2472      CSIDL_COMMON_VIDEO,
2473     };
2474     HRESULT hr;
2475
2476     TRACE("\n");
2477     hr = _SHRegisterFolders(HKEY_LOCAL_MACHINE, NULL, szSHUserFolders,
2478      szSHFolders, folders, sizeof(folders) / sizeof(folders[0]));
2479     TRACE("returning 0x%08x\n", hr);
2480     return hr;
2481 }
2482
2483 /******************************************************************************
2484  * _SHAppendToUnixPath  [Internal]
2485  *
2486  * Helper function for _SHCreateSymbolicLinks. Appends pwszSubPath (or the 
2487  * corresponding resource, if IS_INTRESOURCE) to the unix base path 'szBasePath' 
2488  * and replaces backslashes with slashes.
2489  *
2490  * PARAMS
2491  *  szBasePath  [IO] The unix base path, which will be appended to (CP_UNXICP).
2492  *  pwszSubPath [I]  Sub-path or resource id (use MAKEINTRESOURCEW).
2493  *
2494  * RETURNS
2495  *  Success: TRUE,
2496  *  Failure: FALSE
2497  */
2498 static inline BOOL _SHAppendToUnixPath(char *szBasePath, LPCWSTR pwszSubPath) {
2499     WCHAR wszSubPath[MAX_PATH];
2500     int cLen = strlen(szBasePath);
2501     char *pBackslash;
2502
2503     if (IS_INTRESOURCE(pwszSubPath)) {
2504         if (!LoadStringW(shell32_hInstance, LOWORD(pwszSubPath), wszSubPath, MAX_PATH)) {
2505             /* Fall back to hard coded defaults. */
2506             switch (LOWORD(pwszSubPath)) {
2507                 case IDS_PERSONAL:
2508                     lstrcpyW(wszSubPath, PersonalW);
2509                     break;
2510                 case IDS_MYMUSIC:
2511                     lstrcpyW(wszSubPath, My_MusicW);
2512                     break;
2513                 case IDS_MYPICTURES:
2514                     lstrcpyW(wszSubPath, My_PicturesW);
2515                     break;
2516                 case IDS_MYVIDEO:
2517                     lstrcpyW(wszSubPath, My_VideoW);
2518                     break;
2519                 default:
2520                     ERR("LoadString(%d) failed!\n", LOWORD(pwszSubPath));
2521                     return FALSE;
2522             }
2523         }
2524     } else {
2525         lstrcpyW(wszSubPath, pwszSubPath);
2526     }
2527  
2528     if (szBasePath[cLen-1] != '/') szBasePath[cLen++] = '/';
2529  
2530     if (!WideCharToMultiByte(CP_UNIXCP, 0, wszSubPath, -1, szBasePath + cLen,
2531                              FILENAME_MAX - cLen, NULL, NULL))
2532     {
2533         return FALSE;
2534     }
2535  
2536     pBackslash = szBasePath + cLen;
2537     while ((pBackslash = strchr(pBackslash, '\\'))) *pBackslash = '/';
2538  
2539     return TRUE;
2540 }
2541
2542 /******************************************************************************
2543  * _SHCreateSymbolicLinks  [Internal]
2544  * 
2545  * Sets up symbol links for various shell folders to point into the users home
2546  * directory. We do an educated guess about what the user would probably want:
2547  * - If there is a 'My Documents' directory in $HOME, the user probably wants
2548  *   wine's 'My Documents' to point there. Furthermore, we imply that the user
2549  *   is a Windows lover and has no problem with wine creating 'My Pictures',
2550  *   'My Music' and 'My Video' subfolders under '$HOME/My Documents', if those
2551  *   do not already exits. We put appropriate symbolic links in place for those,
2552  *   too.
2553  * - If there is no 'My Documents' directory in $HOME, we let 'My Documents'
2554  *   point directly to $HOME. We assume the user to be a unix hacker who does not
2555  *   want wine to create anything anywhere besides the .wine directory. So, if
2556  *   there already is a 'My Music' directory in $HOME, we symlink the 'My Music'
2557  *   shell folder to it. But if not, then we check XDG_MUSIC_DIR - "well known"
2558  *   directory, and try to link to that. If that fails, then we symlink to
2559  *   $HOME directly. The same holds fo 'My Pictures' and 'My Video'.
2560  * - The Desktop shell folder is symlinked to XDG_DESKTOP_DIR. If that does not
2561  *   exist, then we try '$HOME/Desktop'. If that does not exist, then we leave
2562  *   it alone.
2563  * ('My Music',... above in fact means LoadString(IDS_MYMUSIC))
2564  */
2565 static void _SHCreateSymbolicLinks(void)
2566 {
2567     UINT aidsMyStuff[] = { IDS_MYPICTURES, IDS_MYVIDEO, IDS_MYMUSIC }, i;
2568     int acsidlMyStuff[] = { CSIDL_MYPICTURES, CSIDL_MYVIDEO, CSIDL_MYMUSIC };
2569     static const char * const xdg_dirs[] = { "PICTURES", "VIDEOS", "MUSIC", "DESKTOP" };
2570     static const unsigned int num = sizeof(xdg_dirs) / sizeof(xdg_dirs[0]);
2571     WCHAR wszTempPath[MAX_PATH];
2572     char szPersonalTarget[FILENAME_MAX], *pszPersonal;
2573     char szMyStuffTarget[FILENAME_MAX], *pszMyStuff;
2574     char szDesktopTarget[FILENAME_MAX], *pszDesktop;
2575     struct stat statFolder;
2576     const char *pszHome;
2577     HRESULT hr;
2578     char ** xdg_results;
2579     char * xdg_desktop_dir;
2580
2581     /* Create all necessary profile sub-dirs up to 'My Documents' and get the unix path. */
2582     hr = SHGetFolderPathW(NULL, CSIDL_PERSONAL|CSIDL_FLAG_CREATE, NULL,
2583                           SHGFP_TYPE_DEFAULT, wszTempPath);
2584     if (FAILED(hr)) return;
2585     pszPersonal = wine_get_unix_file_name(wszTempPath);
2586     if (!pszPersonal) return;
2587
2588     hr = XDG_UserDirLookup(xdg_dirs, num, &xdg_results);
2589     if (FAILED(hr)) xdg_results = NULL;
2590
2591     pszHome = getenv("HOME");
2592     if (pszHome && !stat(pszHome, &statFolder) && S_ISDIR(statFolder.st_mode)) {
2593         strcpy(szPersonalTarget, pszHome);
2594         if (_SHAppendToUnixPath(szPersonalTarget, MAKEINTRESOURCEW(IDS_PERSONAL)) &&
2595             !stat(szPersonalTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
2596         {
2597             /* '$HOME/My Documents' exists. Create 'My Pictures', 'My Videos' and 
2598              * 'My Music' subfolders or fail silently if they already exist. */
2599             for (i = 0; i < sizeof(aidsMyStuff)/sizeof(aidsMyStuff[0]); i++) {
2600                 strcpy(szMyStuffTarget, szPersonalTarget);
2601                 if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
2602                     mkdir(szMyStuffTarget, 0777);
2603             }
2604         } 
2605         else
2606         {
2607             /* '$HOME/My Documents' doesn't exists, but '$HOME' does. */ 
2608             strcpy(szPersonalTarget, pszHome);
2609         }
2610
2611         /* Replace 'My Documents' directory with a symlink of fail silently if not empty. */
2612         rmdir(pszPersonal);
2613         symlink(szPersonalTarget, pszPersonal);
2614     }
2615     else
2616     {
2617         /* '$HOME' doesn't exist. Create 'My Pictures', 'My Videos' and 'My Music' subdirs
2618          * in '%USERPROFILE%\\My Documents' or fail silently if they already exist. */
2619         strcpy(szPersonalTarget, pszPersonal);
2620         for (i = 0; i < sizeof(aidsMyStuff)/sizeof(aidsMyStuff[0]); i++) {
2621             strcpy(szMyStuffTarget, szPersonalTarget);
2622             if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
2623                 mkdir(szMyStuffTarget, 0777);
2624         }
2625     }
2626
2627     /* Create symbolic links for 'My Pictures', 'My Video' and 'My Music'. */
2628     for (i=0; i < sizeof(aidsMyStuff)/sizeof(aidsMyStuff[0]); i++) {
2629         /* Create the current 'My Whatever' folder and get it's unix path. */
2630         hr = SHGetFolderPathW(NULL, acsidlMyStuff[i]|CSIDL_FLAG_CREATE, NULL,
2631                               SHGFP_TYPE_DEFAULT, wszTempPath);
2632         if (FAILED(hr)) continue;
2633         pszMyStuff = wine_get_unix_file_name(wszTempPath);
2634         if (!pszMyStuff) continue;
2635         
2636         strcpy(szMyStuffTarget, szPersonalTarget);
2637         if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])) &&
2638             !stat(szMyStuffTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
2639         {
2640             /* If there's a 'My Whatever' directory where 'My Documents' links to, link to it. */
2641             rmdir(pszMyStuff);
2642             symlink(szMyStuffTarget, pszMyStuff);
2643         } 
2644         else
2645         {
2646             rmdir(pszMyStuff);
2647             if (xdg_results && xdg_results[i])
2648             {
2649                 /* the folder specified by XDG_XXX_DIR exists, link to it. */
2650                 symlink(xdg_results[i], pszMyStuff);
2651             }
2652             else
2653             {
2654                 /* Else link to where 'My Documents' itself links to. */
2655                 symlink(szPersonalTarget, pszMyStuff);
2656             }
2657         }
2658         HeapFree(GetProcessHeap(), 0, pszMyStuff);
2659     }
2660
2661     /* Last but not least, the Desktop folder */
2662     if (pszHome)
2663         strcpy(szDesktopTarget, pszHome);
2664     else
2665         strcpy(szDesktopTarget, pszPersonal);
2666     HeapFree(GetProcessHeap(), 0, pszPersonal);
2667
2668     xdg_desktop_dir = xdg_results ? xdg_results[num - 1] : NULL;
2669     if (xdg_desktop_dir ||
2670         (_SHAppendToUnixPath(szDesktopTarget, DesktopW) &&
2671         !stat(szDesktopTarget, &statFolder) && S_ISDIR(statFolder.st_mode)))
2672     {
2673         hr = SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, NULL,
2674                               SHGFP_TYPE_DEFAULT, wszTempPath);
2675         if (SUCCEEDED(hr) && (pszDesktop = wine_get_unix_file_name(wszTempPath))) 
2676         {
2677             rmdir(pszDesktop);
2678             if (xdg_desktop_dir)
2679                 symlink(xdg_desktop_dir, pszDesktop);
2680             else
2681                 symlink(szDesktopTarget, pszDesktop);
2682             HeapFree(GetProcessHeap(), 0, pszDesktop);
2683         }
2684     }
2685
2686     /* Free resources allocated by XDG_UserDirLookup() */
2687     if (xdg_results)
2688     {
2689         for (i = 0; i < num; i++)
2690             HeapFree(GetProcessHeap(), 0, xdg_results[i]);
2691         HeapFree(GetProcessHeap(), 0, xdg_results);
2692     }
2693 }
2694
2695 /******************************************************************************
2696  * create_extra_folders  [Internal]
2697  *
2698  * Create some extra folders that don't have a standard CSIDL definition.
2699  */
2700 static HRESULT create_extra_folders(void)
2701 {
2702     static const WCHAR environW[] = {'E','n','v','i','r','o','n','m','e','n','t',0};
2703     static const WCHAR TempW[]    = {'T','e','m','p',0};
2704     static const WCHAR TEMPW[]    = {'T','E','M','P',0};
2705     static const WCHAR TMPW[]     = {'T','M','P',0};
2706     WCHAR path[MAX_PATH+5];
2707     HRESULT hr;
2708     HKEY hkey;
2709     DWORD type, size, ret;
2710
2711     ret = RegCreateKeyW( HKEY_CURRENT_USER, environW, &hkey );
2712     if (ret) return HRESULT_FROM_WIN32( ret );
2713
2714     /* FIXME: should be under AppData, but we don't want spaces in the temp path */
2715     hr = SHGetFolderPathAndSubDirW( 0, CSIDL_PROFILE | CSIDL_FLAG_CREATE, NULL,
2716                                     SHGFP_TYPE_DEFAULT, TempW, path );
2717     if (SUCCEEDED(hr))
2718     {
2719         size = sizeof(path);
2720         if (RegQueryValueExW( hkey, TEMPW, NULL, &type, (LPBYTE)path, &size ))
2721             RegSetValueExW( hkey, TEMPW, 0, REG_SZ, (LPBYTE)path, (strlenW(path) + 1) * sizeof(WCHAR) );
2722         size = sizeof(path);
2723         if (RegQueryValueExW( hkey, TMPW, NULL, &type, (LPBYTE)path, &size ))
2724             RegSetValueExW( hkey, TMPW, 0, REG_SZ, (LPBYTE)path, (strlenW(path) + 1) * sizeof(WCHAR) );
2725     }
2726     RegCloseKey( hkey );
2727     return hr;
2728 }
2729
2730
2731 /******************************************************************************
2732  * set_folder_attributes
2733  *
2734  * Set the various folder attributes registry keys.
2735  */
2736 static HRESULT set_folder_attributes(void)
2737 {
2738     static const WCHAR clsidW[] = {'C','L','S','I','D','\\',0 };
2739     static const WCHAR shellfolderW[] = {'\\','S','h','e','l','l','F','o','l','d','e','r', 0 };
2740     static const WCHAR wfparsingW[] = {'W','a','n','t','s','F','O','R','P','A','R','S','I','N','G',0};
2741     static const WCHAR wfdisplayW[] = {'W','a','n','t','s','F','O','R','D','I','S','P','L','A','Y',0};
2742     static const WCHAR hideasdeleteW[] = {'H','i','d','e','A','s','D','e','l','e','t','e','P','e','r','U','s','e','r',0};
2743     static const WCHAR attributesW[] = {'A','t','t','r','i','b','u','t','e','s',0};
2744     static const WCHAR cfattributesW[] = {'C','a','l','l','F','o','r','A','t','t','r','i','b','u','t','e','s',0};
2745     static const WCHAR emptyW[] = {0};
2746
2747     static const struct
2748     {
2749         const CLSID *clsid;
2750         BOOL wfparsing : 1;
2751         BOOL wfdisplay : 1;
2752         BOOL hideasdel : 1;
2753         DWORD attr;
2754         DWORD call_for_attr;
2755     } folders[] =
2756     {
2757         { &CLSID_UnixFolder, TRUE, FALSE, FALSE },
2758         { &CLSID_UnixDosFolder, TRUE, FALSE, FALSE,
2759           SFGAO_FILESYSANCESTOR|SFGAO_FOLDER|SFGAO_HASSUBFOLDER, SFGAO_FILESYSTEM },
2760         { &CLSID_FolderShortcut, FALSE, FALSE, FALSE,
2761           SFGAO_FILESYSTEM|SFGAO_FOLDER|SFGAO_LINK,
2762           SFGAO_HASSUBFOLDER|SFGAO_FILESYSTEM|SFGAO_FOLDER|SFGAO_FILESYSANCESTOR },
2763         { &CLSID_MyDocuments, TRUE, FALSE, FALSE,
2764           SFGAO_FILESYSANCESTOR|SFGAO_FOLDER|SFGAO_HASSUBFOLDER, SFGAO_FILESYSTEM },
2765         { &CLSID_RecycleBin, FALSE, FALSE, FALSE,
2766           SFGAO_FOLDER|SFGAO_DROPTARGET|SFGAO_HASPROPSHEET },
2767         { &CLSID_ControlPanel, FALSE, TRUE, TRUE,
2768           SFGAO_FOLDER|SFGAO_HASSUBFOLDER }
2769     };
2770
2771     unsigned int i;
2772     WCHAR buffer[39 + (sizeof(clsidW) + sizeof(shellfolderW)) / sizeof(WCHAR)];
2773     LONG res;
2774     HKEY hkey;
2775
2776     for (i = 0; i < sizeof(folders)/sizeof(folders[0]); i++)
2777     {
2778         strcpyW( buffer, clsidW );
2779         StringFromGUID2( folders[i].clsid, buffer + strlenW(buffer), 39 );
2780         strcatW( buffer, shellfolderW );
2781         res = RegCreateKeyExW( HKEY_CLASSES_ROOT, buffer, 0, NULL, 0,
2782                                KEY_READ | KEY_WRITE, NULL, &hkey, NULL);
2783         if (res) return HRESULT_FROM_WIN32( res );
2784         if (folders[i].wfparsing)
2785             res = RegSetValueExW( hkey, wfparsingW, 0, REG_SZ, (const BYTE *)emptyW, sizeof(emptyW) );
2786         if (folders[i].wfdisplay)
2787             res = RegSetValueExW( hkey, wfdisplayW, 0, REG_SZ, (const BYTE *)emptyW, sizeof(emptyW) );
2788         if (folders[i].hideasdel)
2789             res = RegSetValueExW( hkey, hideasdeleteW, 0, REG_SZ, (const BYTE *)emptyW, sizeof(emptyW) );
2790         if (folders[i].attr)
2791             res = RegSetValueExW( hkey, attributesW, 0, REG_DWORD,
2792                                   (const BYTE *)&folders[i].attr, sizeof(DWORD));
2793         if (folders[i].call_for_attr)
2794             res = RegSetValueExW( hkey, cfattributesW, 0, REG_DWORD,
2795                                  (const BYTE *)&folders[i].call_for_attr, sizeof(DWORD));
2796         RegCloseKey( hkey );
2797     }
2798     return S_OK;
2799 }
2800
2801
2802 /* Register the default values in the registry, as some apps seem to depend
2803  * on their presence.  The set registered was taken from Windows XP.
2804  */
2805 HRESULT SHELL_RegisterShellFolders(void)
2806 {
2807     HRESULT hr;
2808
2809     /* Set up '$HOME' targeted symlinks for 'My Documents', 'My Pictures',
2810      * 'My Video', 'My Music' and 'Desktop' in advance, so that the
2811      * _SHRegister*ShellFolders() functions will find everything nice and clean
2812      * and thus will not attempt to create them in the profile directory. */
2813     _SHCreateSymbolicLinks();
2814     
2815     hr = _SHRegisterUserShellFolders(TRUE);
2816     if (SUCCEEDED(hr))
2817         hr = _SHRegisterUserShellFolders(FALSE);
2818     if (SUCCEEDED(hr))
2819         hr = _SHRegisterCommonShellFolders();
2820     if (SUCCEEDED(hr))
2821         hr = create_extra_folders();
2822     if (SUCCEEDED(hr))
2823         hr = set_folder_attributes();
2824     return hr;
2825 }
2826
2827 /*************************************************************************
2828  * SHGetSpecialFolderPathA [SHELL32.@]
2829  */
2830 BOOL WINAPI SHGetSpecialFolderPathA (
2831         HWND hwndOwner,
2832         LPSTR szPath,
2833         int nFolder,
2834         BOOL bCreate)
2835 {
2836         return (SHGetFolderPathA(
2837                 hwndOwner,
2838                 nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0),
2839                 NULL,
2840                 0,
2841                 szPath)) == S_OK ? TRUE : FALSE;
2842 }
2843
2844 /*************************************************************************
2845  * SHGetSpecialFolderPathW
2846  */
2847 BOOL WINAPI SHGetSpecialFolderPathW (
2848         HWND hwndOwner,
2849         LPWSTR szPath,
2850         int nFolder,
2851         BOOL bCreate)
2852 {
2853         return (SHGetFolderPathW(
2854                 hwndOwner,
2855                 nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0),
2856                 NULL,
2857                 0,
2858                 szPath)) == S_OK ? TRUE : FALSE;
2859 }
2860
2861 /*************************************************************************
2862  * SHGetSpecialFolderPath (SHELL32.175)
2863  */
2864 BOOL WINAPI SHGetSpecialFolderPathAW (
2865         HWND hwndOwner,
2866         LPVOID szPath,
2867         int nFolder,
2868         BOOL bCreate)
2869
2870 {
2871         if (SHELL_OsIsUnicode())
2872           return SHGetSpecialFolderPathW (hwndOwner, szPath, nFolder, bCreate);
2873         return SHGetSpecialFolderPathA (hwndOwner, szPath, nFolder, bCreate);
2874 }
2875
2876 /*************************************************************************
2877  * SHGetFolderLocation [SHELL32.@]
2878  *
2879  * Gets the folder locations from the registry and creates a pidl.
2880  *
2881  * PARAMS
2882  *   hwndOwner  [I]
2883  *   nFolder    [I] CSIDL_xxxxx
2884  *   hToken     [I] token representing user, or NULL for current user, or -1 for
2885  *                  default user
2886  *   dwReserved [I] must be zero
2887  *   ppidl      [O] PIDL of a special folder
2888  *
2889  * RETURNS
2890  *  Success: S_OK
2891  *  Failure: Standard OLE-defined error result, S_FALSE or E_INVALIDARG
2892  *
2893  * NOTES
2894  *  Creates missing reg keys and directories.
2895  *  Mostly forwards to SHGetFolderPathW, but a few values of nFolder return
2896  *  virtual folders that are handled here.
2897  */
2898 HRESULT WINAPI SHGetFolderLocation(
2899         HWND hwndOwner,
2900         int nFolder,
2901         HANDLE hToken,
2902         DWORD dwReserved,
2903         LPITEMIDLIST *ppidl)
2904 {
2905     HRESULT hr = E_INVALIDARG;
2906
2907     TRACE("%p 0x%08x %p 0x%08x %p\n",
2908      hwndOwner, nFolder, hToken, dwReserved, ppidl);
2909     
2910     if (!ppidl)
2911         return E_INVALIDARG;
2912     if (dwReserved)
2913         return E_INVALIDARG;
2914
2915     /* The virtual folders' locations are not user-dependent */
2916     *ppidl = NULL;
2917     switch (nFolder & CSIDL_FOLDER_MASK)
2918     {
2919         case CSIDL_DESKTOP:
2920             *ppidl = _ILCreateDesktop();
2921             break;
2922
2923         case CSIDL_PERSONAL:
2924             *ppidl = _ILCreateMyDocuments();
2925             break;
2926
2927         case CSIDL_INTERNET:
2928             *ppidl = _ILCreateIExplore();
2929             break;
2930
2931         case CSIDL_CONTROLS:
2932             *ppidl = _ILCreateControlPanel();
2933             break;
2934
2935         case CSIDL_PRINTERS:
2936             *ppidl = _ILCreatePrinters();
2937             break;
2938
2939         case CSIDL_BITBUCKET:
2940             *ppidl = _ILCreateBitBucket();
2941             break;
2942
2943         case CSIDL_DRIVES:
2944             *ppidl = _ILCreateMyComputer();
2945             break;
2946
2947         case CSIDL_NETWORK:
2948             *ppidl = _ILCreateNetwork();
2949             break;
2950
2951         default:
2952         {
2953             WCHAR szPath[MAX_PATH];
2954
2955             hr = SHGetFolderPathW(hwndOwner, nFolder, hToken,
2956              SHGFP_TYPE_CURRENT, szPath);
2957             if (SUCCEEDED(hr))
2958             {
2959                 DWORD attributes=0;
2960
2961                 TRACE("Value=%s\n", debugstr_w(szPath));
2962                 hr = SHILCreateFromPathW(szPath, ppidl, &attributes);
2963             }
2964             else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
2965             {
2966                 /* unlike SHGetFolderPath, SHGetFolderLocation in shell32
2967                  * version 6.0 returns E_FAIL for nonexistent paths
2968                  */
2969                 hr = E_FAIL;
2970             }
2971         }
2972     }
2973     if(*ppidl)
2974         hr = NOERROR;
2975
2976     TRACE("-- (new pidl %p)\n",*ppidl);
2977     return hr;
2978 }
2979
2980 /*************************************************************************
2981  * SHGetSpecialFolderLocation           [SHELL32.@]
2982  *
2983  * NOTES
2984  *   In NT5, SHGetSpecialFolderLocation needs the <winntdir>/Recent
2985  *   directory.
2986  */
2987 HRESULT WINAPI SHGetSpecialFolderLocation(
2988         HWND hwndOwner,
2989         INT nFolder,
2990         LPITEMIDLIST * ppidl)
2991 {
2992     HRESULT hr = E_INVALIDARG;
2993
2994     TRACE("(%p,0x%x,%p)\n", hwndOwner,nFolder,ppidl);
2995
2996     if (!ppidl)
2997         return E_INVALIDARG;
2998
2999     hr = SHGetFolderLocation(hwndOwner, nFolder, NULL, 0, ppidl);
3000     return hr;
3001 }
3002
3003 static int csidl_from_id( const KNOWNFOLDERID *id )
3004 {
3005     int i;
3006     for (i = 0; i < sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]); i++)
3007         if (IsEqualGUID( CSIDL_Data[i].id, id )) return i;
3008     return -1;
3009 }
3010
3011 /*************************************************************************
3012  * SHGetKnownFolderPath           [SHELL32.@]
3013  */
3014 HRESULT WINAPI SHGetKnownFolderPath(REFKNOWNFOLDERID rfid, DWORD flags, HANDLE token, PWSTR *path)
3015 {
3016     HRESULT hr;
3017     WCHAR folder[MAX_PATH];
3018     int index = csidl_from_id( rfid );
3019
3020     TRACE("%s, 0x%08x, %p, %p\n", debugstr_guid(rfid), flags, token, path);
3021
3022     if (index < 0)
3023         return E_INVALIDARG;
3024
3025     if (flags & KF_FLAG_CREATE)
3026         index |= CSIDL_FLAG_CREATE;
3027
3028     if (flags & KF_FLAG_DONT_VERIFY)
3029         index |= CSIDL_FLAG_DONT_VERIFY;
3030
3031     if (flags & KF_FLAG_NO_ALIAS)
3032         index |= CSIDL_FLAG_NO_ALIAS;
3033
3034     if (flags & KF_FLAG_INIT)
3035         index |= CSIDL_FLAG_PER_USER_INIT;
3036
3037     if (flags & ~(KF_FLAG_CREATE|KF_FLAG_DONT_VERIFY|KF_FLAG_NO_ALIAS|KF_FLAG_INIT))
3038     {
3039         FIXME("flags 0x%08x not supported\n", flags);
3040         return E_INVALIDARG;
3041     }
3042
3043     hr = SHGetFolderPathW( NULL, index, token, 0, folder );
3044     if (SUCCEEDED(hr))
3045     {
3046         *path = CoTaskMemAlloc( (strlenW( folder ) + 1) * sizeof(WCHAR) );
3047         if (!*path)
3048             return E_OUTOFMEMORY;
3049         strcpyW( *path, folder );
3050     }
3051     return hr;
3052 }
3053
3054 /*************************************************************************
3055  * SHGetFolderPathEx           [SHELL32.@]
3056  */
3057 HRESULT WINAPI SHGetFolderPathEx(REFKNOWNFOLDERID rfid, DWORD flags, HANDLE token, LPWSTR path, DWORD len)
3058 {
3059     HRESULT hr;
3060     WCHAR *buffer;
3061
3062     TRACE("%s, 0x%08x, %p, %p, %u\n", debugstr_guid(rfid), flags, token, path, len);
3063
3064     if (!path || !len) return E_INVALIDARG;
3065
3066     hr = SHGetKnownFolderPath( rfid, flags, token, &buffer );
3067     if (SUCCEEDED( hr ))
3068     {
3069         if (strlenW( buffer ) + 1 > len)
3070         {
3071             CoTaskMemFree( buffer );
3072             return HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER );
3073         }
3074         strcpyW( path, buffer );
3075         CoTaskMemFree( buffer );
3076     }
3077     return hr;
3078 }
3079
3080 /* constant values used by known folder functions */
3081 static const WCHAR szCategory[] = {'C','a','t','e','g','o','r','y',0};
3082 static const WCHAR szName[] = {'N','a','m','e',0};
3083 static const WCHAR szRelativePath[] = {'R','e','l','a','t','i','v','e','P','a','t','h',0};
3084 static const WCHAR szParentFolder[] = {'P','a','r','e','n','t','F','o','l','d','e','r',0};
3085
3086 /*
3087  * Internal function to convert known folder identifier to path of registry key
3088  * associated with known folder.
3089  *
3090  * Parameters:
3091  *  rfid            [I] pointer to known folder identifier (may be NULL)
3092  *  lpStringGuid    [I] string with known folder identifier (used when rfid is NULL)
3093  *  lpPath          [O] place to store string address. String should be
3094  *                      later freed using HeapFree(GetProcessHeap(),0, ... )
3095  */
3096 static HRESULT get_known_folder_registry_path(
3097     REFKNOWNFOLDERID rfid,
3098     LPWSTR lpStringGuid,
3099     LPWSTR *lpPath)
3100 {
3101     static const WCHAR sBackslash[] = {'\\',0};
3102     HRESULT hr = S_OK;
3103     int length;
3104     WCHAR sGuid[50];
3105
3106     TRACE("(%s, %s, %p)\n", debugstr_guid(rfid), debugstr_w(lpStringGuid), lpPath);
3107
3108     if(rfid)
3109         StringFromGUID2(rfid, sGuid, sizeof(sGuid)/sizeof(sGuid[0]));
3110     else
3111         lstrcpyW(sGuid, lpStringGuid);
3112
3113     length = lstrlenW(szKnownFolderDescriptions)+51;
3114     *lpPath = HeapAlloc(GetProcessHeap(), 0, length*sizeof(WCHAR));
3115     if(!(*lpPath))
3116         hr = E_OUTOFMEMORY;
3117
3118     if(SUCCEEDED(hr))
3119     {
3120         lstrcpyW(*lpPath, szKnownFolderDescriptions);
3121         lstrcatW(*lpPath, sBackslash);
3122         lstrcatW(*lpPath, sGuid);
3123     }
3124
3125     return hr;
3126 }
3127
3128 /*
3129  * Internal function to get place where folder redirection information are stored.
3130  *
3131  * Parameters:
3132  *  rfid            [I] pointer to known folder identifier (may be NULL)
3133  *  rootKey         [O] root key where the redirection information are stored
3134  *                      It can be HKLM for COMMON folders, and HKCU for PERUSER folders.
3135  *                      However, besides root key, path is always that same, and is stored
3136  *                      as "szKnownFolderRedirections" constant
3137  */
3138 static HRESULT get_known_folder_redirection_place(
3139     REFKNOWNFOLDERID rfid,
3140     HKEY *rootKey)
3141 {
3142     HRESULT hr;
3143     LPWSTR lpRegistryPath = NULL;
3144     KF_CATEGORY category;
3145     DWORD dwSize;
3146
3147     /* first, get known folder's category */
3148     hr = get_known_folder_registry_path(rfid, NULL, &lpRegistryPath);
3149
3150     if(SUCCEEDED(hr))
3151     {
3152         dwSize = sizeof(category);
3153         hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, lpRegistryPath, szCategory, RRF_RT_DWORD, NULL, &category, &dwSize));
3154     }
3155
3156     if(SUCCEEDED(hr))
3157     {
3158         if(category == KF_CATEGORY_COMMON)
3159         {
3160             *rootKey = HKEY_LOCAL_MACHINE;
3161             hr = S_OK;
3162         }
3163         else if(category == KF_CATEGORY_PERUSER)
3164         {
3165             *rootKey = HKEY_CURRENT_USER;
3166             hr = S_OK;
3167         }
3168         else
3169             hr = E_FAIL;
3170     }
3171
3172     HeapFree(GetProcessHeap(), 0, lpRegistryPath);
3173     return hr;
3174 }
3175
3176 static HRESULT redirect_known_folder(
3177     REFKNOWNFOLDERID rfid,
3178     HWND hwnd,
3179     KF_REDIRECT_FLAGS flags,
3180     LPCWSTR pszTargetPath,
3181     UINT cFolders,
3182     KNOWNFOLDERID const *pExclusion,
3183     LPWSTR *ppszError)
3184 {
3185     HRESULT hr;
3186     HKEY rootKey = HKEY_LOCAL_MACHINE, hKey;
3187     WCHAR sGuid[39];
3188     TRACE("(%s, %p, 0x%08x, %s, %d, %p, %p)\n", debugstr_guid(rfid), hwnd, flags, debugstr_w(pszTargetPath), cFolders, pExclusion, ppszError);
3189
3190     /* get path to redirection storage */
3191     hr = get_known_folder_redirection_place(rfid, &rootKey);
3192
3193     /* write redirection information */
3194     if(SUCCEEDED(hr))
3195         hr = HRESULT_FROM_WIN32(RegCreateKeyExW(rootKey, szKnownFolderRedirections, 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL));
3196
3197     if(SUCCEEDED(hr))
3198     {
3199         StringFromGUID2(rfid, sGuid, sizeof(sGuid)/sizeof(sGuid[0]));
3200
3201         hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sGuid, 0, REG_SZ, (LPBYTE)pszTargetPath, (lstrlenW(pszTargetPath)+1)*sizeof(WCHAR)));
3202
3203         RegCloseKey(hKey);
3204     }
3205
3206     return hr;
3207 }
3208
3209
3210 struct knownfolder
3211 {
3212     const struct IKnownFolderVtbl *vtbl;
3213     LONG refs;
3214     KNOWNFOLDERID id;
3215     LPWSTR registryPath;
3216 };
3217
3218 static inline struct knownfolder *impl_from_IKnownFolder( IKnownFolder *iface )
3219 {
3220     return (struct knownfolder *)((char *)iface - FIELD_OFFSET( struct knownfolder, vtbl ));
3221 }
3222
3223 static ULONG WINAPI knownfolder_AddRef(
3224     IKnownFolder *iface )
3225 {
3226     struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
3227     return InterlockedIncrement( &knownfolder->refs );
3228 }
3229
3230 static ULONG WINAPI knownfolder_Release(
3231     IKnownFolder *iface )
3232 {
3233     struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
3234     LONG refs = InterlockedDecrement( &knownfolder->refs );
3235     if (!refs)
3236     {
3237         TRACE("destroying %p\n", knownfolder);
3238         HeapFree( GetProcessHeap(), 0, knownfolder->registryPath);
3239         HeapFree( GetProcessHeap(), 0, knownfolder );
3240     }
3241     return refs;
3242 }
3243
3244 static HRESULT WINAPI knownfolder_QueryInterface(
3245     IKnownFolder *iface,
3246     REFIID riid,
3247     void **ppv )
3248 {
3249     struct knownfolder *This = impl_from_IKnownFolder( iface );
3250
3251     TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppv );
3252
3253     if ( IsEqualGUID( riid, &IID_IKnownFolder ) ||
3254          IsEqualGUID( riid, &IID_IUnknown ) )
3255     {
3256         *ppv = iface;
3257     }
3258     else
3259     {
3260         FIXME("interface %s not implemented\n", debugstr_guid(riid));
3261         return E_NOINTERFACE;
3262     }
3263     IKnownFolder_AddRef( iface );
3264     return S_OK;
3265 }
3266
3267 static HRESULT knownfolder_set_id(
3268     IKnownFolder *iface,
3269     const KNOWNFOLDERID *kfid)
3270 {
3271     struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
3272     HKEY hKey;
3273     HRESULT hr;
3274
3275     TRACE("%s\n", debugstr_guid(kfid));
3276
3277     knownfolder->id = *kfid;
3278
3279     /* check is it registry-registered folder */
3280     hr = get_known_folder_registry_path(kfid, NULL, &knownfolder->registryPath);
3281     if(SUCCEEDED(hr))
3282         hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, knownfolder->registryPath, 0, 0, &hKey));
3283
3284     if(SUCCEEDED(hr))
3285     {
3286         hr = S_OK;
3287         RegCloseKey(hKey);
3288     }
3289     else
3290     {
3291         /* This known folder is not registered. To mark it, we set registryPath to NULL */
3292         HeapFree(GetProcessHeap(), 0, knownfolder->registryPath);
3293         knownfolder->registryPath = NULL;
3294         hr = S_OK;
3295     }
3296
3297     return hr;
3298 }
3299
3300 static HRESULT WINAPI knownfolder_GetId(
3301     IKnownFolder *iface,
3302     KNOWNFOLDERID *pkfid)
3303 {
3304     struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
3305
3306     TRACE("%p\n", pkfid);
3307
3308     *pkfid = knownfolder->id;
3309     return S_OK;
3310 }
3311
3312 static HRESULT WINAPI knownfolder_GetCategory(
3313     IKnownFolder *iface,
3314     KF_CATEGORY *pCategory)
3315 {
3316     struct knownfolder *knownfolder = impl_from_IKnownFolder(iface);
3317     HRESULT hr = S_OK;
3318     DWORD dwSize = sizeof(DWORD);
3319     DWORD dwType;
3320
3321     TRACE("%p, %p\n", knownfolder, pCategory);
3322
3323     /* we cannot get a category for a folder which is not registered */
3324     if(!knownfolder->registryPath)
3325         hr = E_FAIL;
3326
3327     if(SUCCEEDED(hr))
3328         hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, knownfolder->registryPath, szCategory, RRF_RT_DWORD, &dwType, pCategory, &dwSize));
3329
3330     return hr;
3331 }
3332
3333 static HRESULT WINAPI knownfolder_GetShellItem(
3334     IKnownFolder *iface,
3335     DWORD dwFlags,
3336     REFIID riid,
3337     void **ppv)
3338 {
3339     FIXME("0x%08x, %s, %p\n", dwFlags, debugstr_guid(riid), ppv);
3340     return E_NOTIMPL;
3341 }
3342
3343 static HRESULT get_known_folder_path(
3344     LPWSTR sFolderId,
3345     LPWSTR registryPath,
3346     LPWSTR *ppszPath)
3347 {
3348     static const WCHAR sBackslash[] = {'\\',0};
3349     HRESULT hr;
3350     DWORD dwSize, dwType;
3351     WCHAR path[MAX_PATH] = {0};
3352     WCHAR parentGuid[39];
3353     KF_CATEGORY category;
3354     LPWSTR parentRegistryPath, parentPath;
3355     HKEY hRedirectionRootKey = NULL;
3356
3357     TRACE("(%s, %p)\n", debugstr_w(registryPath), ppszPath);
3358     *ppszPath = NULL;
3359
3360     /* check if folder has parent */
3361     dwSize = sizeof(parentGuid);
3362     hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, registryPath, szParentFolder, RRF_RT_REG_SZ, &dwType, parentGuid, &dwSize));
3363     if(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) hr = S_FALSE;
3364
3365     if(hr == S_OK)
3366     {
3367         /* get parent's known folder path (recursive) */
3368         get_known_folder_registry_path(NULL, parentGuid, &parentRegistryPath);
3369
3370         hr = get_known_folder_path(parentGuid, parentRegistryPath, &parentPath);
3371
3372         lstrcatW(path, parentPath);
3373         lstrcatW(path, sBackslash);
3374
3375         HeapFree(GetProcessHeap(), 0, parentRegistryPath);
3376         HeapFree(GetProcessHeap(), 0, parentPath);
3377     }
3378
3379     /* check, if folder was redirected */
3380     if(SUCCEEDED(hr))
3381         hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, registryPath, szCategory, RRF_RT_REG_DWORD, NULL, &category, &dwSize));
3382
3383     if(SUCCEEDED(hr))
3384     {
3385         if(category == KF_CATEGORY_COMMON)
3386             hRedirectionRootKey = HKEY_LOCAL_MACHINE;
3387         else if(category == KF_CATEGORY_PERUSER)
3388             hRedirectionRootKey = HKEY_CURRENT_USER;
3389
3390         if(hRedirectionRootKey)
3391         {
3392             hr = HRESULT_FROM_WIN32(RegGetValueW(hRedirectionRootKey, szKnownFolderRedirections, sFolderId, RRF_RT_REG_SZ, NULL, NULL, &dwSize));
3393
3394             if(SUCCEEDED(hr))
3395             {
3396                 *ppszPath = CoTaskMemAlloc(dwSize+(lstrlenW(path)+1)*sizeof(WCHAR));
3397                 if(!*ppszPath) hr = E_OUTOFMEMORY;
3398             }
3399
3400             if(SUCCEEDED(hr))
3401             {
3402                 lstrcpyW(*ppszPath, path);
3403                 hr = HRESULT_FROM_WIN32(RegGetValueW(hRedirectionRootKey, szKnownFolderRedirections, sFolderId, RRF_RT_REG_SZ, NULL, *ppszPath + lstrlenW(path), &dwSize));
3404             }
3405         }
3406
3407         if(!*ppszPath)
3408         {
3409             /* no redirection, use previous way - read the relative path from folder definition */
3410             hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, registryPath, szRelativePath, RRF_RT_REG_SZ, &dwType, NULL, &dwSize));
3411
3412             if(SUCCEEDED(hr))
3413             {
3414                 *ppszPath = CoTaskMemAlloc(dwSize+(lstrlenW(path)+1)*sizeof(WCHAR));
3415                 if(!*ppszPath) hr = E_OUTOFMEMORY;
3416             }
3417
3418             if(SUCCEEDED(hr))
3419             {
3420                 lstrcpyW(*ppszPath, path);
3421                 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE, registryPath, szRelativePath, RRF_RT_REG_SZ, &dwType, *ppszPath + lstrlenW(path), &dwSize));
3422             }
3423         }
3424     }
3425
3426     TRACE("returning path: %s\n", debugstr_w(*ppszPath));
3427     return hr;
3428 }
3429
3430 static HRESULT WINAPI knownfolder_GetPath(
3431     IKnownFolder *iface,
3432     DWORD dwFlags,
3433     LPWSTR *ppszPath)
3434 {
3435     struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
3436     HRESULT hr;
3437     WCHAR sGuid[39];
3438     DWORD dwAttributes;
3439
3440     TRACE("(%p, 0x%08x, %p)\n", knownfolder, dwFlags, ppszPath);
3441
3442     /* if this is registry-registered known folder, get path from registry */
3443     if(knownfolder->registryPath)
3444     {
3445         StringFromGUID2(&knownfolder->id, sGuid, sizeof(sGuid)/sizeof(sGuid[0]));
3446
3447         hr = get_known_folder_path(sGuid, knownfolder->registryPath, ppszPath);
3448     }
3449     /* in other case, use older way */
3450     else
3451         hr = SHGetKnownFolderPath( &knownfolder->id, dwFlags, NULL, ppszPath );
3452
3453     /* check if known folder really exists */
3454     dwAttributes = GetFileAttributesW(*ppszPath);
3455     if(dwAttributes == INVALID_FILE_ATTRIBUTES || !(dwAttributes & FILE_ATTRIBUTE_DIRECTORY) )
3456     {
3457         TRACE("directory %s not found\n", debugstr_w(*ppszPath));
3458         CoTaskMemFree(*ppszPath);
3459         *ppszPath = NULL;
3460         hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
3461     }
3462
3463     return hr;
3464 }
3465
3466 static HRESULT WINAPI knownfolder_SetPath(
3467     IKnownFolder *iface,
3468     DWORD dwFlags,
3469     LPCWSTR pszPath)
3470 {
3471     struct knownfolder *knownfolder = impl_from_IKnownFolder( iface );
3472     HRESULT hr = S_OK;
3473
3474     TRACE("(%p, 0x%08x, %p)\n", knownfolder, dwFlags, debugstr_w(pszPath));
3475
3476     /* check if the known folder is registered */
3477     if(!knownfolder->registryPath)
3478         hr = E_FAIL;
3479
3480     if(SUCCEEDED(hr))
3481         hr = redirect_known_folder(&knownfolder->id, NULL, 0, pszPath, 0, NULL, NULL);
3482
3483     return hr;
3484 }
3485
3486 static HRESULT WINAPI knownfolder_GetIDList(
3487     IKnownFolder *iface,
3488     DWORD dwFlags,
3489     PIDLIST_ABSOLUTE *ppidl)
3490 {
3491     FIXME("0x%08x, %p\n", dwFlags, ppidl);
3492     return E_NOTIMPL;
3493 }
3494
3495 static HRESULT WINAPI knownfolder_GetFolderType(
3496     IKnownFolder *iface,
3497     FOLDERTYPEID *pftid)
3498 {
3499     FIXME("%p\n", pftid);
3500     return E_NOTIMPL;
3501 }
3502
3503 static HRESULT WINAPI knownfolder_GetRedirectionCapabilities(
3504     IKnownFolder *iface,
3505     KF_REDIRECTION_CAPABILITIES *pCapabilities)
3506 {
3507     FIXME("%p\n", pCapabilities);
3508     return E_NOTIMPL;
3509 }
3510
3511 static HRESULT WINAPI knownfolder_GetFolderDefinition(
3512     IKnownFolder *iface,
3513     KNOWNFOLDER_DEFINITION *pKFD)
3514 {
3515     FIXME("%p\n", pKFD);
3516     return E_NOTIMPL;
3517 }
3518
3519 static const struct IKnownFolderVtbl knownfolder_vtbl =
3520 {
3521     knownfolder_QueryInterface,
3522     knownfolder_AddRef,
3523     knownfolder_Release,
3524     knownfolder_GetId,
3525     knownfolder_GetCategory,
3526     knownfolder_GetShellItem,
3527     knownfolder_GetPath,
3528     knownfolder_SetPath,
3529     knownfolder_GetIDList,
3530     knownfolder_GetFolderType,
3531     knownfolder_GetRedirectionCapabilities,
3532     knownfolder_GetFolderDefinition
3533 };
3534
3535 static HRESULT knownfolder_create( void **ppv )
3536 {
3537     struct knownfolder *kf;
3538
3539     kf = HeapAlloc( GetProcessHeap(), 0, sizeof(*kf) );
3540     if (!kf) return E_OUTOFMEMORY;
3541
3542     kf->vtbl = &knownfolder_vtbl;
3543     kf->refs = 1;
3544     memset( &kf->id, 0, sizeof(kf->id) );
3545     kf->registryPath = NULL;
3546
3547     *ppv = &kf->vtbl;
3548
3549     TRACE("returning iface %p\n", *ppv);
3550     return S_OK;
3551 }
3552
3553 struct foldermanager
3554 {
3555     const struct IKnownFolderManagerVtbl *vtbl;
3556     LONG refs;
3557     UINT num_ids;
3558     KNOWNFOLDERID *ids;
3559 };
3560
3561 static inline struct foldermanager *impl_from_IKnownFolderManager( IKnownFolderManager *iface )
3562 {
3563     return (struct foldermanager *)((char *)iface - FIELD_OFFSET( struct foldermanager, vtbl ));
3564 }
3565
3566 static ULONG WINAPI foldermanager_AddRef(
3567     IKnownFolderManager *iface )
3568 {
3569     struct foldermanager *foldermanager = impl_from_IKnownFolderManager( iface );
3570     return InterlockedIncrement( &foldermanager->refs );
3571 }
3572
3573 static ULONG WINAPI foldermanager_Release(
3574     IKnownFolderManager *iface )
3575 {
3576     struct foldermanager *foldermanager = impl_from_IKnownFolderManager( iface );
3577     LONG refs = InterlockedDecrement( &foldermanager->refs );
3578     if (!refs)
3579     {
3580         TRACE("destroying %p\n", foldermanager);
3581         HeapFree( GetProcessHeap(), 0, foldermanager->ids );
3582         HeapFree( GetProcessHeap(), 0, foldermanager );
3583     }
3584     return refs;
3585 }
3586
3587 static HRESULT WINAPI foldermanager_QueryInterface(
3588     IKnownFolderManager *iface,
3589     REFIID riid,
3590     void **ppv )
3591 {
3592     struct foldermanager *This = impl_from_IKnownFolderManager( iface );
3593
3594     TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppv );
3595
3596     if ( IsEqualGUID( riid, &IID_IKnownFolderManager ) ||
3597          IsEqualGUID( riid, &IID_IUnknown ) )
3598     {
3599         *ppv = iface;
3600     }
3601     else
3602     {
3603         FIXME("interface %s not implemented\n", debugstr_guid(riid));
3604         return E_NOINTERFACE;
3605     }
3606     IKnownFolderManager_AddRef( iface );
3607     return S_OK;
3608 }
3609
3610 static HRESULT WINAPI foldermanager_FolderIdFromCsidl(
3611     IKnownFolderManager *iface,
3612     int nCsidl,
3613     KNOWNFOLDERID *pfid)
3614 {
3615     TRACE("%d, %p\n", nCsidl, pfid);
3616
3617     if (nCsidl >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
3618         return E_INVALIDARG;
3619     *pfid = *CSIDL_Data[nCsidl].id;
3620     return S_OK;
3621 }
3622
3623 static HRESULT WINAPI foldermanager_FolderIdToCsidl(
3624     IKnownFolderManager *iface,
3625     REFKNOWNFOLDERID rfid,
3626     int *pnCsidl)
3627 {
3628     int csidl;
3629
3630     TRACE("%s, %p\n", debugstr_guid(rfid), pnCsidl);
3631
3632     csidl = csidl_from_id( rfid );
3633     if (csidl == -1) return E_INVALIDARG;
3634     *pnCsidl = csidl;
3635     return S_OK;
3636 }
3637
3638 static HRESULT WINAPI foldermanager_GetFolderIds(
3639     IKnownFolderManager *iface,
3640     KNOWNFOLDERID **ppKFId,
3641     UINT *pCount)
3642 {
3643     struct foldermanager *fm = impl_from_IKnownFolderManager( iface );
3644
3645     TRACE("%p, %p\n", ppKFId, pCount);
3646
3647     *ppKFId = fm->ids;
3648     *pCount = fm->num_ids;
3649     return S_OK;
3650 }
3651
3652 static BOOL is_knownfolder( struct foldermanager *fm, const KNOWNFOLDERID *id )
3653 {
3654     UINT i;
3655     HRESULT hr;
3656     LPWSTR registryPath = NULL;
3657     HKEY hKey;
3658
3659     /* TODO: move all entries from "CSIDL_Data" static array to registry known folder descriptions */
3660     for (i = 0; i < fm->num_ids; i++)
3661         if (IsEqualGUID( &fm->ids[i], id )) return TRUE;
3662
3663     hr = get_known_folder_registry_path(id, NULL, &registryPath);
3664     if(SUCCEEDED(hr))
3665         hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, registryPath, 0, 0, &hKey));
3666
3667     if(SUCCEEDED(hr))
3668     {
3669         hr = S_OK;
3670         RegCloseKey(hKey);
3671     }
3672
3673     return hr == S_OK;
3674 }
3675
3676 static HRESULT WINAPI foldermanager_GetFolder(
3677     IKnownFolderManager *iface,
3678     REFKNOWNFOLDERID rfid,
3679     IKnownFolder **ppkf)
3680 {
3681     struct foldermanager *fm = impl_from_IKnownFolderManager( iface );
3682     HRESULT hr;
3683
3684     TRACE("%s, %p\n", debugstr_guid(rfid), ppkf);
3685
3686     if (!is_knownfolder( fm, rfid ))
3687     {
3688         WARN("unknown folder\n");
3689         return E_INVALIDARG;
3690     }
3691     hr = knownfolder_create( (void **)ppkf );
3692     if (SUCCEEDED( hr ))
3693         hr = knownfolder_set_id( *ppkf, rfid );
3694
3695     return hr;
3696 }
3697
3698 static HRESULT WINAPI foldermanager_GetFolderByName(
3699     IKnownFolderManager *iface,
3700     LPCWSTR pszCanonicalName,
3701     IKnownFolder **ppkf)
3702 {
3703     FIXME("%s, %p\n", debugstr_w(pszCanonicalName), ppkf);
3704     return E_NOTIMPL;
3705 }
3706
3707 static HRESULT WINAPI foldermanager_RegisterFolder(
3708     IKnownFolderManager *iface,
3709     REFKNOWNFOLDERID rfid,
3710     KNOWNFOLDER_DEFINITION const *pKFD)
3711 {
3712     HRESULT hr;
3713     HKEY hKey = NULL;
3714     DWORD dwDisp;
3715     LPWSTR registryPath = NULL;
3716     TRACE("(%p, %s, %p)\n", iface, debugstr_guid(rfid), pKFD);
3717
3718     hr = get_known_folder_registry_path(rfid, NULL, &registryPath);
3719     TRACE("registry path: %s\n", debugstr_w(registryPath));
3720
3721     if(SUCCEEDED(hr))
3722         hr = HRESULT_FROM_WIN32(RegCreateKeyExW(HKEY_LOCAL_MACHINE, registryPath, 0, NULL, 0, KEY_WRITE, 0, &hKey, &dwDisp));
3723
3724     if(SUCCEEDED(hr))
3725     {
3726         if(dwDisp == REG_OPENED_EXISTING_KEY)
3727             hr = E_FAIL;
3728
3729         if(SUCCEEDED(hr))
3730             hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, szCategory, 0, REG_DWORD, (LPBYTE)&pKFD->category, sizeof(pKFD->category)));
3731
3732         if(SUCCEEDED(hr))
3733             hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, szName, 0, REG_SZ, (LPBYTE)pKFD->pszName, (lstrlenW(pKFD->pszName)+1)*sizeof(WCHAR) ));
3734
3735         if(SUCCEEDED(hr) && !IsEqualGUID(&pKFD->fidParent, &GUID_NULL))
3736         {
3737             WCHAR sParentGuid[39];
3738             StringFromGUID2(&pKFD->fidParent, sParentGuid, sizeof(sParentGuid)/sizeof(sParentGuid[0]));
3739
3740             /* this known folder has parent folder */
3741             hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, szParentFolder, 0, REG_SZ, (LPBYTE)sParentGuid, sizeof(sParentGuid)));
3742         }
3743
3744         if(SUCCEEDED(hr) && pKFD->category != KF_CATEGORY_VIRTUAL)
3745             hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, szRelativePath, 0, REG_SZ, (LPBYTE)pKFD->pszRelativePath, (lstrlenW(pKFD->pszRelativePath)+1)*sizeof(WCHAR) ));
3746
3747         RegCloseKey(hKey);
3748     }
3749
3750     HeapFree(GetProcessHeap(), 0, registryPath);
3751     return hr;
3752 }
3753
3754 static HRESULT WINAPI foldermanager_UnregisterFolder(
3755     IKnownFolderManager *iface,
3756     REFKNOWNFOLDERID rfid)
3757 {
3758     HRESULT hr;
3759     LPWSTR registryPath = NULL;
3760     TRACE("(%p, %s)\n", iface, debugstr_guid(rfid));
3761
3762     hr = get_known_folder_registry_path(rfid, NULL, &registryPath);
3763
3764     if(SUCCEEDED(hr))
3765         hr = HRESULT_FROM_WIN32(RegDeleteKeyW(HKEY_LOCAL_MACHINE, registryPath));
3766
3767     HeapFree(GetProcessHeap(), 0, registryPath);
3768     return hr;
3769 }
3770
3771 static HRESULT WINAPI foldermanager_FindFolderFromPath(
3772     IKnownFolderManager *iface,
3773     LPCWSTR pszPath,
3774     FFFP_MODE mode,
3775     IKnownFolder **ppkf)
3776 {
3777     FIXME("%s, 0x%08x, %p\n", debugstr_w(pszPath), mode, ppkf);
3778     return E_NOTIMPL;
3779 }
3780
3781 static HRESULT WINAPI foldermanager_FindFolderFromIDList(
3782     IKnownFolderManager *iface,
3783     PCIDLIST_ABSOLUTE pidl,
3784     IKnownFolder **ppkf)
3785 {
3786     FIXME("%p, %p\n", pidl, ppkf);
3787     return E_NOTIMPL;
3788 }
3789
3790 static HRESULT WINAPI foldermanager_Redirect(
3791     IKnownFolderManager *iface,
3792     REFKNOWNFOLDERID rfid,
3793     HWND hwnd,
3794     KF_REDIRECT_FLAGS flags,
3795     LPCWSTR pszTargetPath,
3796     UINT cFolders,
3797     KNOWNFOLDERID const *pExclusion,
3798     LPWSTR *ppszError)
3799 {
3800     return redirect_known_folder(rfid, hwnd, flags, pszTargetPath, cFolders, pExclusion, ppszError);
3801 }
3802
3803 static const struct IKnownFolderManagerVtbl foldermanager_vtbl =
3804 {
3805     foldermanager_QueryInterface,
3806     foldermanager_AddRef,
3807     foldermanager_Release,
3808     foldermanager_FolderIdFromCsidl,
3809     foldermanager_FolderIdToCsidl,
3810     foldermanager_GetFolderIds,
3811     foldermanager_GetFolder,
3812     foldermanager_GetFolderByName,
3813     foldermanager_RegisterFolder,
3814     foldermanager_UnregisterFolder,
3815     foldermanager_FindFolderFromPath,
3816     foldermanager_FindFolderFromIDList,
3817     foldermanager_Redirect
3818 };
3819
3820 static HRESULT foldermanager_create( void **ppv )
3821 {
3822     UINT i, j;
3823     struct foldermanager *fm;
3824
3825     fm = HeapAlloc( GetProcessHeap(), 0, sizeof(*fm) );
3826     if (!fm) return E_OUTOFMEMORY;
3827
3828     fm->vtbl = &foldermanager_vtbl;
3829     fm->refs = 1;
3830     fm->num_ids = 0;
3831
3832     for (i = 0; i < sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]); i++)
3833     {
3834         if (!IsEqualGUID( CSIDL_Data[i].id, &GUID_NULL )) fm->num_ids++;
3835     }
3836     fm->ids = HeapAlloc( GetProcessHeap(), 0, fm->num_ids * sizeof(KNOWNFOLDERID) );
3837     if (!fm->ids)
3838     {
3839         HeapFree( GetProcessHeap(), 0, fm );
3840         return E_OUTOFMEMORY;
3841     }
3842     for (i = j = 0; i < sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]); i++)
3843     {
3844         if (!IsEqualGUID( CSIDL_Data[i].id, &GUID_NULL ))
3845         {
3846             fm->ids[j] = *CSIDL_Data[i].id;
3847             j++;
3848         }
3849     }
3850     TRACE("found %u known folders\n", fm->num_ids);
3851     *ppv = &fm->vtbl;
3852
3853     TRACE("returning iface %p\n", *ppv);
3854     return S_OK;
3855 }
3856
3857 HRESULT WINAPI KnownFolderManager_Constructor( IUnknown *punk, REFIID riid, void **ppv )
3858 {
3859     TRACE("%p, %s, %p\n", punk, debugstr_guid(riid), ppv);
3860
3861     if (!ppv)
3862         return E_POINTER;
3863     if (punk)
3864         return CLASS_E_NOAGGREGATION;
3865
3866     return foldermanager_create( ppv );
3867 }