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