taskkill: Implement graceful termination by process name.
[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 #include "config.h"
28 #include "wine/port.h"
29
30 #include <stdio.h>
31 #include <stdarg.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include "wine/debug.h"
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winnls.h"
38 #include "winreg.h"
39 #include "wingdi.h"
40 #include "winuser.h"
41
42 #include "shlobj.h"
43 #include "shtypes.h"
44 #include "shresdef.h"
45 #include "shell32_main.h"
46 #include "undocshell.h"
47 #include "pidl.h"
48 #include "wine/unicode.h"
49 #include "shlwapi.h"
50 #include "xdg.h"
51 #include "sddl.h"
52 #define INITGUID
53 #include "knownfolders.h"
54
55 WINE_DEFAULT_DEBUG_CHANNEL(shell);
56
57 static const BOOL is_win64 = sizeof(void *) > sizeof(int);
58
59 /*
60         ########## Combining and Constructing paths ##########
61 */
62
63 /*************************************************************************
64  * PathAppend           [SHELL32.36]
65  */
66 BOOL WINAPI PathAppendAW(
67         LPVOID lpszPath1,
68         LPCVOID lpszPath2)
69 {
70         if (SHELL_OsIsUnicode())
71           return PathAppendW(lpszPath1, lpszPath2);
72         return PathAppendA(lpszPath1, lpszPath2);
73 }
74
75 /*************************************************************************
76  * PathCombine   [SHELL32.37]
77  */
78 LPVOID WINAPI PathCombineAW(
79         LPVOID szDest,
80         LPCVOID lpszDir,
81         LPCVOID lpszFile)
82 {
83         if (SHELL_OsIsUnicode())
84           return PathCombineW( szDest, lpszDir, lpszFile );
85         return PathCombineA( szDest, lpszDir, lpszFile );
86 }
87
88 /*************************************************************************
89  * PathAddBackslash             [SHELL32.32]
90  */
91 LPVOID WINAPI PathAddBackslashAW(LPVOID lpszPath)
92 {
93         if(SHELL_OsIsUnicode())
94           return PathAddBackslashW(lpszPath);
95         return PathAddBackslashA(lpszPath);
96 }
97
98 /*************************************************************************
99  * PathBuildRoot                [SHELL32.30]
100  */
101 LPVOID WINAPI PathBuildRootAW(LPVOID lpszPath, int drive)
102 {
103         if(SHELL_OsIsUnicode())
104           return PathBuildRootW(lpszPath, drive);
105         return PathBuildRootA(lpszPath, drive);
106 }
107
108 /*
109         Extracting Component Parts
110 */
111
112 /*************************************************************************
113  * PathFindFileName     [SHELL32.34]
114  */
115 LPVOID WINAPI PathFindFileNameAW(LPCVOID lpszPath)
116 {
117         if(SHELL_OsIsUnicode())
118           return PathFindFileNameW(lpszPath);
119         return PathFindFileNameA(lpszPath);
120 }
121
122 /*************************************************************************
123  * PathFindExtension            [SHELL32.31]
124  */
125 LPVOID WINAPI PathFindExtensionAW(LPCVOID lpszPath)
126 {
127         if (SHELL_OsIsUnicode())
128           return PathFindExtensionW(lpszPath);
129         return PathFindExtensionA(lpszPath);
130
131 }
132
133 /*************************************************************************
134  * PathGetExtensionA            [internal]
135  *
136  * NOTES
137  *  exported by ordinal
138  *  return value points to the first char after the dot
139  */
140 static LPSTR PathGetExtensionA(LPCSTR lpszPath)
141 {
142         TRACE("(%s)\n",lpszPath);
143
144         lpszPath = PathFindExtensionA(lpszPath);
145         return (LPSTR)(*lpszPath?(lpszPath+1):lpszPath);
146 }
147
148 /*************************************************************************
149  * PathGetExtensionW            [internal]
150  */
151 static LPWSTR PathGetExtensionW(LPCWSTR lpszPath)
152 {
153         TRACE("(%s)\n",debugstr_w(lpszPath));
154
155         lpszPath = PathFindExtensionW(lpszPath);
156         return (LPWSTR)(*lpszPath?(lpszPath+1):lpszPath);
157 }
158
159 /*************************************************************************
160  * PathGetExtension             [SHELL32.158]
161  */
162 LPVOID WINAPI PathGetExtensionAW(LPCVOID lpszPath,DWORD void1, DWORD void2)
163 {
164         if (SHELL_OsIsUnicode())
165           return PathGetExtensionW(lpszPath);
166         return PathGetExtensionA(lpszPath);
167 }
168
169 /*************************************************************************
170  * PathGetArgs  [SHELL32.52]
171  */
172 LPVOID WINAPI PathGetArgsAW(LPVOID lpszPath)
173 {
174         if (SHELL_OsIsUnicode())
175           return PathGetArgsW(lpszPath);
176         return PathGetArgsA(lpszPath);
177 }
178
179 /*************************************************************************
180  * PathGetDriveNumber   [SHELL32.57]
181  */
182 int WINAPI PathGetDriveNumberAW(LPVOID lpszPath)
183 {
184         if (SHELL_OsIsUnicode())
185           return PathGetDriveNumberW(lpszPath);
186         return PathGetDriveNumberA(lpszPath);
187 }
188
189 /*************************************************************************
190  * PathRemoveFileSpec [SHELL32.35]
191  */
192 BOOL WINAPI PathRemoveFileSpecAW(LPVOID lpszPath)
193 {
194         if (SHELL_OsIsUnicode())
195           return PathRemoveFileSpecW(lpszPath);
196         return PathRemoveFileSpecA(lpszPath);
197 }
198
199 /*************************************************************************
200  * PathStripPath        [SHELL32.38]
201  */
202 void WINAPI PathStripPathAW(LPVOID lpszPath)
203 {
204         if (SHELL_OsIsUnicode())
205             PathStripPathW(lpszPath);
206         else
207             PathStripPathA(lpszPath);
208 }
209
210 /*************************************************************************
211  * PathStripToRoot      [SHELL32.50]
212  */
213 BOOL WINAPI PathStripToRootAW(LPVOID lpszPath)
214 {
215         if (SHELL_OsIsUnicode())
216           return PathStripToRootW(lpszPath);
217         return PathStripToRootA(lpszPath);
218 }
219
220 /*************************************************************************
221  * PathRemoveArgs       [SHELL32.251]
222  */
223 void WINAPI PathRemoveArgsAW(LPVOID lpszPath)
224 {
225         if (SHELL_OsIsUnicode())
226             PathRemoveArgsW(lpszPath);
227         else
228             PathRemoveArgsA(lpszPath);
229 }
230
231 /*************************************************************************
232  * PathRemoveExtension  [SHELL32.250]
233  */
234 void WINAPI PathRemoveExtensionAW(LPVOID lpszPath)
235 {
236         if (SHELL_OsIsUnicode())
237             PathRemoveExtensionW(lpszPath);
238         else
239             PathRemoveExtensionA(lpszPath);
240 }
241
242
243 /*
244         Path Manipulations
245 */
246
247 /*************************************************************************
248  * PathGetShortPathA [internal]
249  */
250 static void PathGetShortPathA(LPSTR pszPath)
251 {
252         CHAR path[MAX_PATH];
253
254         TRACE("%s\n", pszPath);
255
256         if (GetShortPathNameA(pszPath, path, MAX_PATH))
257         {
258           lstrcpyA(pszPath, path);
259         }
260 }
261
262 /*************************************************************************
263  * PathGetShortPathW [internal]
264  */
265 static void PathGetShortPathW(LPWSTR pszPath)
266 {
267         WCHAR path[MAX_PATH];
268
269         TRACE("%s\n", debugstr_w(pszPath));
270
271         if (GetShortPathNameW(pszPath, path, MAX_PATH))
272         {
273           lstrcpyW(pszPath, path);
274         }
275 }
276
277 /*************************************************************************
278  * PathGetShortPath [SHELL32.92]
279  */
280 VOID WINAPI PathGetShortPathAW(LPVOID pszPath)
281 {
282         if(SHELL_OsIsUnicode())
283           PathGetShortPathW(pszPath);
284         PathGetShortPathA(pszPath);
285 }
286
287 /*************************************************************************
288  * PathRemoveBlanks [SHELL32.33]
289  */
290 void WINAPI PathRemoveBlanksAW(LPVOID str)
291 {
292         if(SHELL_OsIsUnicode())
293             PathRemoveBlanksW(str);
294         else
295             PathRemoveBlanksA(str);
296 }
297
298 /*************************************************************************
299  * PathQuoteSpaces [SHELL32.55]
300  */
301 VOID WINAPI PathQuoteSpacesAW (LPVOID lpszPath)
302 {
303         if(SHELL_OsIsUnicode())
304             PathQuoteSpacesW(lpszPath);
305         else
306             PathQuoteSpacesA(lpszPath);
307 }
308
309 /*************************************************************************
310  * PathUnquoteSpaces [SHELL32.56]
311  */
312 VOID WINAPI PathUnquoteSpacesAW(LPVOID str)
313 {
314         if(SHELL_OsIsUnicode())
315           PathUnquoteSpacesW(str);
316         else
317           PathUnquoteSpacesA(str);
318 }
319
320 /*************************************************************************
321  * PathParseIconLocation        [SHELL32.249]
322  */
323 int WINAPI PathParseIconLocationAW (LPVOID lpszPath)
324 {
325         if(SHELL_OsIsUnicode())
326           return PathParseIconLocationW(lpszPath);
327         return PathParseIconLocationA(lpszPath);
328 }
329
330 /*
331         ########## Path Testing ##########
332 */
333 /*************************************************************************
334  * PathIsUNC            [SHELL32.39]
335  */
336 BOOL WINAPI PathIsUNCAW (LPCVOID lpszPath)
337 {
338         if (SHELL_OsIsUnicode())
339           return PathIsUNCW( lpszPath );
340         return PathIsUNCA( lpszPath );
341 }
342
343 /*************************************************************************
344  *  PathIsRelative      [SHELL32.40]
345  */
346 BOOL WINAPI PathIsRelativeAW (LPCVOID lpszPath)
347 {
348         if (SHELL_OsIsUnicode())
349           return PathIsRelativeW( lpszPath );
350         return PathIsRelativeA( lpszPath );
351 }
352
353 /*************************************************************************
354  * PathIsRoot           [SHELL32.29]
355  */
356 BOOL WINAPI PathIsRootAW(LPCVOID lpszPath)
357 {
358         if (SHELL_OsIsUnicode())
359           return PathIsRootW(lpszPath);
360         return PathIsRootA(lpszPath);
361 }
362
363 /*************************************************************************
364  *  PathIsExeA          [internal]
365  */
366 static BOOL PathIsExeA (LPCSTR lpszPath)
367 {
368         LPCSTR lpszExtension = PathGetExtensionA(lpszPath);
369         int i;
370         static const char * const lpszExtensions[] =
371             {"exe", "com", "pif", "cmd", "bat", "scf", "scr", NULL };
372
373         TRACE("path=%s\n",lpszPath);
374
375         for(i=0; lpszExtensions[i]; i++)
376           if (!lstrcmpiA(lpszExtension,lpszExtensions[i])) return TRUE;
377
378         return FALSE;
379 }
380
381 /*************************************************************************
382  *  PathIsExeW          [internal]
383  */
384 static BOOL PathIsExeW (LPCWSTR lpszPath)
385 {
386         LPCWSTR lpszExtension = PathGetExtensionW(lpszPath);
387         int i;
388         static const WCHAR lpszExtensions[][4] =
389             {{'e','x','e','\0'}, {'c','o','m','\0'}, {'p','i','f','\0'},
390              {'c','m','d','\0'}, {'b','a','t','\0'}, {'s','c','f','\0'},
391              {'s','c','r','\0'}, {'\0'} };
392
393         TRACE("path=%s\n",debugstr_w(lpszPath));
394
395         for(i=0; lpszExtensions[i][0]; i++)
396           if (!strcmpiW(lpszExtension,lpszExtensions[i])) return TRUE;
397
398         return FALSE;
399 }
400
401 /*************************************************************************
402  *  PathIsExe           [SHELL32.43]
403  */
404 BOOL WINAPI PathIsExeAW (LPCVOID path)
405 {
406         if (SHELL_OsIsUnicode())
407           return PathIsExeW (path);
408         return PathIsExeA(path);
409 }
410
411 /*************************************************************************
412  * PathIsDirectory      [SHELL32.159]
413  */
414 BOOL WINAPI PathIsDirectoryAW (LPCVOID lpszPath)
415 {
416         if (SHELL_OsIsUnicode())
417           return PathIsDirectoryW (lpszPath);
418         return PathIsDirectoryA (lpszPath);
419 }
420
421 /*************************************************************************
422  * PathFileExists       [SHELL32.45]
423  */
424 BOOL WINAPI PathFileExistsAW (LPCVOID lpszPath)
425 {
426         if (SHELL_OsIsUnicode())
427           return PathFileExistsW (lpszPath);
428         return PathFileExistsA (lpszPath);
429 }
430
431 /*************************************************************************
432  * PathMatchSpec        [SHELL32.46]
433  */
434 BOOL WINAPI PathMatchSpecAW(LPVOID name, LPVOID mask)
435 {
436         if (SHELL_OsIsUnicode())
437           return PathMatchSpecW( name, mask );
438         return PathMatchSpecA( name, mask );
439 }
440
441 /*************************************************************************
442  * PathIsSameRoot       [SHELL32.650]
443  */
444 BOOL WINAPI PathIsSameRootAW(LPCVOID lpszPath1, LPCVOID lpszPath2)
445 {
446         if (SHELL_OsIsUnicode())
447           return PathIsSameRootW(lpszPath1, lpszPath2);
448         return PathIsSameRootA(lpszPath1, lpszPath2);
449 }
450
451 /*************************************************************************
452  * IsLFNDriveA          [SHELL32.41]
453  */
454 BOOL WINAPI IsLFNDriveA(LPCSTR lpszPath)
455 {
456     DWORD       fnlen;
457
458     if (!GetVolumeInformationA(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
459         return FALSE;
460     return fnlen > 12;
461 }
462
463 /*************************************************************************
464  * IsLFNDriveW          [SHELL32.42]
465  */
466 BOOL WINAPI IsLFNDriveW(LPCWSTR lpszPath)
467 {
468     DWORD       fnlen;
469
470     if (!GetVolumeInformationW(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
471         return FALSE;
472     return fnlen > 12;
473 }
474
475 /*************************************************************************
476  * IsLFNDrive           [SHELL32.119]
477  */
478 BOOL WINAPI IsLFNDriveAW(LPCVOID lpszPath)
479 {
480         if (SHELL_OsIsUnicode())
481           return IsLFNDriveW(lpszPath);
482         return IsLFNDriveA(lpszPath);
483 }
484
485 /*
486         ########## Creating Something Unique ##########
487 */
488 /*************************************************************************
489  * PathMakeUniqueNameA  [internal]
490  */
491 static BOOL PathMakeUniqueNameA(
492         LPSTR lpszBuffer,
493         DWORD dwBuffSize,
494         LPCSTR lpszShortName,
495         LPCSTR lpszLongName,
496         LPCSTR lpszPathName)
497 {
498         FIXME("%p %u %s %s %s stub\n",
499          lpszBuffer, dwBuffSize, debugstr_a(lpszShortName),
500          debugstr_a(lpszLongName), debugstr_a(lpszPathName));
501         return TRUE;
502 }
503
504 /*************************************************************************
505  * PathMakeUniqueNameW  [internal]
506  */
507 static BOOL PathMakeUniqueNameW(
508         LPWSTR lpszBuffer,
509         DWORD dwBuffSize,
510         LPCWSTR lpszShortName,
511         LPCWSTR lpszLongName,
512         LPCWSTR lpszPathName)
513 {
514         FIXME("%p %u %s %s %s stub\n",
515          lpszBuffer, dwBuffSize, debugstr_w(lpszShortName),
516          debugstr_w(lpszLongName), debugstr_w(lpszPathName));
517         return TRUE;
518 }
519
520 /*************************************************************************
521  * PathMakeUniqueName   [SHELL32.47]
522  */
523 BOOL WINAPI PathMakeUniqueNameAW(
524         LPVOID lpszBuffer,
525         DWORD dwBuffSize,
526         LPCVOID lpszShortName,
527         LPCVOID lpszLongName,
528         LPCVOID lpszPathName)
529 {
530         if (SHELL_OsIsUnicode())
531           return PathMakeUniqueNameW(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
532         return PathMakeUniqueNameA(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
533 }
534
535 /*************************************************************************
536  * PathYetAnotherMakeUniqueName [SHELL32.75]
537  *
538  * NOTES
539  *     exported by ordinal
540  */
541 BOOL WINAPI PathYetAnotherMakeUniqueName(
542         LPWSTR lpszBuffer,
543         LPCWSTR lpszPathName,
544         LPCWSTR lpszShortName,
545         LPCWSTR lpszLongName)
546 {
547     FIXME("(%p, %s, %s ,%s):stub.\n",
548           lpszBuffer, debugstr_w(lpszPathName), debugstr_w(lpszShortName), debugstr_w(lpszLongName));
549     return TRUE;
550 }
551
552
553 /*
554         ########## cleaning and resolving paths ##########
555  */
556
557 /*************************************************************************
558  * PathFindOnPath       [SHELL32.145]
559  */
560 BOOL WINAPI PathFindOnPathAW(LPVOID sFile, LPCVOID *sOtherDirs)
561 {
562         if (SHELL_OsIsUnicode())
563           return PathFindOnPathW(sFile, (LPCWSTR *)sOtherDirs);
564         return PathFindOnPathA(sFile, (LPCSTR *)sOtherDirs);
565 }
566
567 /*************************************************************************
568  * PathCleanupSpec      [SHELL32.171]
569  *
570  * lpszFile is changed in place.
571  */
572 int WINAPI PathCleanupSpec( LPCWSTR lpszPathW, LPWSTR lpszFileW )
573 {
574     int i = 0;
575     DWORD rc = 0;
576     int length = 0;
577
578     if (SHELL_OsIsUnicode())
579     {
580         LPWSTR p = lpszFileW;
581
582         TRACE("Cleanup %s\n",debugstr_w(lpszFileW));
583
584         if (lpszPathW)
585             length = strlenW(lpszPathW);
586
587         while (*p)
588         {
589             int gct = PathGetCharTypeW(*p);
590             if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
591             {
592                 lpszFileW[i]='-';
593                 rc |= PCS_REPLACEDCHAR;
594             }
595             else
596                 lpszFileW[i]=*p;
597             i++;
598             p++;
599             if (length + i == MAX_PATH)
600             {
601                 rc |= PCS_FATAL | PCS_PATHTOOLONG;
602                 break;
603             }
604         }
605         lpszFileW[i]=0;
606     }
607     else
608     {
609         LPSTR lpszFileA = (LPSTR)lpszFileW;
610         LPCSTR lpszPathA = (LPCSTR)lpszPathW;
611         LPSTR p = lpszFileA;
612
613         TRACE("Cleanup %s\n",debugstr_a(lpszFileA));
614
615         if (lpszPathA)
616             length = strlen(lpszPathA);
617
618         while (*p)
619         {
620             int gct = PathGetCharTypeA(*p);
621             if (gct == GCT_INVALID || gct == GCT_WILD || gct == GCT_SEPARATOR)
622             {
623                 lpszFileA[i]='-';
624                 rc |= PCS_REPLACEDCHAR;
625             }
626             else
627                 lpszFileA[i]=*p;
628             i++;
629             p++;
630             if (length + i == MAX_PATH)
631             {
632                 rc |= PCS_FATAL | PCS_PATHTOOLONG;
633                 break;
634             }
635         }
636         lpszFileA[i]=0;
637     }
638     return rc;
639 }
640
641 /*************************************************************************
642  * PathQualifyA         [SHELL32]
643  */
644 static BOOL PathQualifyA(LPCSTR pszPath)
645 {
646         FIXME("%s\n",pszPath);
647         return 0;
648 }
649
650 /*************************************************************************
651  * PathQualifyW         [SHELL32]
652  */
653 static BOOL PathQualifyW(LPCWSTR pszPath)
654 {
655         FIXME("%s\n",debugstr_w(pszPath));
656         return 0;
657 }
658
659 /*************************************************************************
660  * PathQualify  [SHELL32.49]
661  */
662 BOOL WINAPI PathQualifyAW(LPCVOID pszPath)
663 {
664         if (SHELL_OsIsUnicode())
665           return PathQualifyW(pszPath);
666         return PathQualifyA(pszPath);
667 }
668
669 static BOOL PathResolveA(
670         LPSTR lpszPath,
671         LPCSTR *alpszPaths,
672         DWORD dwFlags)
673 {
674         FIXME("(%s,%p,0x%08x),stub!\n",
675           lpszPath, *alpszPaths, dwFlags);
676         return 0;
677 }
678
679 static BOOL PathResolveW(
680         LPWSTR lpszPath,
681         LPCWSTR *alpszPaths,
682         DWORD dwFlags)
683 {
684         FIXME("(%s,%p,0x%08x),stub!\n",
685           debugstr_w(lpszPath), debugstr_w(*alpszPaths), dwFlags);
686         return 0;
687 }
688
689 /*************************************************************************
690  * PathResolve [SHELL32.51]
691  */
692 BOOL WINAPI PathResolveAW(
693         LPVOID lpszPath,
694         LPCVOID *alpszPaths,
695         DWORD dwFlags)
696 {
697         if (SHELL_OsIsUnicode())
698           return PathResolveW(lpszPath, (LPCWSTR*)alpszPaths, dwFlags);
699         return PathResolveA(lpszPath, (LPCSTR*)alpszPaths, dwFlags);
700 }
701
702 /*************************************************************************
703 *       PathProcessCommandA
704 */
705 static LONG PathProcessCommandA (
706         LPCSTR lpszPath,
707         LPSTR lpszBuff,
708         DWORD dwBuffSize,
709         DWORD dwFlags)
710 {
711         FIXME("%s %p 0x%04x 0x%04x stub\n",
712         lpszPath, lpszBuff, dwBuffSize, dwFlags);
713         if(!lpszPath) return -1;
714         if(lpszBuff) strcpy(lpszBuff, lpszPath);
715         return strlen(lpszPath);
716 }
717
718 /*************************************************************************
719 *       PathProcessCommandW
720 */
721 static LONG PathProcessCommandW (
722         LPCWSTR lpszPath,
723         LPWSTR lpszBuff,
724         DWORD dwBuffSize,
725         DWORD dwFlags)
726 {
727         FIXME("(%s, %p, 0x%04x, 0x%04x) stub\n",
728         debugstr_w(lpszPath), lpszBuff, dwBuffSize, dwFlags);
729         if(!lpszPath) return -1;
730         if(lpszBuff) strcpyW(lpszBuff, lpszPath);
731         return strlenW(lpszPath);
732 }
733
734 /*************************************************************************
735 *       PathProcessCommand (SHELL32.653)
736 */
737 LONG WINAPI PathProcessCommandAW (
738         LPCVOID lpszPath,
739         LPVOID lpszBuff,
740         DWORD dwBuffSize,
741         DWORD dwFlags)
742 {
743         if (SHELL_OsIsUnicode())
744           return PathProcessCommandW(lpszPath, lpszBuff, dwBuffSize, dwFlags);
745         return PathProcessCommandA(lpszPath, lpszBuff, dwBuffSize, dwFlags);
746 }
747
748 /*
749         ########## special ##########
750 */
751
752 /*************************************************************************
753  * PathSetDlgItemPath (SHELL32.48)
754  */
755 VOID WINAPI PathSetDlgItemPathAW(HWND hDlg, int id, LPCVOID pszPath)
756 {
757         if (SHELL_OsIsUnicode())
758             PathSetDlgItemPathW(hDlg, id, pszPath);
759         else
760             PathSetDlgItemPathA(hDlg, id, pszPath);
761 }
762
763 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'};
764 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'};
765 static const WCHAR AppDataW[] = {'A','p','p','D','a','t','a','\0'};
766 static const WCHAR CacheW[] = {'C','a','c','h','e','\0'};
767 static const WCHAR CD_BurningW[] = {'C','D',' ','B','u','r','n','i','n','g','\0'};
768 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'};
769 static const WCHAR Common_AppDataW[] = {'C','o','m','m','o','n',' ','A','p','p','D','a','t','a','\0'};
770 static const WCHAR Common_DesktopW[] = {'C','o','m','m','o','n',' ','D','e','s','k','t','o','p','\0'};
771 static const WCHAR Common_DocumentsW[] = {'C','o','m','m','o','n',' ','D','o','c','u','m','e','n','t','s','\0'};
772 static const WCHAR Common_FavoritesW[] = {'C','o','m','m','o','n',' ','F','a','v','o','r','i','t','e','s','\0'};
773 static const WCHAR CommonFilesDirW[] = {'C','o','m','m','o','n','F','i','l','e','s','D','i','r','\0'};
774 static const WCHAR CommonFilesDirX86W[] = {'C','o','m','m','o','n','F','i','l','e','s','D','i','r',' ','(','x','8','6',')','\0'};
775 static const WCHAR CommonMusicW[] = {'C','o','m','m','o','n','M','u','s','i','c','\0'};
776 static const WCHAR CommonPicturesW[] = {'C','o','m','m','o','n','P','i','c','t','u','r','e','s','\0'};
777 static const WCHAR Common_ProgramsW[] = {'C','o','m','m','o','n',' ','P','r','o','g','r','a','m','s','\0'};
778 static const WCHAR Common_StartUpW[] = {'C','o','m','m','o','n',' ','S','t','a','r','t','U','p','\0'};
779 static const WCHAR Common_Start_MenuW[] = {'C','o','m','m','o','n',' ','S','t','a','r','t',' ','M','e','n','u','\0'};
780 static const WCHAR Common_TemplatesW[] = {'C','o','m','m','o','n',' ','T','e','m','p','l','a','t','e','s','\0'};
781 static const WCHAR CommonVideoW[] = {'C','o','m','m','o','n','V','i','d','e','o','\0'};
782 static const WCHAR ContactsW[] = {'C','o','n','t','a','c','t','s','\0'};
783 static const WCHAR CookiesW[] = {'C','o','o','k','i','e','s','\0'};
784 static const WCHAR DesktopW[] = {'D','e','s','k','t','o','p','\0'};
785 static const WCHAR FavoritesW[] = {'F','a','v','o','r','i','t','e','s','\0'};
786 static const WCHAR FontsW[] = {'F','o','n','t','s','\0'};
787 static const WCHAR HistoryW[] = {'H','i','s','t','o','r','y','\0'};
788 static const WCHAR Local_AppDataW[] = {'L','o','c','a','l',' ','A','p','p','D','a','t','a','\0'};
789 static const WCHAR My_MusicW[] = {'M','y',' ','M','u','s','i','c','\0'};
790 static const WCHAR My_PicturesW[] = {'M','y',' ','P','i','c','t','u','r','e','s','\0'};
791 static const WCHAR My_VideoW[] = {'M','y',' ','V','i','d','e','o','s','\0'};
792 static const WCHAR NetHoodW[] = {'N','e','t','H','o','o','d','\0'};
793 static const WCHAR PersonalW[] = {'P','e','r','s','o','n','a','l','\0'};
794 static const WCHAR PrintHoodW[] = {'P','r','i','n','t','H','o','o','d','\0'};
795 static const WCHAR ProgramFilesDirW[] = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r','\0'};
796 static const WCHAR ProgramFilesDirX86W[] = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r',' ','(','x','8','6',')','\0'};
797 static const WCHAR ProgramsW[] = {'P','r','o','g','r','a','m','s','\0'};
798 static const WCHAR RecentW[] = {'R','e','c','e','n','t','\0'};
799 static const WCHAR ResourcesW[] = {'R','e','s','o','u','r','c','e','s','\0'};
800 static const WCHAR SendToW[] = {'S','e','n','d','T','o','\0'};
801 static const WCHAR StartUpW[] = {'S','t','a','r','t','U','p','\0'};
802 static const WCHAR Start_MenuW[] = {'S','t','a','r','t',' ','M','e','n','u','\0'};
803 static const WCHAR TemplatesW[] = {'T','e','m','p','l','a','t','e','s','\0'};
804 static const WCHAR UsersW[] = {'U','s','e','r','s','\0'};
805 static const WCHAR UsersPublicW[] = {'U','s','e','r','s','\\','P','u','b','l','i','c','\0'};
806 static const WCHAR DefaultW[] = {'.','D','e','f','a','u','l','t','\0'};
807 static const WCHAR AllUsersProfileW[] = {'%','A','L','L','U','S','E','R','S','P','R','O','F','I','L','E','%','\0'};
808 static const WCHAR UserProfileW[] = {'%','U','S','E','R','P','R','O','F','I','L','E','%','\0'};
809 static const WCHAR SystemDriveW[] = {'%','S','y','s','t','e','m','D','r','i','v','e','%','\0'};
810 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};
811 static const WCHAR ProfilesDirectoryW[] = {'P','r','o','f','i','l','e','s','D','i','r','e','c','t','o','r','y',0};
812 static const WCHAR AllUsersProfileValueW[] = {'A','l','l','U','s','e','r','s','P','r','o','f','i','l','e','\0'};
813 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'};
814 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'};
815 static const WCHAR szDefaultProfileDirW[] = {'u','s','e','r','s',0};
816 static const WCHAR AllUsersW[] = {'P','u','b','l','i','c',0};
817
818 typedef enum _CSIDL_Type {
819     CSIDL_Type_User,
820     CSIDL_Type_AllUsers,
821     CSIDL_Type_CurrVer,
822     CSIDL_Type_Disallowed,
823     CSIDL_Type_NonExistent,
824     CSIDL_Type_WindowsPath,
825     CSIDL_Type_SystemPath,
826     CSIDL_Type_SystemX86Path,
827 } CSIDL_Type;
828
829 typedef struct
830 {
831     const KNOWNFOLDERID *id;
832     CSIDL_Type type;
833     LPCWSTR    szValueName;
834     LPCWSTR    szDefaultPath; /* fallback string or resource ID */
835 } CSIDL_DATA;
836
837 static const CSIDL_DATA CSIDL_Data[] =
838 {
839     { /* 0x00 - CSIDL_DESKTOP */
840         &FOLDERID_Desktop,
841         CSIDL_Type_User,
842         DesktopW,
843         MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
844     },
845     { /* 0x01 - CSIDL_INTERNET */
846         &FOLDERID_InternetFolder,
847         CSIDL_Type_Disallowed,
848         NULL,
849         NULL
850     },
851     { /* 0x02 - CSIDL_PROGRAMS */
852         &FOLDERID_Programs,
853         CSIDL_Type_User,
854         ProgramsW,
855         MAKEINTRESOURCEW(IDS_PROGRAMS)
856     },
857     { /* 0x03 - CSIDL_CONTROLS (.CPL files) */
858         &FOLDERID_ControlPanelFolder,
859         CSIDL_Type_SystemPath,
860         NULL,
861         NULL
862     },
863     { /* 0x04 - CSIDL_PRINTERS */
864         &FOLDERID_PrintersFolder,
865         CSIDL_Type_SystemPath,
866         NULL,
867         NULL
868     },
869     { /* 0x05 - CSIDL_PERSONAL */
870         &GUID_NULL,
871         CSIDL_Type_User,
872         PersonalW,
873         MAKEINTRESOURCEW(IDS_PERSONAL)
874     },
875     { /* 0x06 - CSIDL_FAVORITES */
876         &FOLDERID_Favorites,
877         CSIDL_Type_User,
878         FavoritesW,
879         MAKEINTRESOURCEW(IDS_FAVORITES)
880     },
881     { /* 0x07 - CSIDL_STARTUP */
882         &FOLDERID_Startup,
883         CSIDL_Type_User,
884         StartUpW,
885         MAKEINTRESOURCEW(IDS_STARTUP)
886     },
887     { /* 0x08 - CSIDL_RECENT */
888         &FOLDERID_Recent,
889         CSIDL_Type_User,
890         RecentW,
891         MAKEINTRESOURCEW(IDS_RECENT)
892     },
893     { /* 0x09 - CSIDL_SENDTO */
894         &FOLDERID_SendTo,
895         CSIDL_Type_User,
896         SendToW,
897         MAKEINTRESOURCEW(IDS_SENDTO)
898     },
899     { /* 0x0a - CSIDL_BITBUCKET - Recycle Bin */
900         &FOLDERID_RecycleBinFolder,
901         CSIDL_Type_Disallowed,
902         NULL,
903         NULL,
904     },
905     { /* 0x0b - CSIDL_STARTMENU */
906         &FOLDERID_StartMenu,
907         CSIDL_Type_User,
908         Start_MenuW,
909         MAKEINTRESOURCEW(IDS_STARTMENU)
910     },
911     { /* 0x0c - CSIDL_MYDOCUMENTS */
912         &GUID_NULL,
913         CSIDL_Type_Disallowed, /* matches WinXP--can't get its path */
914         NULL,
915         NULL
916     },
917     { /* 0x0d - CSIDL_MYMUSIC */
918         &FOLDERID_Music,
919         CSIDL_Type_User,
920         My_MusicW,
921         MAKEINTRESOURCEW(IDS_MYMUSIC)
922     },
923     { /* 0x0e - CSIDL_MYVIDEO */
924         &FOLDERID_Videos,
925         CSIDL_Type_User,
926         My_VideoW,
927         MAKEINTRESOURCEW(IDS_MYVIDEO)
928     },
929     { /* 0x0f - unassigned */
930         &GUID_NULL,
931         CSIDL_Type_Disallowed,
932         NULL,
933         NULL,
934     },
935     { /* 0x10 - CSIDL_DESKTOPDIRECTORY */
936         &FOLDERID_Desktop,
937         CSIDL_Type_User,
938         DesktopW,
939         MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
940     },
941     { /* 0x11 - CSIDL_DRIVES */
942         &FOLDERID_ComputerFolder,
943         CSIDL_Type_Disallowed,
944         NULL,
945         NULL,
946     },
947     { /* 0x12 - CSIDL_NETWORK */
948         &FOLDERID_NetworkFolder,
949         CSIDL_Type_Disallowed,
950         NULL,
951         NULL,
952     },
953     { /* 0x13 - CSIDL_NETHOOD */
954         &FOLDERID_NetHood,
955         CSIDL_Type_User,
956         NetHoodW,
957         MAKEINTRESOURCEW(IDS_NETHOOD)
958     },
959     { /* 0x14 - CSIDL_FONTS */
960         &FOLDERID_Fonts,
961         CSIDL_Type_WindowsPath,
962         FontsW,
963         FontsW
964     },
965     { /* 0x15 - CSIDL_TEMPLATES */
966         &FOLDERID_Templates,
967         CSIDL_Type_User,
968         TemplatesW,
969         MAKEINTRESOURCEW(IDS_TEMPLATES)
970     },
971     { /* 0x16 - CSIDL_COMMON_STARTMENU */
972         &FOLDERID_CommonStartMenu,
973         CSIDL_Type_AllUsers,
974         Common_Start_MenuW,
975         MAKEINTRESOURCEW(IDS_STARTMENU)
976     },
977     { /* 0x17 - CSIDL_COMMON_PROGRAMS */
978         &FOLDERID_CommonPrograms,
979         CSIDL_Type_AllUsers,
980         Common_ProgramsW,
981         MAKEINTRESOURCEW(IDS_PROGRAMS)
982     },
983     { /* 0x18 - CSIDL_COMMON_STARTUP */
984         &FOLDERID_CommonStartup,
985         CSIDL_Type_AllUsers,
986         Common_StartUpW,
987         MAKEINTRESOURCEW(IDS_STARTUP)
988     },
989     { /* 0x19 - CSIDL_COMMON_DESKTOPDIRECTORY */
990         &FOLDERID_PublicDesktop,
991         CSIDL_Type_AllUsers,
992         Common_DesktopW,
993         MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
994     },
995     { /* 0x1a - CSIDL_APPDATA */
996         &FOLDERID_RoamingAppData,
997         CSIDL_Type_User,
998         AppDataW,
999         MAKEINTRESOURCEW(IDS_APPDATA)
1000     },
1001     { /* 0x1b - CSIDL_PRINTHOOD */
1002         &FOLDERID_PrintHood,
1003         CSIDL_Type_User,
1004         PrintHoodW,
1005         MAKEINTRESOURCEW(IDS_PRINTHOOD)
1006     },
1007     { /* 0x1c - CSIDL_LOCAL_APPDATA */
1008         &FOLDERID_LocalAppData,
1009         CSIDL_Type_User,
1010         Local_AppDataW,
1011         MAKEINTRESOURCEW(IDS_LOCAL_APPDATA)
1012     },
1013     { /* 0x1d - CSIDL_ALTSTARTUP */
1014         &GUID_NULL,
1015         CSIDL_Type_NonExistent,
1016         NULL,
1017         NULL
1018     },
1019     { /* 0x1e - CSIDL_COMMON_ALTSTARTUP */
1020         &GUID_NULL,
1021         CSIDL_Type_NonExistent,
1022         NULL,
1023         NULL
1024     },
1025     { /* 0x1f - CSIDL_COMMON_FAVORITES */
1026         &FOLDERID_Favorites,
1027         CSIDL_Type_AllUsers,
1028         Common_FavoritesW,
1029         MAKEINTRESOURCEW(IDS_FAVORITES)
1030     },
1031     { /* 0x20 - CSIDL_INTERNET_CACHE */
1032         &FOLDERID_InternetCache,
1033         CSIDL_Type_User,
1034         CacheW,
1035         MAKEINTRESOURCEW(IDS_INTERNET_CACHE)
1036     },
1037     { /* 0x21 - CSIDL_COOKIES */
1038         &FOLDERID_Cookies,
1039         CSIDL_Type_User,
1040         CookiesW,
1041         MAKEINTRESOURCEW(IDS_COOKIES)
1042     },
1043     { /* 0x22 - CSIDL_HISTORY */
1044         &FOLDERID_History,
1045         CSIDL_Type_User,
1046         HistoryW,
1047         MAKEINTRESOURCEW(IDS_HISTORY)
1048     },
1049     { /* 0x23 - CSIDL_COMMON_APPDATA */
1050         &FOLDERID_ProgramData,
1051         CSIDL_Type_AllUsers,
1052         Common_AppDataW,
1053         MAKEINTRESOURCEW(IDS_APPDATA)
1054     },
1055     { /* 0x24 - CSIDL_WINDOWS */
1056         &FOLDERID_Windows,
1057         CSIDL_Type_WindowsPath,
1058         NULL,
1059         NULL
1060     },
1061     { /* 0x25 - CSIDL_SYSTEM */
1062         &FOLDERID_System,
1063         CSIDL_Type_SystemPath,
1064         NULL,
1065         NULL
1066     },
1067     { /* 0x26 - CSIDL_PROGRAM_FILES */
1068         &FOLDERID_ProgramFiles,
1069         CSIDL_Type_CurrVer,
1070         ProgramFilesDirW,
1071         MAKEINTRESOURCEW(IDS_PROGRAM_FILES)
1072     },
1073     { /* 0x27 - CSIDL_MYPICTURES */
1074         &FOLDERID_Pictures,
1075         CSIDL_Type_User,
1076         My_PicturesW,
1077         MAKEINTRESOURCEW(IDS_MYPICTURES)
1078     },
1079     { /* 0x28 - CSIDL_PROFILE */
1080         &FOLDERID_Profile,
1081         CSIDL_Type_User,
1082         NULL,
1083         NULL
1084     },
1085     { /* 0x29 - CSIDL_SYSTEMX86 */
1086         &FOLDERID_SystemX86,
1087         CSIDL_Type_SystemX86Path,
1088         NULL,
1089         NULL
1090     },
1091     { /* 0x2a - CSIDL_PROGRAM_FILESX86 */
1092         &FOLDERID_ProgramFilesX86,
1093         CSIDL_Type_CurrVer,
1094         ProgramFilesDirX86W,
1095         MAKEINTRESOURCEW(IDS_PROGRAM_FILESX86)
1096     },
1097     { /* 0x2b - CSIDL_PROGRAM_FILES_COMMON */
1098         &FOLDERID_ProgramFilesCommon,
1099         CSIDL_Type_CurrVer,
1100         CommonFilesDirW,
1101         MAKEINTRESOURCEW(IDS_PROGRAM_FILES_COMMON)
1102     },
1103     { /* 0x2c - CSIDL_PROGRAM_FILES_COMMONX86 */
1104         &FOLDERID_ProgramFilesCommonX86,
1105         CSIDL_Type_CurrVer,
1106         CommonFilesDirX86W,
1107         MAKEINTRESOURCEW(IDS_PROGRAM_FILES_COMMONX86)
1108     },
1109     { /* 0x2d - CSIDL_COMMON_TEMPLATES */
1110         &FOLDERID_CommonTemplates,
1111         CSIDL_Type_AllUsers,
1112         Common_TemplatesW,
1113         MAKEINTRESOURCEW(IDS_TEMPLATES)
1114     },
1115     { /* 0x2e - CSIDL_COMMON_DOCUMENTS */
1116         &FOLDERID_PublicDocuments,
1117         CSIDL_Type_AllUsers,
1118         Common_DocumentsW,
1119         MAKEINTRESOURCEW(IDS_COMMON_DOCUMENTS)
1120     },
1121     { /* 0x2f - CSIDL_COMMON_ADMINTOOLS */
1122         &FOLDERID_CommonAdminTools,
1123         CSIDL_Type_AllUsers,
1124         Common_Administrative_ToolsW,
1125         MAKEINTRESOURCEW(IDS_ADMINTOOLS)
1126     },
1127     { /* 0x30 - CSIDL_ADMINTOOLS */
1128         &FOLDERID_AdminTools,
1129         CSIDL_Type_User,
1130         Administrative_ToolsW,
1131         MAKEINTRESOURCEW(IDS_ADMINTOOLS)
1132     },
1133     { /* 0x31 - CSIDL_CONNECTIONS */
1134         &FOLDERID_ConnectionsFolder,
1135         CSIDL_Type_Disallowed,
1136         NULL,
1137         NULL
1138     },
1139     { /* 0x32 - unassigned */
1140         &GUID_NULL,
1141         CSIDL_Type_Disallowed,
1142         NULL,
1143         NULL
1144     },
1145     { /* 0x33 - unassigned */
1146         &GUID_NULL,
1147         CSIDL_Type_Disallowed,
1148         NULL,
1149         NULL
1150     },
1151     { /* 0x34 - unassigned */
1152         &GUID_NULL,
1153         CSIDL_Type_Disallowed,
1154         NULL,
1155         NULL
1156     },
1157     { /* 0x35 - CSIDL_COMMON_MUSIC */
1158         &FOLDERID_PublicMusic,
1159         CSIDL_Type_AllUsers,
1160         CommonMusicW,
1161         MAKEINTRESOURCEW(IDS_COMMON_MUSIC)
1162     },
1163     { /* 0x36 - CSIDL_COMMON_PICTURES */
1164         &FOLDERID_PublicPictures,
1165         CSIDL_Type_AllUsers,
1166         CommonPicturesW,
1167         MAKEINTRESOURCEW(IDS_COMMON_PICTURES)
1168     },
1169     { /* 0x37 - CSIDL_COMMON_VIDEO */
1170         &FOLDERID_PublicVideos,
1171         CSIDL_Type_AllUsers,
1172         CommonVideoW,
1173         MAKEINTRESOURCEW(IDS_COMMON_VIDEO)
1174     },
1175     { /* 0x38 - CSIDL_RESOURCES */
1176         &FOLDERID_ResourceDir,
1177         CSIDL_Type_WindowsPath,
1178         NULL,
1179         ResourcesW
1180     },
1181     { /* 0x39 - CSIDL_RESOURCES_LOCALIZED */
1182         &FOLDERID_LocalizedResourcesDir,
1183         CSIDL_Type_NonExistent,
1184         NULL,
1185         NULL
1186     },
1187     { /* 0x3a - CSIDL_COMMON_OEM_LINKS */
1188         &FOLDERID_CommonOEMLinks,
1189         CSIDL_Type_AllUsers,
1190         NULL,
1191         MAKEINTRESOURCEW(IDS_COMMON_OEM_LINKS)
1192     },
1193     { /* 0x3b - CSIDL_CDBURN_AREA */
1194         &FOLDERID_CDBurning,
1195         CSIDL_Type_User,
1196         CD_BurningW,
1197         MAKEINTRESOURCEW(IDS_CDBURN_AREA)
1198     },
1199     { /* 0x3c unassigned */
1200         &GUID_NULL,
1201         CSIDL_Type_Disallowed,
1202         NULL,
1203         NULL
1204     },
1205     { /* 0x3d - CSIDL_COMPUTERSNEARME */
1206         &GUID_NULL,
1207         CSIDL_Type_Disallowed, /* FIXME */
1208         NULL,
1209         NULL
1210     },
1211     { /* 0x3e - CSIDL_PROFILES */
1212         &GUID_NULL,
1213         CSIDL_Type_Disallowed, /* oddly, this matches WinXP */
1214         NULL,
1215         NULL
1216     },
1217     { /* 0x3f */
1218         &FOLDERID_AddNewPrograms,
1219         CSIDL_Type_Disallowed,
1220         NULL,
1221         NULL
1222     },
1223     { /* 0x40 */
1224         &FOLDERID_AppUpdates,
1225         CSIDL_Type_Disallowed,
1226         NULL,
1227         NULL
1228     },
1229     { /* 0x41 */
1230         &FOLDERID_ChangeRemovePrograms,
1231         CSIDL_Type_Disallowed,
1232         NULL,
1233         NULL
1234     },
1235     { /* 0x42 */
1236         &FOLDERID_ConflictFolder,
1237         CSIDL_Type_Disallowed,
1238         NULL,
1239         NULL
1240     },
1241     { /* 0x43 */
1242         &FOLDERID_Contacts,
1243         CSIDL_Type_User,
1244         ContactsW,
1245         MAKEINTRESOURCEW(IDS_CONTACTS)
1246     },
1247     { /* 0x44 */
1248         &FOLDERID_DeviceMetadataStore,
1249         CSIDL_Type_Disallowed, /* FIXME */
1250         NULL,
1251         NULL
1252     },
1253     { /* 0x45 */
1254         &FOLDERID_Documents,
1255         CSIDL_Type_User,
1256         NULL,
1257         MAKEINTRESOURCEW(IDS_DOCUMENTS)
1258     },
1259     { /* 0x46 */
1260         &FOLDERID_DocumentsLibrary,
1261         CSIDL_Type_Disallowed, /* FIXME */
1262         NULL,
1263         NULL
1264     },
1265     { /* 0x47 */
1266         &FOLDERID_Downloads,
1267         CSIDL_Type_User,
1268         NULL,
1269         MAKEINTRESOURCEW(IDS_DOWNLOADS)
1270     },
1271     { /* 0x48 */
1272         &FOLDERID_Games,
1273         CSIDL_Type_Disallowed,
1274         NULL,
1275         NULL
1276     },
1277     { /* 0x49 */
1278         &FOLDERID_GameTasks,
1279         CSIDL_Type_Disallowed, /* FIXME */
1280         NULL,
1281         NULL
1282     },
1283     { /* 0x4a */
1284         &FOLDERID_HomeGroup,
1285         CSIDL_Type_Disallowed,
1286         NULL,
1287         NULL
1288     },
1289     { /* 0x4b */
1290         &FOLDERID_ImplicitAppShortcuts,
1291         CSIDL_Type_Disallowed, /* FIXME */
1292         NULL,
1293         NULL
1294     },
1295     { /* 0x4c */
1296         &FOLDERID_Libraries,
1297         CSIDL_Type_Disallowed, /* FIXME */
1298         NULL,
1299         NULL
1300     },
1301     { /* 0x4d */
1302         &FOLDERID_Links,
1303         CSIDL_Type_User,
1304         NULL,
1305         MAKEINTRESOURCEW(IDS_LINKS)
1306     },
1307     { /* 0x4e */
1308         &FOLDERID_LocalAppDataLow,
1309         CSIDL_Type_User,
1310         NULL,
1311         MAKEINTRESOURCEW(IDS_LOCAL_APPDATA_LOW)
1312     },
1313     { /* 0x4f */
1314         &FOLDERID_MusicLibrary,
1315         CSIDL_Type_Disallowed, /* FIXME */
1316         NULL,
1317         NULL
1318     },
1319     { /* 0x50 */
1320         &FOLDERID_OriginalImages,
1321         CSIDL_Type_Disallowed, /* FIXME */
1322         NULL,
1323         NULL
1324     },
1325     { /* 0x51 */
1326         &FOLDERID_PhotoAlbums,
1327         CSIDL_Type_User,
1328         NULL,
1329         MAKEINTRESOURCEW(IDS_PHOTO_ALBUMS)
1330     },
1331     { /* 0x52 */
1332         &FOLDERID_PicturesLibrary,
1333         CSIDL_Type_Disallowed, /* FIXME */
1334         NULL,
1335         NULL
1336     },
1337     { /* 0x53 */
1338         &FOLDERID_Playlists,
1339         CSIDL_Type_User,
1340         NULL,
1341         MAKEINTRESOURCEW(IDS_PLAYLISTS)
1342     },
1343     { /* 0x54 */
1344         &FOLDERID_ProgramFilesX64,
1345         CSIDL_Type_NonExistent,
1346         NULL,
1347         NULL
1348     },
1349     { /* 0x55 */
1350         &FOLDERID_ProgramFilesCommonX64,
1351         CSIDL_Type_NonExistent,
1352         NULL,
1353         NULL
1354     },
1355     { /* 0x56 */
1356         &FOLDERID_Public,
1357         CSIDL_Type_CurrVer, /* FIXME */
1358         NULL,
1359         UsersPublicW
1360     },
1361     { /* 0x57 */
1362         &FOLDERID_PublicDownloads,
1363         CSIDL_Type_AllUsers,
1364         NULL,
1365         MAKEINTRESOURCEW(IDS_PUBLIC_DOWNLOADS)
1366     },
1367     { /* 0x58 */
1368         &FOLDERID_PublicGameTasks,
1369         CSIDL_Type_AllUsers,
1370         NULL,
1371         MAKEINTRESOURCEW(IDS_PUBLIC_GAME_TASKS)
1372     },
1373     { /* 0x59 */
1374         &FOLDERID_PublicLibraries,
1375         CSIDL_Type_AllUsers,
1376         NULL,
1377         MAKEINTRESOURCEW(IDS_PUBLIC_LIBRARIES)
1378     },
1379     { /* 0x5a */
1380         &FOLDERID_PublicRingtones,
1381         CSIDL_Type_AllUsers,
1382         NULL,
1383         MAKEINTRESOURCEW(IDS_PUBLIC_RINGTONES)
1384     },
1385     { /* 0x5b */
1386         &FOLDERID_QuickLaunch,
1387         CSIDL_Type_Disallowed, /* FIXME */
1388         NULL,
1389         NULL
1390     },
1391     { /* 0x5c */
1392         &FOLDERID_RecordedTVLibrary,
1393         CSIDL_Type_Disallowed, /* FIXME */
1394         NULL,
1395         NULL
1396     },
1397     { /* 0x5d */
1398         &FOLDERID_Ringtones,
1399         CSIDL_Type_Disallowed, /* FIXME */
1400         NULL,
1401         NULL
1402     },
1403     { /* 0x5e */
1404         &FOLDERID_SampleMusic,
1405         CSIDL_Type_AllUsers,
1406         NULL,
1407         MAKEINTRESOURCEW(IDS_SAMPLE_MUSIC)
1408     },
1409     { /* 0x5f */
1410         &FOLDERID_SamplePictures,
1411         CSIDL_Type_AllUsers,
1412         NULL,
1413         MAKEINTRESOURCEW(IDS_SAMPLE_PICTURES)
1414     },
1415     { /* 0x60 */
1416         &FOLDERID_SamplePlaylists,
1417         CSIDL_Type_AllUsers,
1418         NULL,
1419         MAKEINTRESOURCEW(IDS_SAMPLE_PLAYLISTS)
1420     },
1421     { /* 0x61 */
1422         &FOLDERID_SampleVideos,
1423         CSIDL_Type_AllUsers,
1424         NULL,
1425         MAKEINTRESOURCEW(IDS_SAMPLE_VIDEOS)
1426     },
1427     { /* 0x62 */
1428         &FOLDERID_SavedGames,
1429         CSIDL_Type_User,
1430         NULL,
1431         MAKEINTRESOURCEW(IDS_SAVED_GAMES)
1432     },
1433     { /* 0x63 */
1434         &FOLDERID_SavedSearches,
1435         CSIDL_Type_User,
1436         NULL,
1437         MAKEINTRESOURCEW(IDS_SAVED_SEARCHES)
1438     },
1439     { /* 0x64 */
1440         &FOLDERID_SEARCH_CSC,
1441         CSIDL_Type_Disallowed,
1442         NULL,
1443         NULL
1444     },
1445     { /* 0x65 */
1446         &FOLDERID_SEARCH_MAPI,
1447         CSIDL_Type_Disallowed,
1448         NULL,
1449         NULL
1450     },
1451     { /* 0x66 */
1452         &FOLDERID_SearchHome,
1453         CSIDL_Type_Disallowed,
1454         NULL,
1455         NULL
1456     },
1457     { /* 0x67 */
1458         &FOLDERID_SidebarDefaultParts,
1459         CSIDL_Type_Disallowed, /* FIXME */
1460         NULL,
1461         NULL
1462     },
1463     { /* 0x68 */
1464         &FOLDERID_SidebarParts,
1465         CSIDL_Type_Disallowed, /* FIXME */
1466         NULL,
1467         NULL
1468     },
1469     { /* 0x69 */
1470         &FOLDERID_SyncManagerFolder,
1471         CSIDL_Type_Disallowed,
1472         NULL,
1473         NULL
1474     },
1475     { /* 0x6a */
1476         &FOLDERID_SyncResultsFolder,
1477         CSIDL_Type_Disallowed,
1478         NULL,
1479         NULL
1480     },
1481     { /* 0x6b */
1482         &FOLDERID_SyncSetupFolder,
1483         CSIDL_Type_Disallowed,
1484         NULL,
1485         NULL
1486     },
1487     { /* 0x6c */
1488         &FOLDERID_UserPinned,
1489         CSIDL_Type_Disallowed, /* FIXME */
1490         NULL,
1491         NULL
1492     },
1493     { /* 0x6d */
1494         &FOLDERID_UserProfiles,
1495         CSIDL_Type_CurrVer,
1496         UsersW,
1497         MAKEINTRESOURCEW(IDS_USER_PROFILES)
1498     },
1499     { /* 0x6e */
1500         &FOLDERID_UserProgramFiles,
1501         CSIDL_Type_Disallowed, /* FIXME */
1502         NULL,
1503         NULL
1504     },
1505     { /* 0x6f */
1506         &FOLDERID_UserProgramFilesCommon,
1507         CSIDL_Type_Disallowed, /* FIXME */
1508         NULL,
1509         NULL
1510     },
1511     { /* 0x70 */
1512         &FOLDERID_UsersFiles,
1513         CSIDL_Type_Disallowed,
1514         NULL,
1515         NULL
1516     },
1517     { /* 0x71 */
1518         &FOLDERID_UsersLibraries,
1519         CSIDL_Type_Disallowed,
1520         NULL,
1521         NULL
1522     },
1523     { /* 0x72 */
1524         &FOLDERID_VideosLibrary,
1525         CSIDL_Type_Disallowed, /* FIXME */
1526         NULL,
1527         NULL
1528     }
1529 };
1530
1531 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest);
1532
1533 /* Gets the value named value from the registry key
1534  * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
1535  * (or from rootKey\userPrefix\... if userPrefix is not NULL) into path, which
1536  * is assumed to be MAX_PATH WCHARs in length.
1537  * If it exists, expands the value and writes the expanded value to
1538  * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
1539  * Returns successful error code if the value was retrieved from the registry,
1540  * and a failure otherwise.
1541  */
1542 static HRESULT _SHGetUserShellFolderPath(HKEY rootKey, LPCWSTR userPrefix,
1543  LPCWSTR value, LPWSTR path)
1544 {
1545     HRESULT hr;
1546     WCHAR shellFolderPath[MAX_PATH], userShellFolderPath[MAX_PATH];
1547     LPCWSTR pShellFolderPath, pUserShellFolderPath;
1548     DWORD dwType, dwPathLen = MAX_PATH;
1549     HKEY userShellFolderKey, shellFolderKey;
1550
1551     TRACE("%p,%s,%s,%p\n",rootKey, debugstr_w(userPrefix), debugstr_w(value),
1552      path);
1553
1554     if (userPrefix)
1555     {
1556         strcpyW(shellFolderPath, userPrefix);
1557         PathAddBackslashW(shellFolderPath);
1558         strcatW(shellFolderPath, szSHFolders);
1559         pShellFolderPath = shellFolderPath;
1560         strcpyW(userShellFolderPath, userPrefix);
1561         PathAddBackslashW(userShellFolderPath);
1562         strcatW(userShellFolderPath, szSHUserFolders);
1563         pUserShellFolderPath = userShellFolderPath;
1564     }
1565     else
1566     {
1567         pUserShellFolderPath = szSHUserFolders;
1568         pShellFolderPath = szSHFolders;
1569     }
1570
1571     if (RegCreateKeyW(rootKey, pShellFolderPath, &shellFolderKey))
1572     {
1573         TRACE("Failed to create %s\n", debugstr_w(pShellFolderPath));
1574         return E_FAIL;
1575     }
1576     if (RegCreateKeyW(rootKey, pUserShellFolderPath, &userShellFolderKey))
1577     {
1578         TRACE("Failed to create %s\n",
1579          debugstr_w(pUserShellFolderPath));
1580         RegCloseKey(shellFolderKey);
1581         return E_FAIL;
1582     }
1583
1584     if (!RegQueryValueExW(userShellFolderKey, value, NULL, &dwType,
1585      (LPBYTE)path, &dwPathLen) && (dwType == REG_EXPAND_SZ || dwType == REG_SZ))
1586     {
1587         LONG ret;
1588
1589         path[dwPathLen / sizeof(WCHAR)] = '\0';
1590         if (dwType == REG_EXPAND_SZ && path[0] == '%')
1591         {
1592             WCHAR szTemp[MAX_PATH];
1593
1594             _SHExpandEnvironmentStrings(path, szTemp);
1595             lstrcpynW(path, szTemp, MAX_PATH);
1596         }
1597         ret = RegSetValueExW(shellFolderKey, value, 0, REG_SZ, (LPBYTE)path,
1598          (strlenW(path) + 1) * sizeof(WCHAR));
1599         if (ret != ERROR_SUCCESS)
1600             hr = HRESULT_FROM_WIN32(ret);
1601         else
1602             hr = S_OK;
1603     }
1604     else
1605         hr = E_FAIL;
1606     RegCloseKey(shellFolderKey);
1607     RegCloseKey(userShellFolderKey);
1608     TRACE("returning 0x%08x\n", hr);
1609     return hr;
1610 }
1611
1612 /* Gets a 'semi-expanded' default value of the CSIDL with index folder into
1613  * pszPath, based on the entries in CSIDL_Data.  By semi-expanded, I mean:
1614  * - The entry's szDefaultPath may be either a string value or an integer
1615  *   resource identifier.  In the latter case, the string value of the resource
1616  *   is written.
1617  * - Depending on the entry's type, the path may begin with an (unexpanded)
1618  *   environment variable name.  The caller is responsible for expanding
1619  *   environment strings if so desired.
1620  *   The types that are prepended with environment variables are:
1621  *   CSIDL_Type_User:     %USERPROFILE%
1622  *   CSIDL_Type_AllUsers: %ALLUSERSPROFILE%
1623  *   CSIDL_Type_CurrVer:  %SystemDrive%
1624  *   (Others might make sense too, but as yet are unneeded.)
1625  */
1626 static HRESULT _SHGetDefaultValue(BYTE folder, LPWSTR pszPath)
1627 {
1628     HRESULT hr;
1629     WCHAR resourcePath[MAX_PATH];
1630     LPCWSTR pDefaultPath = NULL;
1631
1632     TRACE("0x%02x,%p\n", folder, pszPath);
1633
1634     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1635         return E_INVALIDARG;
1636     if (!pszPath)
1637         return E_INVALIDARG;
1638
1639     if (!is_win64)
1640     {
1641         BOOL is_wow64;
1642
1643         switch (folder)
1644         {
1645         case CSIDL_PROGRAM_FILES:
1646         case CSIDL_PROGRAM_FILESX86:
1647             IsWow64Process( GetCurrentProcess(), &is_wow64 );
1648             folder = is_wow64 ? CSIDL_PROGRAM_FILESX86 : CSIDL_PROGRAM_FILES;
1649             break;
1650         case CSIDL_PROGRAM_FILES_COMMON:
1651         case CSIDL_PROGRAM_FILES_COMMONX86:
1652             IsWow64Process( GetCurrentProcess(), &is_wow64 );
1653             folder = is_wow64 ? CSIDL_PROGRAM_FILES_COMMONX86 : CSIDL_PROGRAM_FILES_COMMON;
1654             break;
1655         }
1656     }
1657
1658     if (CSIDL_Data[folder].szDefaultPath &&
1659      IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath))
1660     {
1661         if (LoadStringW(shell32_hInstance,
1662          LOWORD(CSIDL_Data[folder].szDefaultPath), resourcePath, MAX_PATH))
1663         {
1664             hr = S_OK;
1665             pDefaultPath = resourcePath;
1666         }
1667         else
1668         {
1669             FIXME("(%d,%s), LoadString failed, missing translation?\n", folder,
1670              debugstr_w(pszPath));
1671             hr = E_FAIL;
1672         }
1673     }
1674     else
1675     {
1676         hr = S_OK;
1677         pDefaultPath = CSIDL_Data[folder].szDefaultPath;
1678     }
1679     if (SUCCEEDED(hr))
1680     {
1681         switch (CSIDL_Data[folder].type)
1682         {
1683             case CSIDL_Type_User:
1684                 strcpyW(pszPath, UserProfileW);
1685                 break;
1686             case CSIDL_Type_AllUsers:
1687                 strcpyW(pszPath, AllUsersProfileW);
1688                 break;
1689             case CSIDL_Type_CurrVer:
1690                 strcpyW(pszPath, SystemDriveW);
1691                 break;
1692             default:
1693                 ; /* no corresponding env. var, do nothing */
1694         }
1695         if (pDefaultPath)
1696         {
1697             PathAddBackslashW(pszPath);
1698             strcatW(pszPath, pDefaultPath);
1699         }
1700     }
1701     TRACE("returning 0x%08x\n", hr);
1702     return hr;
1703 }
1704
1705 /* Gets the (unexpanded) value of the folder with index folder into pszPath.
1706  * The folder's type is assumed to be CSIDL_Type_CurrVer.  Its default value
1707  * can be overridden in the HKLM\\szCurrentVersion key.
1708  * If dwFlags has SHGFP_TYPE_DEFAULT set or if the value isn't overridden in
1709  * the registry, uses _SHGetDefaultValue to get the value.
1710  */
1711 static HRESULT _SHGetCurrentVersionPath(DWORD dwFlags, BYTE folder,
1712  LPWSTR pszPath)
1713 {
1714     HRESULT hr;
1715
1716     TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
1717
1718     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1719         return E_INVALIDARG;
1720     if (CSIDL_Data[folder].type != CSIDL_Type_CurrVer)
1721         return E_INVALIDARG;
1722     if (!pszPath)
1723         return E_INVALIDARG;
1724
1725     if (dwFlags & SHGFP_TYPE_DEFAULT)
1726         hr = _SHGetDefaultValue(folder, pszPath);
1727     else
1728     {
1729         HKEY hKey;
1730
1731         if (RegCreateKeyW(HKEY_LOCAL_MACHINE, szCurrentVersion, &hKey))
1732             hr = E_FAIL;
1733         else
1734         {
1735             DWORD dwType, dwPathLen = MAX_PATH * sizeof(WCHAR);
1736
1737             if (RegQueryValueExW(hKey, CSIDL_Data[folder].szValueName, NULL,
1738              &dwType, (LPBYTE)pszPath, &dwPathLen) ||
1739              (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
1740             {
1741                 hr = _SHGetDefaultValue(folder, pszPath);
1742                 dwType = REG_EXPAND_SZ;
1743                 switch (folder)
1744                 {
1745                 case CSIDL_PROGRAM_FILESX86:
1746                 case CSIDL_PROGRAM_FILES_COMMONX86:
1747                     /* these two should never be set on 32-bit setups */
1748                     if (!is_win64)
1749                     {
1750                         BOOL is_wow64;
1751                         IsWow64Process( GetCurrentProcess(), &is_wow64 );
1752                         if (!is_wow64) break;
1753                     }
1754                     /* fall through */
1755                 default:
1756                     RegSetValueExW(hKey, CSIDL_Data[folder].szValueName, 0, dwType,
1757                                    (LPBYTE)pszPath, (strlenW(pszPath)+1)*sizeof(WCHAR));
1758                 }
1759             }
1760             else
1761             {
1762                 pszPath[dwPathLen / sizeof(WCHAR)] = '\0';
1763                 hr = S_OK;
1764             }
1765             RegCloseKey(hKey);
1766         }
1767     }
1768     TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1769     return hr;
1770 }
1771
1772 static LPWSTR _GetUserSidStringFromToken(HANDLE Token)
1773 {
1774     char InfoBuffer[64];
1775     PTOKEN_USER UserInfo;
1776     DWORD InfoSize;
1777     LPWSTR SidStr;
1778
1779     UserInfo = (PTOKEN_USER) InfoBuffer;
1780     if (! GetTokenInformation(Token, TokenUser, InfoBuffer, sizeof(InfoBuffer),
1781                               &InfoSize))
1782     {
1783         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1784             return NULL;
1785         UserInfo = HeapAlloc(GetProcessHeap(), 0, InfoSize);
1786         if (UserInfo == NULL)
1787             return NULL;
1788         if (! GetTokenInformation(Token, TokenUser, UserInfo, InfoSize,
1789                                   &InfoSize))
1790         {
1791             HeapFree(GetProcessHeap(), 0, UserInfo);
1792             return NULL;
1793         }
1794     }
1795
1796     if (! ConvertSidToStringSidW(UserInfo->User.Sid, &SidStr))
1797         SidStr = NULL;
1798
1799     if (UserInfo != (PTOKEN_USER) InfoBuffer)
1800         HeapFree(GetProcessHeap(), 0, UserInfo);
1801
1802     return SidStr;
1803 }
1804
1805 /* Gets the user's path (unexpanded) for the CSIDL with index folder:
1806  * If SHGFP_TYPE_DEFAULT is set, calls _SHGetDefaultValue for it.  Otherwise
1807  * calls _SHGetUserShellFolderPath for it.  Where it looks depends on hToken:
1808  * - if hToken is -1, looks in HKEY_USERS\.Default
1809  * - otherwise looks first in HKEY_CURRENT_USER, followed by HKEY_LOCAL_MACHINE
1810  *   if HKEY_CURRENT_USER doesn't contain any entries.  If both fail, finally
1811  *   calls _SHGetDefaultValue for it.
1812  */
1813 static HRESULT _SHGetUserProfilePath(HANDLE hToken, DWORD dwFlags, BYTE folder,
1814  LPWSTR pszPath)
1815 {
1816     HRESULT hr;
1817
1818     TRACE("%p,0x%08x,0x%02x,%p\n", hToken, dwFlags, folder, pszPath);
1819
1820     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1821         return E_INVALIDARG;
1822     if (CSIDL_Data[folder].type != CSIDL_Type_User)
1823         return E_INVALIDARG;
1824     if (!pszPath)
1825         return E_INVALIDARG;
1826
1827     if (dwFlags & SHGFP_TYPE_DEFAULT)
1828     {
1829         if (hToken != NULL && hToken != (HANDLE)-1)
1830         {
1831             FIXME("unsupported for user other than current or default\n");
1832             return E_FAIL;
1833         }
1834         hr = _SHGetDefaultValue(folder, pszPath);
1835     }
1836     else
1837     {
1838         LPCWSTR userPrefix = NULL;
1839         HKEY hRootKey;
1840
1841         if (hToken == (HANDLE)-1)
1842         {
1843             hRootKey = HKEY_USERS;
1844             userPrefix = DefaultW;
1845         }
1846         else if (hToken == NULL)
1847             hRootKey = HKEY_CURRENT_USER;
1848         else
1849         {
1850             hRootKey = HKEY_USERS;
1851             userPrefix = _GetUserSidStringFromToken(hToken);
1852             if (userPrefix == NULL)
1853             {
1854                 hr = E_FAIL;
1855                 goto error;
1856             }
1857         }
1858         hr = _SHGetUserShellFolderPath(hRootKey, userPrefix,
1859          CSIDL_Data[folder].szValueName, pszPath);
1860         if (FAILED(hr) && hRootKey != HKEY_LOCAL_MACHINE)
1861             hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL,
1862              CSIDL_Data[folder].szValueName, pszPath);
1863         if (FAILED(hr))
1864             hr = _SHGetDefaultValue(folder, pszPath);
1865         if (userPrefix != NULL && userPrefix != DefaultW)
1866             LocalFree((HLOCAL) userPrefix);
1867     }
1868 error:
1869     TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1870     return hr;
1871 }
1872
1873 /* Gets the (unexpanded) path for the CSIDL with index folder.  If dwFlags has
1874  * SHGFP_TYPE_DEFAULT set, calls _SHGetDefaultValue.  Otherwise calls
1875  * _SHGetUserShellFolderPath for it, looking only in HKEY_LOCAL_MACHINE.
1876  * If this fails, falls back to _SHGetDefaultValue.
1877  */
1878 static HRESULT _SHGetAllUsersProfilePath(DWORD dwFlags, BYTE folder,
1879  LPWSTR pszPath)
1880 {
1881     HRESULT hr;
1882
1883     TRACE("0x%08x,0x%02x,%p\n", dwFlags, folder, pszPath);
1884
1885     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1886         return E_INVALIDARG;
1887     if (CSIDL_Data[folder].type != CSIDL_Type_AllUsers)
1888         return E_INVALIDARG;
1889     if (!pszPath)
1890         return E_INVALIDARG;
1891
1892     if (dwFlags & SHGFP_TYPE_DEFAULT)
1893         hr = _SHGetDefaultValue(folder, pszPath);
1894     else
1895     {
1896         hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL,
1897          CSIDL_Data[folder].szValueName, pszPath);
1898         if (FAILED(hr))
1899             hr = _SHGetDefaultValue(folder, pszPath);
1900     }
1901     TRACE("returning 0x%08x (output path is %s)\n", hr, debugstr_w(pszPath));
1902     return hr;
1903 }
1904
1905 static HRESULT _SHOpenProfilesKey(PHKEY pKey)
1906 {
1907     LONG lRet;
1908     DWORD disp;
1909
1910     lRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, ProfileListW, 0, NULL, 0,
1911      KEY_ALL_ACCESS, NULL, pKey, &disp);
1912     return HRESULT_FROM_WIN32(lRet);
1913 }
1914
1915 /* Reads the value named szValueName from the key profilesKey (assumed to be
1916  * opened by _SHOpenProfilesKey) into szValue, which is assumed to be MAX_PATH
1917  * WCHARs in length.  If it doesn't exist, returns szDefault (and saves
1918  * szDefault to the registry).
1919  */
1920 static HRESULT _SHGetProfilesValue(HKEY profilesKey, LPCWSTR szValueName,
1921  LPWSTR szValue, LPCWSTR szDefault)
1922 {
1923     HRESULT hr;
1924     DWORD type, dwPathLen = MAX_PATH * sizeof(WCHAR);
1925     LONG lRet;
1926
1927     TRACE("%p,%s,%p,%s\n", profilesKey, debugstr_w(szValueName), szValue,
1928      debugstr_w(szDefault));
1929     lRet = RegQueryValueExW(profilesKey, szValueName, NULL, &type,
1930      (LPBYTE)szValue, &dwPathLen);
1931     if (!lRet && (type == REG_SZ || type == REG_EXPAND_SZ) && dwPathLen
1932      && *szValue)
1933     {
1934         dwPathLen /= sizeof(WCHAR);
1935         szValue[dwPathLen] = '\0';
1936         hr = S_OK;
1937     }
1938     else
1939     {
1940         /* Missing or invalid value, set a default */
1941         lstrcpynW(szValue, szDefault, MAX_PATH);
1942         TRACE("Setting missing value %s to %s\n", debugstr_w(szValueName),
1943                                                   debugstr_w(szValue));
1944         lRet = RegSetValueExW(profilesKey, szValueName, 0, REG_EXPAND_SZ,
1945                               (LPBYTE)szValue,
1946                               (strlenW(szValue) + 1) * sizeof(WCHAR));
1947         if (lRet)
1948             hr = HRESULT_FROM_WIN32(lRet);
1949         else
1950             hr = S_OK;
1951     }
1952     TRACE("returning 0x%08x (output value is %s)\n", hr, debugstr_w(szValue));
1953     return hr;
1954 }
1955
1956 /* Attempts to expand environment variables from szSrc into szDest, which is
1957  * assumed to be MAX_PATH characters in length.  Before referring to the
1958  * environment, handles a few variables directly, because the environment
1959  * variables may not be set when this is called (as during Wine's installation
1960  * when default values are being written to the registry).
1961  * The directly handled environment variables, and their source, are:
1962  * - ALLUSERSPROFILE, USERPROFILE: reads from the registry
1963  * - SystemDrive: uses GetSystemDirectoryW and uses the drive portion of its
1964  *   path
1965  * If one of the directly handled environment variables is expanded, only
1966  * expands a single variable, and only in the beginning of szSrc.
1967  */
1968 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest)
1969 {
1970     HRESULT hr;
1971     WCHAR szTemp[MAX_PATH], szProfilesPrefix[MAX_PATH] = { 0 };
1972     HKEY key = NULL;
1973
1974     TRACE("%s, %p\n", debugstr_w(szSrc), szDest);
1975
1976     if (!szSrc || !szDest) return E_INVALIDARG;
1977
1978     /* short-circuit if there's nothing to expand */
1979     if (szSrc[0] != '%')
1980     {
1981         strcpyW(szDest, szSrc);
1982         hr = S_OK;
1983         goto end;
1984     }
1985     /* Get the profile prefix, we'll probably be needing it */
1986     hr = _SHOpenProfilesKey(&key);
1987     if (SUCCEEDED(hr))
1988     {
1989         WCHAR def_val[MAX_PATH];
1990
1991         /* get the system drive */
1992         GetSystemDirectoryW(def_val, MAX_PATH);
1993         if (def_val[1] == ':') strcpyW( def_val + 3, szDefaultProfileDirW );
1994         else FIXME("non-drive system paths unsupported\n");
1995
1996         hr = _SHGetProfilesValue(key, ProfilesDirectoryW, szProfilesPrefix, def_val );
1997     }
1998
1999     *szDest = 0;
2000     strcpyW(szTemp, szSrc);
2001     while (SUCCEEDED(hr) && szTemp[0] == '%')
2002     {
2003         if (!strncmpiW(szTemp, AllUsersProfileW, strlenW(AllUsersProfileW)))
2004         {
2005             WCHAR szAllUsers[MAX_PATH];
2006
2007             strcpyW(szDest, szProfilesPrefix);
2008             hr = _SHGetProfilesValue(key, AllUsersProfileValueW,
2009              szAllUsers, AllUsersW);
2010             PathAppendW(szDest, szAllUsers);
2011             PathAppendW(szDest, szTemp + strlenW(AllUsersProfileW));
2012         }
2013         else if (!strncmpiW(szTemp, UserProfileW, strlenW(UserProfileW)))
2014         {
2015             WCHAR userName[MAX_PATH];
2016             DWORD userLen = MAX_PATH;
2017
2018             strcpyW(szDest, szProfilesPrefix);
2019             GetUserNameW(userName, &userLen);
2020             PathAppendW(szDest, userName);
2021             PathAppendW(szDest, szTemp + strlenW(UserProfileW));
2022         }
2023         else if (!strncmpiW(szTemp, SystemDriveW, strlenW(SystemDriveW)))
2024         {
2025             GetSystemDirectoryW(szDest, MAX_PATH);
2026             if (szDest[1] != ':')
2027             {
2028                 FIXME("non-drive system paths unsupported\n");
2029                 hr = E_FAIL;
2030             }
2031             else
2032             {
2033                 strcpyW(szDest + 3, szTemp + strlenW(SystemDriveW) + 1);
2034                 hr = S_OK;
2035             }
2036         }
2037         else
2038         {
2039             DWORD ret = ExpandEnvironmentStringsW(szSrc, szDest, MAX_PATH);
2040
2041             if (ret > MAX_PATH)
2042                 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
2043             else if (ret == 0)
2044                 hr = HRESULT_FROM_WIN32(GetLastError());
2045             else
2046                 hr = S_OK;
2047         }
2048         if (SUCCEEDED(hr) && szDest[0] == '%')
2049             strcpyW(szTemp, szDest);
2050         else
2051         {
2052             /* terminate loop */
2053             szTemp[0] = '\0';
2054         }
2055     }
2056 end:
2057     if (key)
2058         RegCloseKey(key);
2059     TRACE("returning 0x%08x (input was %s, output is %s)\n", hr,
2060      debugstr_w(szSrc), debugstr_w(szDest));
2061     return hr;
2062 }
2063
2064 /*************************************************************************
2065  * SHGetFolderPathW                     [SHELL32.@]
2066  *
2067  * Convert nFolder to path.  
2068  *
2069  * RETURNS
2070  *  Success: S_OK
2071  *  Failure: standard HRESULT error codes.
2072  *
2073  * NOTES
2074  * Most values can be overridden in either
2075  * HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
2076  * or in the same location in HKLM.
2077  * The "Shell Folders" registry key was used in NT4 and earlier systems.
2078  * Beginning with Windows 2000, the "User Shell Folders" key is used, so
2079  * changes made to it are made to the former key too.  This synchronization is
2080  * done on-demand: not until someone requests the value of one of these paths
2081  * (by calling one of the SHGet functions) is the value synchronized.
2082  * Furthermore, the HKCU paths take precedence over the HKLM paths.
2083  */
2084 HRESULT WINAPI SHGetFolderPathW(
2085         HWND hwndOwner,    /* [I] owner window */
2086         int nFolder,       /* [I] CSIDL identifying the folder */
2087         HANDLE hToken,     /* [I] access token */
2088         DWORD dwFlags,     /* [I] which path to return */
2089         LPWSTR pszPath)    /* [O] converted path */
2090 {
2091     HRESULT hr =  SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, NULL, pszPath);
2092     if(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
2093         hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
2094     return hr;
2095 }
2096
2097 HRESULT WINAPI SHGetFolderPathAndSubDirA(
2098         HWND hwndOwner,    /* [I] owner window */
2099         int nFolder,       /* [I] CSIDL identifying the folder */
2100         HANDLE hToken,     /* [I] access token */
2101         DWORD dwFlags,     /* [I] which path to return */
2102         LPCSTR pszSubPath, /* [I] sub directory of the specified folder */
2103         LPSTR pszPath)     /* [O] converted path */
2104 {
2105     int length;
2106     HRESULT hr = S_OK;
2107     LPWSTR pszSubPathW = NULL;
2108     LPWSTR pszPathW = NULL;
2109     TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
2110
2111     if(pszPath) {
2112         pszPathW = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
2113         if(!pszPathW) {
2114             hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
2115             goto cleanup;
2116         }
2117     }
2118     TRACE("%08x,%08x,%s\n",nFolder, dwFlags, debugstr_w(pszSubPathW));
2119
2120     /* SHGetFolderPathAndSubDirW does not distinguish if pszSubPath isn't
2121      * set (null), or an empty string.therefore call it without the parameter set
2122      * if pszSubPath is an empty string
2123      */
2124     if (pszSubPath && pszSubPath[0]) {
2125         length = MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, NULL, 0);
2126         pszSubPathW = HeapAlloc(GetProcessHeap(), 0, length * sizeof(WCHAR));
2127         if(!pszSubPathW) {
2128             hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
2129             goto cleanup;
2130         }
2131         MultiByteToWideChar(CP_ACP, 0, pszSubPath, -1, pszSubPathW, length);
2132     }
2133
2134     hr = SHGetFolderPathAndSubDirW(hwndOwner, nFolder, hToken, dwFlags, pszSubPathW, pszPathW);
2135
2136     if (SUCCEEDED(hr) && pszPath)
2137         WideCharToMultiByte(CP_ACP, 0, pszPathW, -1, pszPath, MAX_PATH, NULL, NULL);
2138
2139 cleanup:
2140     HeapFree(GetProcessHeap(), 0, pszPathW);
2141     HeapFree(GetProcessHeap(), 0, pszSubPathW);
2142     return hr;
2143 }
2144
2145 /*************************************************************************
2146  * SHGetFolderPathAndSubDirW            [SHELL32.@]
2147  */
2148 HRESULT WINAPI SHGetFolderPathAndSubDirW(
2149         HWND hwndOwner,    /* [I] owner window */
2150         int nFolder,       /* [I] CSIDL identifying the folder */
2151         HANDLE hToken,     /* [I] access token */
2152         DWORD dwFlags,     /* [I] which path to return */
2153         LPCWSTR pszSubPath,/* [I] sub directory of the specified folder */
2154         LPWSTR pszPath)    /* [O] converted path */
2155 {
2156     HRESULT    hr;
2157     WCHAR      szBuildPath[MAX_PATH], szTemp[MAX_PATH];
2158     DWORD      folder = nFolder & CSIDL_FOLDER_MASK;
2159     CSIDL_Type type;
2160     int        ret;
2161     
2162     TRACE("%p,%p,nFolder=0x%04x,%s\n", hwndOwner,pszPath,nFolder,debugstr_w(pszSubPath));
2163
2164     /* Windows always NULL-terminates the resulting path regardless of success
2165      * or failure, so do so first
2166      */
2167     if (pszPath)
2168         *pszPath = '\0';
2169
2170     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
2171         return E_INVALIDARG;
2172     if ((SHGFP_TYPE_CURRENT != dwFlags) && (SHGFP_TYPE_DEFAULT != dwFlags))
2173         return E_INVALIDARG;
2174     szTemp[0] = 0;
2175     type = CSIDL_Data[folder].type;
2176     switch (type)
2177     {
2178         case CSIDL_Type_Disallowed:
2179             hr = E_INVALIDARG;
2180             break;
2181         case CSIDL_Type_NonExistent:
2182             hr = S_FALSE;
2183             break;
2184         case CSIDL_Type_WindowsPath:
2185             GetWindowsDirectoryW(szTemp, MAX_PATH);
2186             if (CSIDL_Data[folder].szDefaultPath &&
2187              !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
2188              *CSIDL_Data[folder].szDefaultPath)
2189             {
2190                 PathAddBackslashW(szTemp);
2191                 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
2192             }
2193             hr = S_OK;
2194             break;
2195         case CSIDL_Type_SystemPath:
2196             GetSystemDirectoryW(szTemp, MAX_PATH);
2197             if (CSIDL_Data[folder].szDefaultPath &&
2198              !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
2199              *CSIDL_Data[folder].szDefaultPath)
2200             {
2201                 PathAddBackslashW(szTemp);
2202                 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
2203             }
2204             hr = S_OK;
2205             break;
2206         case CSIDL_Type_SystemX86Path:
2207             if (!GetSystemWow64DirectoryW(szTemp, MAX_PATH)) GetSystemDirectoryW(szTemp, MAX_PATH);
2208             if (CSIDL_Data[folder].szDefaultPath &&
2209              !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
2210              *CSIDL_Data[folder].szDefaultPath)
2211             {
2212                 PathAddBackslashW(szTemp);
2213                 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
2214             }
2215             hr = S_OK;
2216             break;
2217         case CSIDL_Type_CurrVer:
2218             hr = _SHGetCurrentVersionPath(dwFlags, folder, szTemp);
2219             break;
2220         case CSIDL_Type_User:
2221             hr = _SHGetUserProfilePath(hToken, dwFlags, folder, szTemp);
2222             break;
2223         case CSIDL_Type_AllUsers:
2224             hr = _SHGetAllUsersProfilePath(dwFlags, folder, szTemp);
2225             break;
2226         default:
2227             FIXME("bogus type %d, please fix\n", type);
2228             hr = E_INVALIDARG;
2229             break;
2230     }
2231
2232     /* Expand environment strings if necessary */
2233     if (*szTemp == '%')
2234         hr = _SHExpandEnvironmentStrings(szTemp, szBuildPath);
2235     else
2236         strcpyW(szBuildPath, szTemp);
2237
2238     if (FAILED(hr)) goto end;
2239
2240     if(pszSubPath) {
2241         /* make sure the new path does not exceed th bufferlength
2242          * rememebr to backslash and the termination */
2243         if(MAX_PATH < (lstrlenW(szBuildPath) + lstrlenW(pszSubPath) + 2)) {
2244             hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
2245             goto end;
2246         }
2247         PathAppendW(szBuildPath, pszSubPath);
2248         PathRemoveBackslashW(szBuildPath);
2249     }
2250     /* Copy the path if it's available before we might return */
2251     if (SUCCEEDED(hr) && pszPath)
2252         strcpyW(pszPath, szBuildPath);
2253
2254     /* if we don't care about existing directories we are ready */
2255     if(nFolder & CSIDL_FLAG_DONT_VERIFY) goto end;
2256
2257     if (PathFileExistsW(szBuildPath)) goto end;
2258
2259     /* not existing but we are not allowed to create it.  The return value
2260      * is verified against shell32 version 6.0.
2261      */
2262     if (!(nFolder & CSIDL_FLAG_CREATE))
2263     {
2264         hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
2265         goto end;
2266     }
2267
2268     /* create directory/directories */
2269     ret = SHCreateDirectoryExW(hwndOwner, szBuildPath, NULL);
2270     if (ret && ret != ERROR_ALREADY_EXISTS)
2271     {
2272         ERR("Failed to create directory %s.\n", debugstr_w(szBuildPath));
2273         hr = E_FAIL;
2274         goto end;
2275     }
2276
2277     TRACE("Created missing system directory %s\n", debugstr_w(szBuildPath));
2278 end:
2279     TRACE("returning 0x%08x (final path is %s)\n", hr, debugstr_w(szBuildPath));
2280     return hr;
2281 }
2282
2283 /*************************************************************************
2284  * SHGetFolderPathA                     [SHELL32.@]
2285  *
2286  * See SHGetFolderPathW.
2287  */
2288 HRESULT WINAPI SHGetFolderPathA(
2289         HWND hwndOwner,
2290         int nFolder,
2291         HANDLE hToken,
2292         DWORD dwFlags,
2293         LPSTR pszPath)
2294 {
2295     WCHAR szTemp[MAX_PATH];
2296     HRESULT hr;
2297
2298     TRACE("%p,%p,nFolder=0x%04x\n",hwndOwner,pszPath,nFolder);
2299
2300     if (pszPath)
2301         *pszPath = '\0';
2302     hr = SHGetFolderPathW(hwndOwner, nFolder, hToken, dwFlags, szTemp);
2303     if (SUCCEEDED(hr) && pszPath)
2304         WideCharToMultiByte(CP_ACP, 0, szTemp, -1, pszPath, MAX_PATH, NULL,
2305          NULL);
2306
2307     return hr;
2308 }
2309
2310 /* For each folder in folders, if its value has not been set in the registry,
2311  * calls _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the
2312  * folder's type) to get the unexpanded value first.
2313  * Writes the unexpanded value to User Shell Folders, and queries it with
2314  * SHGetFolderPathW to force the creation of the directory if it doesn't
2315  * already exist.  SHGetFolderPathW also returns the expanded value, which
2316  * this then writes to Shell Folders.
2317  */
2318 static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken,
2319  LPCWSTR szUserShellFolderPath, LPCWSTR szShellFolderPath, const UINT folders[],
2320  UINT foldersLen)
2321 {
2322     UINT i;
2323     WCHAR path[MAX_PATH];
2324     HRESULT hr = S_OK;
2325     HKEY hUserKey = NULL, hKey = NULL;
2326     DWORD dwType, dwPathLen;
2327     LONG ret;
2328
2329     TRACE("%p,%p,%s,%p,%u\n", hRootKey, hToken,
2330      debugstr_w(szUserShellFolderPath), folders, foldersLen);
2331
2332     ret = RegCreateKeyW(hRootKey, szUserShellFolderPath, &hUserKey);
2333     if (ret)
2334         hr = HRESULT_FROM_WIN32(ret);
2335     else
2336     {
2337         ret = RegCreateKeyW(hRootKey, szShellFolderPath, &hKey);
2338         if (ret)
2339             hr = HRESULT_FROM_WIN32(ret);
2340     }
2341     for (i = 0; SUCCEEDED(hr) && i < foldersLen; i++)
2342     {
2343         dwPathLen = MAX_PATH * sizeof(WCHAR);
2344         if (RegQueryValueExW(hUserKey, CSIDL_Data[folders[i]].szValueName, NULL,
2345          &dwType, (LPBYTE)path, &dwPathLen) || (dwType != REG_SZ &&
2346          dwType != REG_EXPAND_SZ))
2347         {
2348             *path = '\0';
2349             if (CSIDL_Data[folders[i]].type == CSIDL_Type_User)
2350                 _SHGetUserProfilePath(hToken, SHGFP_TYPE_DEFAULT, folders[i],
2351                  path);
2352             else if (CSIDL_Data[folders[i]].type == CSIDL_Type_AllUsers)
2353                 _SHGetAllUsersProfilePath(SHGFP_TYPE_DEFAULT, folders[i], path);
2354             else if (CSIDL_Data[folders[i]].type == CSIDL_Type_WindowsPath)
2355             {
2356                 GetWindowsDirectoryW(path, MAX_PATH);
2357                 if (CSIDL_Data[folders[i]].szDefaultPath &&
2358                     !IS_INTRESOURCE(CSIDL_Data[folders[i]].szDefaultPath))
2359                 {
2360                     PathAddBackslashW(path);
2361                     strcatW(path, CSIDL_Data[folders[i]].szDefaultPath);
2362                 }
2363             }
2364             else
2365                 hr = E_FAIL;
2366             if (*path)
2367             {
2368                 ret = RegSetValueExW(hUserKey,
2369                  CSIDL_Data[folders[i]].szValueName, 0, REG_EXPAND_SZ,
2370                  (LPBYTE)path, (strlenW(path) + 1) * sizeof(WCHAR));
2371                 if (ret)
2372                     hr = HRESULT_FROM_WIN32(ret);
2373                 else
2374                 {
2375                     hr = SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
2376                      hToken, SHGFP_TYPE_DEFAULT, path);
2377                     ret = RegSetValueExW(hKey,
2378                      CSIDL_Data[folders[i]].szValueName, 0, REG_SZ,
2379                      (LPBYTE)path, (strlenW(path) + 1) * sizeof(WCHAR));
2380                     if (ret)
2381                         hr = HRESULT_FROM_WIN32(ret);
2382                 }
2383             }
2384         }
2385     }
2386     if (hUserKey)
2387         RegCloseKey(hUserKey);
2388     if (hKey)
2389         RegCloseKey(hKey);
2390
2391     TRACE("returning 0x%08x\n", hr);
2392     return hr;
2393 }
2394
2395 static HRESULT _SHRegisterUserShellFolders(BOOL bDefault)
2396 {
2397     static const UINT folders[] = {
2398      CSIDL_PROGRAMS,
2399      CSIDL_PERSONAL,
2400      CSIDL_FAVORITES,
2401      CSIDL_APPDATA,
2402      CSIDL_STARTUP,
2403      CSIDL_RECENT,
2404      CSIDL_SENDTO,
2405      CSIDL_STARTMENU,
2406      CSIDL_MYMUSIC,
2407      CSIDL_MYVIDEO,
2408      CSIDL_DESKTOPDIRECTORY,
2409      CSIDL_NETHOOD,
2410      CSIDL_TEMPLATES,
2411      CSIDL_PRINTHOOD,
2412      CSIDL_LOCAL_APPDATA,
2413      CSIDL_INTERNET_CACHE,
2414      CSIDL_COOKIES,
2415      CSIDL_HISTORY,
2416      CSIDL_MYPICTURES,
2417      CSIDL_FONTS
2418     };
2419     WCHAR userShellFolderPath[MAX_PATH], shellFolderPath[MAX_PATH];
2420     LPCWSTR pUserShellFolderPath, pShellFolderPath;
2421     HRESULT hr = S_OK;
2422     HKEY hRootKey;
2423     HANDLE hToken;
2424
2425     TRACE("%s\n", bDefault ? "TRUE" : "FALSE");
2426     if (bDefault)
2427     {
2428         hToken = (HANDLE)-1;
2429         hRootKey = HKEY_USERS;
2430         strcpyW(userShellFolderPath, DefaultW);
2431         PathAddBackslashW(userShellFolderPath);
2432         strcatW(userShellFolderPath, szSHUserFolders);
2433         pUserShellFolderPath = userShellFolderPath;
2434         strcpyW(shellFolderPath, DefaultW);
2435         PathAddBackslashW(shellFolderPath);
2436         strcatW(shellFolderPath, szSHFolders);
2437         pShellFolderPath = shellFolderPath;
2438     }
2439     else
2440     {
2441         hToken = NULL;
2442         hRootKey = HKEY_CURRENT_USER;
2443         pUserShellFolderPath = szSHUserFolders;
2444         pShellFolderPath = szSHFolders;
2445     }
2446
2447     hr = _SHRegisterFolders(hRootKey, hToken, pUserShellFolderPath,
2448      pShellFolderPath, folders, sizeof(folders) / sizeof(folders[0]));
2449     TRACE("returning 0x%08x\n", hr);
2450     return hr;
2451 }
2452
2453 static HRESULT _SHRegisterCommonShellFolders(void)
2454 {
2455     static const UINT folders[] = {
2456      CSIDL_COMMON_STARTMENU,
2457      CSIDL_COMMON_PROGRAMS,
2458      CSIDL_COMMON_STARTUP,
2459      CSIDL_COMMON_DESKTOPDIRECTORY,
2460      CSIDL_COMMON_FAVORITES,
2461      CSIDL_COMMON_APPDATA,
2462      CSIDL_COMMON_TEMPLATES,
2463      CSIDL_COMMON_DOCUMENTS,
2464      CSIDL_COMMON_ADMINTOOLS,
2465      CSIDL_COMMON_MUSIC,
2466      CSIDL_COMMON_PICTURES,
2467      CSIDL_COMMON_VIDEO,
2468     };
2469     HRESULT hr;
2470
2471     TRACE("\n");
2472     hr = _SHRegisterFolders(HKEY_LOCAL_MACHINE, NULL, szSHUserFolders,
2473      szSHFolders, folders, sizeof(folders) / sizeof(folders[0]));
2474     TRACE("returning 0x%08x\n", hr);
2475     return hr;
2476 }
2477
2478 /******************************************************************************
2479  * _SHAppendToUnixPath  [Internal]
2480  *
2481  * Helper function for _SHCreateSymbolicLinks. Appends pwszSubPath (or the 
2482  * corresponding resource, if IS_INTRESOURCE) to the unix base path 'szBasePath' 
2483  * and replaces backslashes with slashes.
2484  *
2485  * PARAMS
2486  *  szBasePath  [IO] The unix base path, which will be appended to (CP_UNXICP).
2487  *  pwszSubPath [I]  Sub-path or resource id (use MAKEINTRESOURCEW).
2488  *
2489  * RETURNS
2490  *  Success: TRUE,
2491  *  Failure: FALSE
2492  */
2493 static inline BOOL _SHAppendToUnixPath(char *szBasePath, LPCWSTR pwszSubPath) {
2494     WCHAR wszSubPath[MAX_PATH];
2495     int cLen = strlen(szBasePath);
2496     char *pBackslash;
2497
2498     if (IS_INTRESOURCE(pwszSubPath)) {
2499         if (!LoadStringW(shell32_hInstance, LOWORD(pwszSubPath), wszSubPath, MAX_PATH)) {
2500             /* Fall back to hard coded defaults. */
2501             switch (LOWORD(pwszSubPath)) {
2502                 case IDS_PERSONAL:
2503                     lstrcpyW(wszSubPath, PersonalW);
2504                     break;
2505                 case IDS_MYMUSIC:
2506                     lstrcpyW(wszSubPath, My_MusicW);
2507                     break;
2508                 case IDS_MYPICTURES:
2509                     lstrcpyW(wszSubPath, My_PicturesW);
2510                     break;
2511                 case IDS_MYVIDEO:
2512                     lstrcpyW(wszSubPath, My_VideoW);
2513                     break;
2514                 default:
2515                     ERR("LoadString(%d) failed!\n", LOWORD(pwszSubPath));
2516                     return FALSE;
2517             }
2518         }
2519     } else {
2520         lstrcpyW(wszSubPath, pwszSubPath);
2521     }
2522  
2523     if (szBasePath[cLen-1] != '/') szBasePath[cLen++] = '/';
2524  
2525     if (!WideCharToMultiByte(CP_UNIXCP, 0, wszSubPath, -1, szBasePath + cLen,
2526                              FILENAME_MAX - cLen, NULL, NULL))
2527     {
2528         return FALSE;
2529     }
2530  
2531     pBackslash = szBasePath + cLen;
2532     while ((pBackslash = strchr(pBackslash, '\\'))) *pBackslash = '/';
2533  
2534     return TRUE;
2535 }
2536
2537 /******************************************************************************
2538  * _SHCreateSymbolicLinks  [Internal]
2539  * 
2540  * Sets up symbol links for various shell folders to point into the users home
2541  * directory. We do an educated guess about what the user would probably want:
2542  * - If there is a 'My Documents' directory in $HOME, the user probably wants
2543  *   wine's 'My Documents' to point there. Furthermore, we imply that the user
2544  *   is a Windows lover and has no problem with wine creating 'My Pictures',
2545  *   'My Music' and 'My Video' subfolders under '$HOME/My Documents', if those
2546  *   do not already exits. We put appropriate symbolic links in place for those,
2547  *   too.
2548  * - If there is no 'My Documents' directory in $HOME, we let 'My Documents'
2549  *   point directly to $HOME. We assume the user to be a unix hacker who does not
2550  *   want wine to create anything anywhere besides the .wine directory. So, if
2551  *   there already is a 'My Music' directory in $HOME, we symlink the 'My Music'
2552  *   shell folder to it. But if not, then we check XDG_MUSIC_DIR - "well known"
2553  *   directory, and try to link to that. If that fails, then we symlink to
2554  *   $HOME directly. The same holds fo 'My Pictures' and 'My Video'.
2555  * - The Desktop shell folder is symlinked to XDG_DESKTOP_DIR. If that does not
2556  *   exist, then we try '$HOME/Desktop'. If that does not exist, then we leave
2557  *   it alone.
2558  * ('My Music',... above in fact means LoadString(IDS_MYMUSIC))
2559  */
2560 static void _SHCreateSymbolicLinks(void)
2561 {
2562     UINT aidsMyStuff[] = { IDS_MYPICTURES, IDS_MYVIDEO, IDS_MYMUSIC }, i;
2563     int acsidlMyStuff[] = { CSIDL_MYPICTURES, CSIDL_MYVIDEO, CSIDL_MYMUSIC };
2564     static const char * const xdg_dirs[] = { "PICTURES", "VIDEOS", "MUSIC", "DESKTOP" };
2565     static const unsigned int num = sizeof(xdg_dirs) / sizeof(xdg_dirs[0]);
2566     WCHAR wszTempPath[MAX_PATH];
2567     char szPersonalTarget[FILENAME_MAX], *pszPersonal;
2568     char szMyStuffTarget[FILENAME_MAX], *pszMyStuff;
2569     char szDesktopTarget[FILENAME_MAX], *pszDesktop;
2570     struct stat statFolder;
2571     const char *pszHome;
2572     HRESULT hr;
2573     char ** xdg_results;
2574     char * xdg_desktop_dir;
2575
2576     /* Create all necessary profile sub-dirs up to 'My Documents' and get the unix path. */
2577     hr = SHGetFolderPathW(NULL, CSIDL_PERSONAL|CSIDL_FLAG_CREATE, NULL,
2578                           SHGFP_TYPE_DEFAULT, wszTempPath);
2579     if (FAILED(hr)) return;
2580     pszPersonal = wine_get_unix_file_name(wszTempPath);
2581     if (!pszPersonal) return;
2582
2583     hr = XDG_UserDirLookup(xdg_dirs, num, &xdg_results);
2584     if (FAILED(hr)) xdg_results = NULL;
2585
2586     pszHome = getenv("HOME");
2587     if (pszHome && !stat(pszHome, &statFolder) && S_ISDIR(statFolder.st_mode)) {
2588         strcpy(szPersonalTarget, pszHome);
2589         if (_SHAppendToUnixPath(szPersonalTarget, MAKEINTRESOURCEW(IDS_PERSONAL)) &&
2590             !stat(szPersonalTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
2591         {
2592             /* '$HOME/My Documents' exists. Create 'My Pictures', 'My Videos' and 
2593              * 'My Music' subfolders or fail silently if they already exist. */
2594             for (i = 0; i < sizeof(aidsMyStuff)/sizeof(aidsMyStuff[0]); i++) {
2595                 strcpy(szMyStuffTarget, szPersonalTarget);
2596                 if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
2597                     mkdir(szMyStuffTarget, 0777);
2598             }
2599         } 
2600         else
2601         {
2602             /* '$HOME/My Documents' doesn't exists, but '$HOME' does. */ 
2603             strcpy(szPersonalTarget, pszHome);
2604         }
2605
2606         /* Replace 'My Documents' directory with a symlink of fail silently if not empty. */
2607         rmdir(pszPersonal);
2608         symlink(szPersonalTarget, pszPersonal);
2609     }
2610     else
2611     {
2612         /* '$HOME' doesn't exist. Create 'My Pictures', 'My Videos' and 'My Music' subdirs
2613          * in '%USERPROFILE%\\My Documents' or fail silently if they already exist. */
2614         strcpy(szPersonalTarget, pszPersonal);
2615         for (i = 0; i < sizeof(aidsMyStuff)/sizeof(aidsMyStuff[0]); i++) {
2616             strcpy(szMyStuffTarget, szPersonalTarget);
2617             if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
2618                 mkdir(szMyStuffTarget, 0777);
2619         }
2620     }
2621
2622     /* Create symbolic links for 'My Pictures', 'My Video' and 'My Music'. */
2623     for (i=0; i < sizeof(aidsMyStuff)/sizeof(aidsMyStuff[0]); i++) {
2624         /* Create the current 'My Whatever' folder and get it's unix path. */
2625         hr = SHGetFolderPathW(NULL, acsidlMyStuff[i]|CSIDL_FLAG_CREATE, NULL,
2626                               SHGFP_TYPE_DEFAULT, wszTempPath);
2627         if (FAILED(hr)) continue;
2628         pszMyStuff = wine_get_unix_file_name(wszTempPath);
2629         if (!pszMyStuff) continue;
2630         
2631         strcpy(szMyStuffTarget, szPersonalTarget);
2632         if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])) &&
2633             !stat(szMyStuffTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
2634         {
2635             /* If there's a 'My Whatever' directory where 'My Documents' links to, link to it. */
2636             rmdir(pszMyStuff);
2637             symlink(szMyStuffTarget, pszMyStuff);
2638         } 
2639         else
2640         {
2641             rmdir(pszMyStuff);
2642             if (xdg_results && xdg_results[i])
2643             {
2644                 /* the folder specified by XDG_XXX_DIR exists, link to it. */
2645                 symlink(xdg_results[i], pszMyStuff);
2646             }
2647             else
2648             {
2649                 /* Else link to where 'My Documents' itself links to. */
2650                 symlink(szPersonalTarget, pszMyStuff);
2651             }
2652         }
2653         HeapFree(GetProcessHeap(), 0, pszMyStuff);
2654     }
2655
2656     /* Last but not least, the Desktop folder */
2657     if (pszHome)
2658         strcpy(szDesktopTarget, pszHome);
2659     else
2660         strcpy(szDesktopTarget, pszPersonal);
2661     HeapFree(GetProcessHeap(), 0, pszPersonal);
2662
2663     xdg_desktop_dir = xdg_results ? xdg_results[num - 1] : NULL;
2664     if (xdg_desktop_dir ||
2665         (_SHAppendToUnixPath(szDesktopTarget, DesktopW) &&
2666         !stat(szDesktopTarget, &statFolder) && S_ISDIR(statFolder.st_mode)))
2667     {
2668         hr = SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, NULL,
2669                               SHGFP_TYPE_DEFAULT, wszTempPath);
2670         if (SUCCEEDED(hr) && (pszDesktop = wine_get_unix_file_name(wszTempPath))) 
2671         {
2672             rmdir(pszDesktop);
2673             if (xdg_desktop_dir)
2674                 symlink(xdg_desktop_dir, pszDesktop);
2675             else
2676                 symlink(szDesktopTarget, pszDesktop);
2677             HeapFree(GetProcessHeap(), 0, pszDesktop);
2678         }
2679     }
2680
2681     /* Free resources allocated by XDG_UserDirLookup() */
2682     if (xdg_results)
2683     {
2684         for (i = 0; i < num; i++)
2685             HeapFree(GetProcessHeap(), 0, xdg_results[i]);
2686         HeapFree(GetProcessHeap(), 0, xdg_results);
2687     }
2688 }
2689
2690 /******************************************************************************
2691  * create_extra_folders  [Internal]
2692  *
2693  * Create some extra folders that don't have a standard CSIDL definition.
2694  */
2695 static HRESULT create_extra_folders(void)
2696 {
2697     static const WCHAR environW[] = {'E','n','v','i','r','o','n','m','e','n','t',0};
2698     static const WCHAR TempW[]    = {'T','e','m','p',0};
2699     static const WCHAR TEMPW[]    = {'T','E','M','P',0};
2700     static const WCHAR TMPW[]     = {'T','M','P',0};
2701     WCHAR path[MAX_PATH+5];
2702     HRESULT hr;
2703     HKEY hkey;
2704     DWORD type, size, ret;
2705
2706     ret = RegCreateKeyW( HKEY_CURRENT_USER, environW, &hkey );
2707     if (ret) return HRESULT_FROM_WIN32( ret );
2708
2709     /* FIXME: should be under AppData, but we don't want spaces in the temp path */
2710     hr = SHGetFolderPathAndSubDirW( 0, CSIDL_PROFILE | CSIDL_FLAG_CREATE, NULL,
2711                                     SHGFP_TYPE_DEFAULT, TempW, path );
2712     if (SUCCEEDED(hr))
2713     {
2714         size = sizeof(path);
2715         if (RegQueryValueExW( hkey, TEMPW, NULL, &type, (LPBYTE)path, &size ))
2716             RegSetValueExW( hkey, TEMPW, 0, REG_SZ, (LPBYTE)path, (strlenW(path) + 1) * sizeof(WCHAR) );
2717         size = sizeof(path);
2718         if (RegQueryValueExW( hkey, TMPW, NULL, &type, (LPBYTE)path, &size ))
2719             RegSetValueExW( hkey, TMPW, 0, REG_SZ, (LPBYTE)path, (strlenW(path) + 1) * sizeof(WCHAR) );
2720     }
2721     RegCloseKey( hkey );
2722     return hr;
2723 }
2724
2725
2726 /* Register the default values in the registry, as some apps seem to depend
2727  * on their presence.  The set registered was taken from Windows XP.
2728  */
2729 HRESULT SHELL_RegisterShellFolders(void)
2730 {
2731     HRESULT hr;
2732
2733     /* Set up '$HOME' targeted symlinks for 'My Documents', 'My Pictures',
2734      * 'My Video', 'My Music' and 'Desktop' in advance, so that the
2735      * _SHRegister*ShellFolders() functions will find everything nice and clean
2736      * and thus will not attempt to create them in the profile directory. */
2737     _SHCreateSymbolicLinks();
2738     
2739     hr = _SHRegisterUserShellFolders(TRUE);
2740     if (SUCCEEDED(hr))
2741         hr = _SHRegisterUserShellFolders(FALSE);
2742     if (SUCCEEDED(hr))
2743         hr = _SHRegisterCommonShellFolders();
2744     if (SUCCEEDED(hr))
2745         hr = create_extra_folders();
2746     return hr;
2747 }
2748
2749 /*************************************************************************
2750  * SHGetSpecialFolderPathA [SHELL32.@]
2751  */
2752 BOOL WINAPI SHGetSpecialFolderPathA (
2753         HWND hwndOwner,
2754         LPSTR szPath,
2755         int nFolder,
2756         BOOL bCreate)
2757 {
2758         return (SHGetFolderPathA(
2759                 hwndOwner,
2760                 nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0),
2761                 NULL,
2762                 0,
2763                 szPath)) == S_OK ? TRUE : FALSE;
2764 }
2765
2766 /*************************************************************************
2767  * SHGetSpecialFolderPathW
2768  */
2769 BOOL WINAPI SHGetSpecialFolderPathW (
2770         HWND hwndOwner,
2771         LPWSTR szPath,
2772         int nFolder,
2773         BOOL bCreate)
2774 {
2775         return (SHGetFolderPathW(
2776                 hwndOwner,
2777                 nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0),
2778                 NULL,
2779                 0,
2780                 szPath)) == S_OK ? TRUE : FALSE;
2781 }
2782
2783 /*************************************************************************
2784  * SHGetSpecialFolderPath (SHELL32.175)
2785  */
2786 BOOL WINAPI SHGetSpecialFolderPathAW (
2787         HWND hwndOwner,
2788         LPVOID szPath,
2789         int nFolder,
2790         BOOL bCreate)
2791
2792 {
2793         if (SHELL_OsIsUnicode())
2794           return SHGetSpecialFolderPathW (hwndOwner, szPath, nFolder, bCreate);
2795         return SHGetSpecialFolderPathA (hwndOwner, szPath, nFolder, bCreate);
2796 }
2797
2798 /*************************************************************************
2799  * SHGetFolderLocation [SHELL32.@]
2800  *
2801  * Gets the folder locations from the registry and creates a pidl.
2802  *
2803  * PARAMS
2804  *   hwndOwner  [I]
2805  *   nFolder    [I] CSIDL_xxxxx
2806  *   hToken     [I] token representing user, or NULL for current user, or -1 for
2807  *                  default user
2808  *   dwReserved [I] must be zero
2809  *   ppidl      [O] PIDL of a special folder
2810  *
2811  * RETURNS
2812  *  Success: S_OK
2813  *  Failure: Standard OLE-defined error result, S_FALSE or E_INVALIDARG
2814  *
2815  * NOTES
2816  *  Creates missing reg keys and directories.
2817  *  Mostly forwards to SHGetFolderPathW, but a few values of nFolder return
2818  *  virtual folders that are handled here.
2819  */
2820 HRESULT WINAPI SHGetFolderLocation(
2821         HWND hwndOwner,
2822         int nFolder,
2823         HANDLE hToken,
2824         DWORD dwReserved,
2825         LPITEMIDLIST *ppidl)
2826 {
2827     HRESULT hr = E_INVALIDARG;
2828
2829     TRACE("%p 0x%08x %p 0x%08x %p\n",
2830      hwndOwner, nFolder, hToken, dwReserved, ppidl);
2831     
2832     if (!ppidl)
2833         return E_INVALIDARG;
2834     if (dwReserved)
2835         return E_INVALIDARG;
2836
2837     /* The virtual folders' locations are not user-dependent */
2838     *ppidl = NULL;
2839     switch (nFolder & CSIDL_FOLDER_MASK)
2840     {
2841         case CSIDL_DESKTOP:
2842             *ppidl = _ILCreateDesktop();
2843             break;
2844
2845         case CSIDL_PERSONAL:
2846             *ppidl = _ILCreateMyDocuments();
2847             break;
2848
2849         case CSIDL_INTERNET:
2850             *ppidl = _ILCreateIExplore();
2851             break;
2852
2853         case CSIDL_CONTROLS:
2854             *ppidl = _ILCreateControlPanel();
2855             break;
2856
2857         case CSIDL_PRINTERS:
2858             *ppidl = _ILCreatePrinters();
2859             break;
2860
2861         case CSIDL_BITBUCKET:
2862             *ppidl = _ILCreateBitBucket();
2863             break;
2864
2865         case CSIDL_DRIVES:
2866             *ppidl = _ILCreateMyComputer();
2867             break;
2868
2869         case CSIDL_NETWORK:
2870             *ppidl = _ILCreateNetwork();
2871             break;
2872
2873         default:
2874         {
2875             WCHAR szPath[MAX_PATH];
2876
2877             hr = SHGetFolderPathW(hwndOwner, nFolder, hToken,
2878              SHGFP_TYPE_CURRENT, szPath);
2879             if (SUCCEEDED(hr))
2880             {
2881                 DWORD attributes=0;
2882
2883                 TRACE("Value=%s\n", debugstr_w(szPath));
2884                 hr = SHILCreateFromPathW(szPath, ppidl, &attributes);
2885             }
2886             else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
2887             {
2888                 /* unlike SHGetFolderPath, SHGetFolderLocation in shell32
2889                  * version 6.0 returns E_FAIL for nonexistent paths
2890                  */
2891                 hr = E_FAIL;
2892             }
2893         }
2894     }
2895     if(*ppidl)
2896         hr = NOERROR;
2897
2898     TRACE("-- (new pidl %p)\n",*ppidl);
2899     return hr;
2900 }
2901
2902 /*************************************************************************
2903  * SHGetSpecialFolderLocation           [SHELL32.@]
2904  *
2905  * NOTES
2906  *   In NT5, SHGetSpecialFolderLocation needs the <winntdir>/Recent
2907  *   directory.
2908  */
2909 HRESULT WINAPI SHGetSpecialFolderLocation(
2910         HWND hwndOwner,
2911         INT nFolder,
2912         LPITEMIDLIST * ppidl)
2913 {
2914     HRESULT hr = E_INVALIDARG;
2915
2916     TRACE("(%p,0x%x,%p)\n", hwndOwner,nFolder,ppidl);
2917
2918     if (!ppidl)
2919         return E_INVALIDARG;
2920
2921     hr = SHGetFolderLocation(hwndOwner, nFolder, NULL, 0, ppidl);
2922     return hr;
2923 }
2924
2925 static int csidl_from_id( const KNOWNFOLDERID *id )
2926 {
2927     int i;
2928     for (i = 0; i < sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]); i++)
2929         if (IsEqualGUID( CSIDL_Data[i].id, id )) return i;
2930     return -1;
2931 }
2932
2933 /*************************************************************************
2934  * SHGetKnownFolderPath           [SHELL32.@]
2935  */
2936 HRESULT WINAPI SHGetKnownFolderPath(REFKNOWNFOLDERID rfid, DWORD flags, HANDLE token, PWSTR *path)
2937 {
2938     HRESULT hr;
2939     WCHAR folder[MAX_PATH];
2940     int index = csidl_from_id( rfid );
2941
2942     TRACE("%s, 0x%08x, %p, %p\n", debugstr_guid(rfid), flags, token, path);
2943
2944     if (index < 0)
2945         return E_INVALIDARG;
2946
2947     if (flags & KF_FLAG_CREATE)
2948         index |= CSIDL_FLAG_CREATE;
2949
2950     if (flags & KF_FLAG_DONT_VERIFY)
2951         index |= CSIDL_FLAG_DONT_VERIFY;
2952
2953     if (flags & KF_FLAG_NO_ALIAS)
2954         index |= CSIDL_FLAG_NO_ALIAS;
2955
2956     if (flags & KF_FLAG_INIT)
2957         index |= CSIDL_FLAG_PER_USER_INIT;
2958
2959     if (flags & ~(KF_FLAG_CREATE|KF_FLAG_DONT_VERIFY|KF_FLAG_NO_ALIAS|KF_FLAG_INIT))
2960     {
2961         FIXME("flags 0x%08x not supported\n", flags);
2962         return E_INVALIDARG;
2963     }
2964
2965     hr = SHGetFolderPathW( NULL, index, token, 0, folder );
2966     if (SUCCEEDED(hr))
2967     {
2968         *path = CoTaskMemAlloc( (strlenW( folder ) + 1) * sizeof(WCHAR) );
2969         if (!*path)
2970             return E_OUTOFMEMORY;
2971         strcpyW( *path, folder );
2972     }
2973     return hr;
2974 }
2975
2976 /*************************************************************************
2977  * SHGetFolderPathEx           [SHELL32.@]
2978  */
2979 HRESULT WINAPI SHGetFolderPathEx(REFKNOWNFOLDERID rfid, DWORD flags, HANDLE token, LPWSTR path, DWORD len)
2980 {
2981     HRESULT hr;
2982     WCHAR *buffer;
2983
2984     TRACE("%s, 0x%08x, %p, %p, %u\n", debugstr_guid(rfid), flags, token, path, len);
2985
2986     if (!path || !len) return E_INVALIDARG;
2987
2988     hr = SHGetKnownFolderPath( rfid, flags, token, &buffer );
2989     if (SUCCEEDED( hr ))
2990     {
2991         if (strlenW( buffer ) + 1 > len)
2992         {
2993             CoTaskMemFree( buffer );
2994             return HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER );
2995         }
2996         strcpyW( path, buffer );
2997         CoTaskMemFree( buffer );
2998     }
2999     return hr;
3000 }