Use SHCreateDirectoryEx function instead of doing explicit recursive
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 <stdarg.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include "wine/debug.h"
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winnls.h"
37 #include "winreg.h"
38 #include "wingdi.h"
39 #include "winuser.h"
40
41 #include "shlobj.h"
42 #include "shresdef.h"
43 #include "shell32_main.h"
44 #include "undocshell.h"
45 #include "pidl.h"
46 #include "wine/unicode.h"
47 #include "shlwapi.h"
48
49 WINE_DEFAULT_DEBUG_CHANNEL(shell);
50
51 /*
52         ########## Combining and Constructing paths ##########
53 */
54
55 /*************************************************************************
56  * PathAppend           [SHELL32.36]
57  */
58 BOOL WINAPI PathAppendAW(
59         LPVOID lpszPath1,
60         LPCVOID lpszPath2)
61 {
62         if (SHELL_OsIsUnicode())
63           return PathAppendW(lpszPath1, lpszPath2);
64         return PathAppendA(lpszPath1, lpszPath2);
65 }
66
67 /*************************************************************************
68  * PathCombine   [SHELL32.37]
69  */
70 LPVOID WINAPI PathCombineAW(
71         LPVOID szDest,
72         LPCVOID lpszDir,
73         LPCVOID lpszFile)
74 {
75         if (SHELL_OsIsUnicode())
76           return PathCombineW( szDest, lpszDir, lpszFile );
77         return PathCombineA( szDest, lpszDir, lpszFile );
78 }
79
80 /*************************************************************************
81  * PathAddBackslash             [SHELL32.32]
82  */
83 LPVOID WINAPI PathAddBackslashAW(LPVOID lpszPath)
84 {
85         if(SHELL_OsIsUnicode())
86           return PathAddBackslashW(lpszPath);
87         return PathAddBackslashA(lpszPath);
88 }
89
90 /*************************************************************************
91  * PathBuildRoot                [SHELL32.30]
92  */
93 LPVOID WINAPI PathBuildRootAW(LPVOID lpszPath, int drive)
94 {
95         if(SHELL_OsIsUnicode())
96           return PathBuildRootW(lpszPath, drive);
97         return PathBuildRootA(lpszPath, drive);
98 }
99
100 /*
101         Extracting Component Parts
102 */
103
104 /*************************************************************************
105  * PathFindFileName     [SHELL32.34]
106  */
107 LPVOID WINAPI PathFindFileNameAW(LPCVOID lpszPath)
108 {
109         if(SHELL_OsIsUnicode())
110           return PathFindFileNameW(lpszPath);
111         return PathFindFileNameA(lpszPath);
112 }
113
114 /*************************************************************************
115  * PathFindExtension            [SHELL32.31]
116  */
117 LPVOID WINAPI PathFindExtensionAW(LPCVOID lpszPath)
118 {
119         if (SHELL_OsIsUnicode())
120           return PathFindExtensionW(lpszPath);
121         return PathFindExtensionA(lpszPath);
122
123 }
124
125 /*************************************************************************
126  * PathGetExtensionA            [internal]
127  *
128  * NOTES
129  *  exported by ordinal
130  *  return value points to the first char after the dot
131  */
132 static LPSTR PathGetExtensionA(LPCSTR lpszPath)
133 {
134         TRACE("(%s)\n",lpszPath);
135
136         lpszPath = PathFindExtensionA(lpszPath);
137         return (LPSTR)(*lpszPath?(lpszPath+1):lpszPath);
138 }
139
140 /*************************************************************************
141  * PathGetExtensionW            [internal]
142  */
143 static LPWSTR PathGetExtensionW(LPCWSTR lpszPath)
144 {
145         TRACE("(%s)\n",debugstr_w(lpszPath));
146
147         lpszPath = PathFindExtensionW(lpszPath);
148         return (LPWSTR)(*lpszPath?(lpszPath+1):lpszPath);
149 }
150
151 /*************************************************************************
152  * PathGetExtension             [SHELL32.158]
153  */
154 LPVOID WINAPI PathGetExtensionAW(LPCVOID lpszPath,DWORD void1, DWORD void2)
155 {
156         if (SHELL_OsIsUnicode())
157           return PathGetExtensionW(lpszPath);
158         return PathGetExtensionA(lpszPath);
159 }
160
161 /*************************************************************************
162  * PathGetArgs  [SHELL32.52]
163  */
164 LPVOID WINAPI PathGetArgsAW(LPVOID lpszPath)
165 {
166         if (SHELL_OsIsUnicode())
167           return PathGetArgsW(lpszPath);
168         return PathGetArgsA(lpszPath);
169 }
170
171 /*************************************************************************
172  * PathGetDriveNumber   [SHELL32.57]
173  */
174 int WINAPI PathGetDriveNumberAW(LPVOID lpszPath)
175 {
176         if (SHELL_OsIsUnicode())
177           return PathGetDriveNumberW(lpszPath);
178         return PathGetDriveNumberA(lpszPath);
179 }
180
181 /*************************************************************************
182  * PathRemoveFileSpec [SHELL32.35]
183  */
184 BOOL WINAPI PathRemoveFileSpecAW(LPVOID lpszPath)
185 {
186         if (SHELL_OsIsUnicode())
187           return PathRemoveFileSpecW(lpszPath);
188         return PathRemoveFileSpecA(lpszPath);
189 }
190
191 /*************************************************************************
192  * PathStripPath        [SHELL32.38]
193  */
194 void WINAPI PathStripPathAW(LPVOID lpszPath)
195 {
196         if (SHELL_OsIsUnicode())
197             PathStripPathW(lpszPath);
198         else
199             PathStripPathA(lpszPath);
200 }
201
202 /*************************************************************************
203  * PathStripToRoot      [SHELL32.50]
204  */
205 BOOL WINAPI PathStripToRootAW(LPVOID lpszPath)
206 {
207         if (SHELL_OsIsUnicode())
208           return PathStripToRootW(lpszPath);
209         return PathStripToRootA(lpszPath);
210 }
211
212 /*************************************************************************
213  * PathRemoveArgs       [SHELL32.251]
214  */
215 void WINAPI PathRemoveArgsAW(LPVOID lpszPath)
216 {
217         if (SHELL_OsIsUnicode())
218             PathRemoveArgsW(lpszPath);
219         else
220             PathRemoveArgsA(lpszPath);
221 }
222
223 /*************************************************************************
224  * PathRemoveExtension  [SHELL32.250]
225  */
226 void WINAPI PathRemoveExtensionAW(LPVOID lpszPath)
227 {
228         if (SHELL_OsIsUnicode())
229             PathRemoveExtensionW(lpszPath);
230         else
231             PathRemoveExtensionA(lpszPath);
232 }
233
234
235 /*
236         Path Manipulations
237 */
238
239 /*************************************************************************
240  * PathGetShortPathA [internal]
241  */
242 static void PathGetShortPathA(LPSTR pszPath)
243 {
244         CHAR path[MAX_PATH];
245
246         TRACE("%s\n", pszPath);
247
248         if (GetShortPathNameA(pszPath, path, MAX_PATH))
249         {
250           lstrcpyA(pszPath, path);
251         }
252 }
253
254 /*************************************************************************
255  * PathGetShortPathW [internal]
256  */
257 static void PathGetShortPathW(LPWSTR pszPath)
258 {
259         WCHAR path[MAX_PATH];
260
261         TRACE("%s\n", debugstr_w(pszPath));
262
263         if (GetShortPathNameW(pszPath, path, MAX_PATH))
264         {
265           lstrcpyW(pszPath, path);
266         }
267 }
268
269 /*************************************************************************
270  * PathGetShortPath [SHELL32.92]
271  */
272 VOID WINAPI PathGetShortPathAW(LPVOID pszPath)
273 {
274         if(SHELL_OsIsUnicode())
275           PathGetShortPathW(pszPath);
276         PathGetShortPathA(pszPath);
277 }
278
279 /*************************************************************************
280  * PathRemoveBlanks [SHELL32.33]
281  */
282 void WINAPI PathRemoveBlanksAW(LPVOID str)
283 {
284         if(SHELL_OsIsUnicode())
285             PathRemoveBlanksW(str);
286         else
287             PathRemoveBlanksA(str);
288 }
289
290 /*************************************************************************
291  * PathQuoteSpaces [SHELL32.55]
292  */
293 VOID WINAPI PathQuoteSpacesAW (LPVOID lpszPath)
294 {
295         if(SHELL_OsIsUnicode())
296             PathQuoteSpacesW(lpszPath);
297         else
298             PathQuoteSpacesA(lpszPath);
299 }
300
301 /*************************************************************************
302  * PathUnquoteSpaces [SHELL32.56]
303  */
304 VOID WINAPI PathUnquoteSpacesAW(LPVOID str)
305 {
306         if(SHELL_OsIsUnicode())
307           PathUnquoteSpacesW(str);
308         else
309           PathUnquoteSpacesA(str);
310 }
311
312 /*************************************************************************
313  * PathParseIconLocation        [SHELL32.249]
314  */
315 int WINAPI PathParseIconLocationAW (LPVOID lpszPath)
316 {
317         if(SHELL_OsIsUnicode())
318           return PathParseIconLocationW(lpszPath);
319         return PathParseIconLocationA(lpszPath);
320 }
321
322 /*
323         ########## Path Testing ##########
324 */
325 /*************************************************************************
326  * PathIsUNC            [SHELL32.39]
327  */
328 BOOL WINAPI PathIsUNCAW (LPCVOID lpszPath)
329 {
330         if (SHELL_OsIsUnicode())
331           return PathIsUNCW( lpszPath );
332         return PathIsUNCA( lpszPath );
333 }
334
335 /*************************************************************************
336  *  PathIsRelative      [SHELL32.40]
337  */
338 BOOL WINAPI PathIsRelativeAW (LPCVOID lpszPath)
339 {
340         if (SHELL_OsIsUnicode())
341           return PathIsRelativeW( lpszPath );
342         return PathIsRelativeA( lpszPath );
343 }
344
345 /*************************************************************************
346  * PathIsRoot           [SHELL32.29]
347  */
348 BOOL WINAPI PathIsRootAW(LPCVOID lpszPath)
349 {
350         if (SHELL_OsIsUnicode())
351           return PathIsRootW(lpszPath);
352         return PathIsRootA(lpszPath);
353 }
354
355 /*************************************************************************
356  *  PathIsExeA          [internal]
357  */
358 static BOOL PathIsExeA (LPCSTR lpszPath)
359 {
360         LPCSTR lpszExtension = PathGetExtensionA(lpszPath);
361         int i;
362         static const char * const lpszExtensions[] =
363             {"exe", "com", "pif", "cmd", "bat", "scf", "scr", NULL };
364
365         TRACE("path=%s\n",lpszPath);
366
367         for(i=0; lpszExtensions[i]; i++)
368           if (!strcasecmp(lpszExtension,lpszExtensions[i])) return TRUE;
369
370         return FALSE;
371 }
372
373 /*************************************************************************
374  *  PathIsExeW          [internal]
375  */
376 static BOOL PathIsExeW (LPCWSTR lpszPath)
377 {
378         LPCWSTR lpszExtension = PathGetExtensionW(lpszPath);
379         int i;
380         static const WCHAR lpszExtensions[][4] =
381             {{'e','x','e','\0'}, {'c','o','m','\0'}, {'p','i','f','\0'},
382              {'c','m','d','\0'}, {'b','a','t','\0'}, {'s','c','f','\0'},
383              {'s','c','r','\0'}, {'\0'} };
384
385         TRACE("path=%s\n",debugstr_w(lpszPath));
386
387         for(i=0; lpszExtensions[i][0]; i++)
388           if (!strcmpiW(lpszExtension,lpszExtensions[i])) return TRUE;
389
390         return FALSE;
391 }
392
393 /*************************************************************************
394  *  PathIsExe           [SHELL32.43]
395  */
396 BOOL WINAPI PathIsExeAW (LPCVOID path)
397 {
398         if (SHELL_OsIsUnicode())
399           return PathIsExeW (path);
400         return PathIsExeA(path);
401 }
402
403 /*************************************************************************
404  * PathIsDirectory      [SHELL32.159]
405  */
406 BOOL WINAPI PathIsDirectoryAW (LPCVOID lpszPath)
407 {
408         if (SHELL_OsIsUnicode())
409           return PathIsDirectoryW (lpszPath);
410         return PathIsDirectoryA (lpszPath);
411 }
412
413 /*************************************************************************
414  * PathFileExists       [SHELL32.45]
415  */
416 BOOL WINAPI PathFileExistsAW (LPCVOID lpszPath)
417 {
418         if (SHELL_OsIsUnicode())
419           return PathFileExistsW (lpszPath);
420         return PathFileExistsA (lpszPath);
421 }
422
423 /*************************************************************************
424  * PathMatchSpec        [SHELL32.46]
425  */
426 BOOL WINAPI PathMatchSpecAW(LPVOID name, LPVOID mask)
427 {
428         if (SHELL_OsIsUnicode())
429           return PathMatchSpecW( name, mask );
430         return PathMatchSpecA( name, mask );
431 }
432
433 /*************************************************************************
434  * PathIsSameRoot       [SHELL32.650]
435  */
436 BOOL WINAPI PathIsSameRootAW(LPCVOID lpszPath1, LPCVOID lpszPath2)
437 {
438         if (SHELL_OsIsUnicode())
439           return PathIsSameRootW(lpszPath1, lpszPath2);
440         return PathIsSameRootA(lpszPath1, lpszPath2);
441 }
442
443 /*************************************************************************
444  * IsLFNDriveA          [SHELL32.41]
445  */
446 BOOL WINAPI IsLFNDriveA(LPCSTR lpszPath)
447 {
448     DWORD       fnlen;
449
450     if (!GetVolumeInformationA(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
451         return FALSE;
452     return fnlen > 12;
453 }
454
455 /*************************************************************************
456  * IsLFNDriveW          [SHELL32.42]
457  */
458 BOOL WINAPI IsLFNDriveW(LPCWSTR lpszPath)
459 {
460     DWORD       fnlen;
461
462     if (!GetVolumeInformationW(lpszPath, NULL, 0, NULL, &fnlen, NULL, NULL, 0))
463         return FALSE;
464     return fnlen > 12;
465 }
466
467 /*************************************************************************
468  * IsLFNDrive           [SHELL32.119]
469  */
470 BOOL WINAPI IsLFNDriveAW(LPCVOID lpszPath)
471 {
472         if (SHELL_OsIsUnicode())
473           return IsLFNDriveW(lpszPath);
474         return IsLFNDriveA(lpszPath);
475 }
476
477 /*
478         ########## Creating Something Unique ##########
479 */
480 /*************************************************************************
481  * PathMakeUniqueNameA  [internal]
482  */
483 BOOL WINAPI PathMakeUniqueNameA(
484         LPSTR lpszBuffer,
485         DWORD dwBuffSize,
486         LPCSTR lpszShortName,
487         LPCSTR lpszLongName,
488         LPCSTR lpszPathName)
489 {
490         FIXME("%p %lu %s %s %s stub\n",
491          lpszBuffer, dwBuffSize, debugstr_a(lpszShortName),
492          debugstr_a(lpszLongName), debugstr_a(lpszPathName));
493         return TRUE;
494 }
495
496 /*************************************************************************
497  * PathMakeUniqueNameW  [internal]
498  */
499 BOOL WINAPI PathMakeUniqueNameW(
500         LPWSTR lpszBuffer,
501         DWORD dwBuffSize,
502         LPCWSTR lpszShortName,
503         LPCWSTR lpszLongName,
504         LPCWSTR lpszPathName)
505 {
506         FIXME("%p %lu %s %s %s stub\n",
507          lpszBuffer, dwBuffSize, debugstr_w(lpszShortName),
508          debugstr_w(lpszLongName), debugstr_w(lpszPathName));
509         return TRUE;
510 }
511
512 /*************************************************************************
513  * PathMakeUniqueName   [SHELL32.47]
514  */
515 BOOL WINAPI PathMakeUniqueNameAW(
516         LPVOID lpszBuffer,
517         DWORD dwBuffSize,
518         LPCVOID lpszShortName,
519         LPCVOID lpszLongName,
520         LPCVOID lpszPathName)
521 {
522         if (SHELL_OsIsUnicode())
523           return PathMakeUniqueNameW(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
524         return PathMakeUniqueNameA(lpszBuffer,dwBuffSize, lpszShortName,lpszLongName,lpszPathName);
525 }
526
527 /*************************************************************************
528  * PathYetAnotherMakeUniqueName [SHELL32.75]
529  *
530  * NOTES
531  *     exported by ordinal
532  */
533 BOOL WINAPI PathYetAnotherMakeUniqueName(
534         LPWSTR lpszBuffer,
535         LPCWSTR lpszPathName,
536         LPCWSTR lpszShortName,
537         LPCWSTR lpszLongName)
538 {
539     FIXME("(%p, %s, %s ,%s):stub.\n",
540           lpszBuffer, debugstr_w(lpszPathName), debugstr_w(lpszShortName), debugstr_w(lpszLongName));
541     return TRUE;
542 }
543
544
545 /*
546         ########## cleaning and resolving paths ##########
547  */
548
549 /*************************************************************************
550  * PathFindOnPath       [SHELL32.145]
551  */
552 BOOL WINAPI PathFindOnPathAW(LPVOID sFile, LPCVOID sOtherDirs)
553 {
554         if (SHELL_OsIsUnicode())
555           return PathFindOnPathW(sFile, (LPCWSTR *)sOtherDirs);
556         return PathFindOnPathA(sFile, (LPCSTR *)sOtherDirs);
557 }
558
559 /*************************************************************************
560  * PathCleanupSpec      [SHELL32.171]
561  */
562 DWORD WINAPI PathCleanupSpecAW (LPCVOID x, LPVOID y)
563 {
564     FIXME("(%p, %p) stub\n",x,y);
565     return TRUE;
566 }
567
568 /*************************************************************************
569  * PathQualifyA         [SHELL32]
570  */
571 BOOL WINAPI PathQualifyA(LPCSTR pszPath)
572 {
573         FIXME("%s\n",pszPath);
574         return 0;
575 }
576
577 /*************************************************************************
578  * PathQualifyW         [SHELL32]
579  */
580 BOOL WINAPI PathQualifyW(LPCWSTR pszPath)
581 {
582         FIXME("%s\n",debugstr_w(pszPath));
583         return 0;
584 }
585
586 /*************************************************************************
587  * PathQualify  [SHELL32.49]
588  */
589 BOOL WINAPI PathQualifyAW(LPCVOID pszPath)
590 {
591         if (SHELL_OsIsUnicode())
592           return PathQualifyW(pszPath);
593         return PathQualifyA(pszPath);
594 }
595
596 /*************************************************************************
597  * PathResolveA [SHELL32.51]
598  */
599 BOOL WINAPI PathResolveA(
600         LPSTR lpszPath,
601         LPCSTR *alpszPaths,
602         DWORD dwFlags)
603 {
604         FIXME("(%s,%p,0x%08lx),stub!\n",
605           lpszPath, *alpszPaths, dwFlags);
606         return 0;
607 }
608
609 /*************************************************************************
610  * PathResolveW [SHELL32]
611  */
612 BOOL WINAPI PathResolveW(
613         LPWSTR lpszPath,
614         LPCWSTR *alpszPaths,
615         DWORD dwFlags)
616 {
617         FIXME("(%s,%p,0x%08lx),stub!\n",
618           debugstr_w(lpszPath), debugstr_w(*alpszPaths), dwFlags);
619         return 0;
620 }
621
622 /*************************************************************************
623  * PathResolve [SHELL32.51]
624  */
625 BOOL WINAPI PathResolveAW(
626         LPVOID lpszPath,
627         LPCVOID *alpszPaths,
628         DWORD dwFlags)
629 {
630         if (SHELL_OsIsUnicode())
631           return PathResolveW(lpszPath, (LPCWSTR*)alpszPaths, dwFlags);
632         return PathResolveA(lpszPath, (LPCSTR*)alpszPaths, dwFlags);
633 }
634
635 /*************************************************************************
636 *       PathProcessCommandA     [SHELL32.653]
637 */
638 HRESULT WINAPI PathProcessCommandA (
639         LPCSTR lpszPath,
640         LPSTR lpszBuff,
641         DWORD dwBuffSize,
642         DWORD dwFlags)
643 {
644         FIXME("%s %p 0x%04lx 0x%04lx stub\n",
645         lpszPath, lpszBuff, dwBuffSize, dwFlags);
646         strcpy(lpszBuff, lpszPath);
647         return 0;
648 }
649
650 /*************************************************************************
651 *       PathProcessCommandW
652 */
653 HRESULT WINAPI PathProcessCommandW (
654         LPCWSTR lpszPath,
655         LPWSTR lpszBuff,
656         DWORD dwBuffSize,
657         DWORD dwFlags)
658 {
659         FIXME("(%s, %p, 0x%04lx, 0x%04lx) stub\n",
660         debugstr_w(lpszPath), lpszBuff, dwBuffSize, dwFlags);
661         strcpyW(lpszBuff, lpszPath);
662         return 0;
663 }
664
665 /*************************************************************************
666 *       PathProcessCommand (SHELL32.653)
667 */
668 HRESULT WINAPI PathProcessCommandAW (
669         LPCVOID lpszPath,
670         LPVOID lpszBuff,
671         DWORD dwBuffSize,
672         DWORD dwFlags)
673 {
674         if (SHELL_OsIsUnicode())
675           return PathProcessCommandW(lpszPath, lpszBuff, dwBuffSize, dwFlags);
676         return PathProcessCommandA(lpszPath, lpszBuff, dwBuffSize, dwFlags);
677 }
678
679 /*
680         ########## special ##########
681 */
682
683 /*************************************************************************
684  * PathSetDlgItemPath (SHELL32.48)
685  */
686 VOID WINAPI PathSetDlgItemPathAW(HWND hDlg, int id, LPCVOID pszPath)
687 {
688         if (SHELL_OsIsUnicode())
689             PathSetDlgItemPathW(hDlg, id, pszPath);
690         else
691             PathSetDlgItemPathA(hDlg, id, pszPath);
692 }
693
694 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'};
695 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'};
696 static const WCHAR AppDataW[] = {'A','p','p','D','a','t','a','\0'};
697 static const WCHAR CacheW[] = {'C','a','c','h','e','\0'};
698 static const WCHAR CD_BurningW[] = {'C','D',' ','B','u','r','n','i','n','g','\0'};
699 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'};
700 static const WCHAR Common_AppDataW[] = {'C','o','m','m','o','n',' ','A','p','p','D','a','t','a','\0'};
701 static const WCHAR Common_DesktopW[] = {'C','o','m','m','o','n',' ','D','e','s','k','t','o','p','\0'};
702 static const WCHAR Common_DocumentsW[] = {'C','o','m','m','o','n',' ','D','o','c','u','m','e','n','t','s','\0'};
703 static const WCHAR CommonFilesDirW[] = {'C','o','m','m','o','n','F','i','l','e','s','D','i','r','\0'};
704 static const WCHAR CommonMusicW[] = {'C','o','m','m','o','n','M','u','s','i','c','\0'};
705 static const WCHAR CommonPicturesW[] = {'C','o','m','m','o','n','P','i','c','t','u','r','e','s','\0'};
706 static const WCHAR Common_ProgramsW[] = {'C','o','m','m','o','n',' ','P','r','o','g','r','a','m','s','\0'};
707 static const WCHAR Common_StartUpW[] = {'C','o','m','m','o','n',' ','S','t','a','r','t','U','p','\0'};
708 static const WCHAR Common_Start_MenuW[] = {'C','o','m','m','o','n',' ','S','t','a','r','t',' ','M','e','n','u','\0'};
709 static const WCHAR Common_TemplatesW[] = {'C','o','m','m','o','n',' ','T','e','m','p','l','a','t','e','s','\0'};
710 static const WCHAR CommonVideoW[] = {'C','o','m','m','o','n','V','i','d','e','o','\0'};
711 static const WCHAR CookiesW[] = {'C','o','o','k','i','e','s','\0'};
712 static const WCHAR DesktopW[] = {'D','e','s','k','t','o','p','\0'};
713 static const WCHAR FavoritesW[] = {'F','a','v','o','r','i','t','e','s','\0'};
714 static const WCHAR FontsW[] = {'F','o','n','t','s','\0'};
715 static const WCHAR HistoryW[] = {'H','i','s','t','o','r','y','\0'};
716 static const WCHAR Local_AppDataW[] = {'L','o','c','a','l',' ','A','p','p','D','a','t','a','\0'};
717 static const WCHAR My_MusicW[] = {'M','y',' ','M','u','s','i','c','\0'};
718 static const WCHAR My_PicturesW[] = {'M','y',' ','P','i','c','t','u','r','e','s','\0'};
719 static const WCHAR My_VideoW[] = {'M','y',' ','V','i','d','e','o','\0'};
720 static const WCHAR NetHoodW[] = {'N','e','t','H','o','o','d','\0'};
721 static const WCHAR PersonalW[] = {'P','e','r','s','o','n','a','l','\0'};
722 static const WCHAR PrintHoodW[] = {'P','r','i','n','t','H','o','o','d','\0'};
723 static const WCHAR ProgramFilesDirW[] = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r','\0'};
724 static const WCHAR ProgramsW[] = {'P','r','o','g','r','a','m','s','\0'};
725 static const WCHAR RecentW[] = {'R','e','c','e','n','t','\0'};
726 static const WCHAR ResourcesW[] = {'R','e','s','o','u','r','c','e','s','\0'};
727 static const WCHAR SendToW[] = {'S','e','n','d','T','o','\0'};
728 static const WCHAR StartUpW[] = {'S','t','a','r','t','U','p','\0'};
729 static const WCHAR Start_MenuW[] = {'S','t','a','r','t',' ','M','e','n','u','\0'};
730 static const WCHAR TemplatesW[] = {'T','e','m','p','l','a','t','e','s','\0'};
731 static const WCHAR DefaultW[] = {'.','D','e','f','a','u','l','t','\0'};
732 static const WCHAR AllUsersProfileW[] = {'%','A','L','L','U','S','E','R','S','P','R','O','F','I','L','E','%','\0'};
733 static const WCHAR UserProfileW[] = {'%','U','S','E','R','P','R','O','F','I','L','E','%','\0'};
734 static const WCHAR SystemDriveW[] = {'%','S','y','s','t','e','m','D','r','i','v','e','%','\0'};
735 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};
736 static const WCHAR ProfilesDirectoryW[] = {'P','r','o','f','i','l','e','s','D','i','r','e','c','t','o','r','y',0};
737 static const WCHAR AllUsersProfileValueW[] = {'A','l','l','U','s','e','r','s','P','r','o','f','i','l','e','\0'};
738 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'};
739 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'};
740 /* This defaults to L"Documents and Settings" on Windows 2000/XP, but we're
741  * acting more Windows 9x-like for now.
742  */
743 static const WCHAR szDefaultProfileDirW[] = {'w','i','n','d','o','w','s','\\','p','r','o','f','i','l','e','s','\0'};
744 static const WCHAR AllUsersW[] = {'A','l','l',' ','U','s','e','r','s','\0'};
745
746 typedef enum _CSIDL_Type {
747     CSIDL_Type_User,
748     CSIDL_Type_AllUsers,
749     CSIDL_Type_CurrVer,
750     CSIDL_Type_Disallowed,
751     CSIDL_Type_NonExistent,
752     CSIDL_Type_WindowsPath,
753     CSIDL_Type_SystemPath,
754 } CSIDL_Type;
755
756 typedef struct
757 {
758     CSIDL_Type type;
759     LPCWSTR    szValueName;
760     LPCWSTR    szDefaultPath; /* fallback string or resource ID */
761 } CSIDL_DATA;
762
763 static const CSIDL_DATA CSIDL_Data[] =
764 {
765     { /* 0x00 - CSIDL_DESKTOP */
766         CSIDL_Type_User,
767         DesktopW,
768         MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
769     },
770     { /* 0x01 - CSIDL_INTERNET */
771         CSIDL_Type_Disallowed,
772         NULL,
773         NULL
774     },
775     { /* 0x02 - CSIDL_PROGRAMS */
776         CSIDL_Type_User,
777         ProgramsW,
778         MAKEINTRESOURCEW(IDS_PROGRAMS)
779     },
780     { /* 0x03 - CSIDL_CONTROLS (.CPL files) */
781         CSIDL_Type_SystemPath,
782         NULL,
783         NULL
784     },
785     { /* 0x04 - CSIDL_PRINTERS */
786         CSIDL_Type_SystemPath,
787         NULL,
788         NULL
789     },
790     { /* 0x05 - CSIDL_PERSONAL */
791         CSIDL_Type_User,
792         PersonalW,
793         MAKEINTRESOURCEW(IDS_PERSONAL)
794     },
795     { /* 0x06 - CSIDL_FAVORITES */
796         CSIDL_Type_User,
797         FavoritesW,
798         MAKEINTRESOURCEW(IDS_FAVORITES)
799     },
800     { /* 0x07 - CSIDL_STARTUP */
801         CSIDL_Type_User,
802         StartUpW,
803         MAKEINTRESOURCEW(IDS_STARTUP)
804     },
805     { /* 0x08 - CSIDL_RECENT */
806         CSIDL_Type_User,
807         RecentW,
808         MAKEINTRESOURCEW(IDS_RECENT)
809     },
810     { /* 0x09 - CSIDL_SENDTO */
811         CSIDL_Type_User,
812         SendToW,
813         MAKEINTRESOURCEW(IDS_SENDTO)
814     },
815     { /* 0x0a - CSIDL_BITBUCKET - Recycle Bin */
816         CSIDL_Type_Disallowed,
817         NULL,
818         NULL,
819     },
820     { /* 0x0b - CSIDL_STARTMENU */
821         CSIDL_Type_User,
822         Start_MenuW,
823         MAKEINTRESOURCEW(IDS_STARTMENU)
824     },
825     { /* 0x0c - CSIDL_MYDOCUMENTS */
826         CSIDL_Type_Disallowed, /* matches WinXP--can't get its path */
827         NULL,
828         NULL
829     },
830     { /* 0x0d - CSIDL_MYMUSIC */
831         CSIDL_Type_User,
832         My_MusicW,
833         MAKEINTRESOURCEW(IDS_MYMUSIC)
834     },
835     { /* 0x0e - CSIDL_MYVIDEO */
836         CSIDL_Type_User,
837         My_VideoW,
838         MAKEINTRESOURCEW(IDS_MYVIDEO)
839     },
840     { /* 0x0f - unassigned */
841         CSIDL_Type_Disallowed,
842         NULL,
843         NULL,
844     },
845     { /* 0x10 - CSIDL_DESKTOPDIRECTORY */
846         CSIDL_Type_User,
847         DesktopW,
848         MAKEINTRESOURCEW(IDS_DESKTOPDIRECTORY)
849     },
850     { /* 0x11 - CSIDL_DRIVES */
851         CSIDL_Type_Disallowed,
852         NULL,
853         NULL,
854     },
855     { /* 0x12 - CSIDL_NETWORK */
856         CSIDL_Type_Disallowed,
857         NULL,
858         NULL,
859     },
860     { /* 0x13 - CSIDL_NETHOOD */
861         CSIDL_Type_User,
862         NetHoodW,
863         MAKEINTRESOURCEW(IDS_NETHOOD)
864     },
865     { /* 0x14 - CSIDL_FONTS */
866         CSIDL_Type_WindowsPath,
867         NULL,
868         FontsW
869     },
870     { /* 0x15 - CSIDL_TEMPLATES */
871         CSIDL_Type_User,
872         TemplatesW,
873         MAKEINTRESOURCEW(IDS_TEMPLATES)
874     },
875     { /* 0x16 - CSIDL_COMMON_STARTMENU */
876         CSIDL_Type_AllUsers,
877         Common_Start_MenuW,
878         MAKEINTRESOURCEW(IDS_STARTMENU)
879     },
880     { /* 0x17 - CSIDL_COMMON_PROGRAMS */
881         CSIDL_Type_AllUsers,
882         Common_ProgramsW,
883         MAKEINTRESOURCEW(IDS_PROGRAMS)
884     },
885     { /* 0x18 - CSIDL_COMMON_STARTUP */
886         CSIDL_Type_AllUsers,
887         Common_StartUpW,
888         MAKEINTRESOURCEW(IDS_STARTUP)
889     },
890     { /* 0x19 - CSIDL_COMMON_DESKTOPDIRECTORY */
891         CSIDL_Type_AllUsers,
892         Common_DesktopW,
893         MAKEINTRESOURCEW(IDS_DESKTOP)
894     },
895     { /* 0x1a - CSIDL_APPDATA */
896         CSIDL_Type_User,
897         AppDataW,
898         MAKEINTRESOURCEW(IDS_APPDATA)
899     },
900     { /* 0x1b - CSIDL_PRINTHOOD */
901         CSIDL_Type_User,
902         PrintHoodW,
903         MAKEINTRESOURCEW(IDS_PRINTHOOD)
904     },
905     { /* 0x1c - CSIDL_LOCAL_APPDATA */
906         CSIDL_Type_User,
907         Local_AppDataW,
908         MAKEINTRESOURCEW(IDS_LOCAL_APPDATA)
909     },
910     { /* 0x1d - CSIDL_ALTSTARTUP */
911         CSIDL_Type_NonExistent,
912         NULL,
913         NULL
914     },
915     { /* 0x1e - CSIDL_COMMON_ALTSTARTUP */
916         CSIDL_Type_NonExistent,
917         NULL,
918         NULL
919     },
920     { /* 0x1f - CSIDL_COMMON_FAVORITES */
921         CSIDL_Type_AllUsers,
922         FavoritesW,
923         MAKEINTRESOURCEW(IDS_FAVORITES)
924     },
925     { /* 0x20 - CSIDL_INTERNET_CACHE */
926         CSIDL_Type_User,
927         CacheW,
928         MAKEINTRESOURCEW(IDS_INTERNET_CACHE)
929     },
930     { /* 0x21 - CSIDL_COOKIES */
931         CSIDL_Type_User,
932         CookiesW,
933         MAKEINTRESOURCEW(IDS_COOKIES)
934     },
935     { /* 0x22 - CSIDL_HISTORY */
936         CSIDL_Type_User,
937         HistoryW,
938         MAKEINTRESOURCEW(IDS_HISTORY)
939     },
940     { /* 0x23 - CSIDL_COMMON_APPDATA */
941         CSIDL_Type_AllUsers,
942         Common_AppDataW,
943         MAKEINTRESOURCEW(IDS_APPDATA)
944     },
945     { /* 0x24 - CSIDL_WINDOWS */
946         CSIDL_Type_WindowsPath,
947         NULL,
948         NULL
949     },
950     { /* 0x25 - CSIDL_SYSTEM */
951         CSIDL_Type_SystemPath,
952         NULL,
953         NULL
954     },
955     { /* 0x26 - CSIDL_PROGRAM_FILES */
956         CSIDL_Type_CurrVer,
957         ProgramFilesDirW,
958         MAKEINTRESOURCEW(IDS_PROGRAM_FILES)
959     },
960     { /* 0x27 - CSIDL_MYPICTURES */
961         CSIDL_Type_User,
962         My_PicturesW,
963         MAKEINTRESOURCEW(IDS_MYPICTURES)
964     },
965     { /* 0x28 - CSIDL_PROFILE */
966         CSIDL_Type_User,
967         NULL,
968         NULL
969     },
970     { /* 0x29 - CSIDL_SYSTEMX86 */
971         CSIDL_Type_NonExistent,
972         NULL,
973         NULL
974     },
975     { /* 0x2a - CSIDL_PROGRAM_FILESX86 */
976         CSIDL_Type_NonExistent,
977         NULL,
978         NULL
979     },
980     { /* 0x2b - CSIDL_PROGRAM_FILES_COMMON */
981         CSIDL_Type_CurrVer,
982         CommonFilesDirW,
983         MAKEINTRESOURCEW(IDS_PROGRAM_FILES_COMMON)
984     },
985     { /* 0x2c - CSIDL_PROGRAM_FILES_COMMONX86 */
986         CSIDL_Type_NonExistent,
987         NULL,
988         NULL
989     },
990     { /* 0x2d - CSIDL_COMMON_TEMPLATES */
991         CSIDL_Type_AllUsers,
992         Common_TemplatesW,
993         MAKEINTRESOURCEW(IDS_TEMPLATES)
994     },
995     { /* 0x2e - CSIDL_COMMON_DOCUMENTS */
996         CSIDL_Type_AllUsers,
997         Common_DocumentsW,
998         MAKEINTRESOURCEW(IDS_COMMON_DOCUMENTS)
999     },
1000     { /* 0x2f - CSIDL_COMMON_ADMINTOOLS */
1001         CSIDL_Type_AllUsers,
1002         Common_Administrative_ToolsW,
1003         MAKEINTRESOURCEW(IDS_ADMINTOOLS)
1004     },
1005     { /* 0x30 - CSIDL_ADMINTOOLS */
1006         CSIDL_Type_User,
1007         Administrative_ToolsW,
1008         MAKEINTRESOURCEW(IDS_ADMINTOOLS)
1009     },
1010     { /* 0x31 - CSIDL_CONNECTIONS */
1011         CSIDL_Type_Disallowed,
1012         NULL,
1013         NULL
1014     },
1015     { /* 0x32 - unassigned */
1016         CSIDL_Type_Disallowed,
1017         NULL,
1018         NULL
1019     },
1020     { /* 0x33 - unassigned */
1021         CSIDL_Type_Disallowed,
1022         NULL,
1023         NULL
1024     },
1025     { /* 0x34 - unassigned */
1026         CSIDL_Type_Disallowed,
1027         NULL,
1028         NULL
1029     },
1030     { /* 0x35 - CSIDL_COMMON_MUSIC */
1031         CSIDL_Type_AllUsers,
1032         CommonMusicW,
1033         MAKEINTRESOURCEW(IDS_COMMON_MUSIC)
1034     },
1035     { /* 0x36 - CSIDL_COMMON_PICTURES */
1036         CSIDL_Type_AllUsers,
1037         CommonPicturesW,
1038         MAKEINTRESOURCEW(IDS_COMMON_PICTURES)
1039     },
1040     { /* 0x37 - CSIDL_COMMON_VIDEO */
1041         CSIDL_Type_AllUsers,
1042         CommonVideoW,
1043         MAKEINTRESOURCEW(IDS_COMMON_VIDEO)
1044     },
1045     { /* 0x38 - CSIDL_RESOURCES */
1046         CSIDL_Type_WindowsPath,
1047         NULL,
1048         ResourcesW
1049     },
1050     { /* 0x39 - CSIDL_RESOURCES_LOCALIZED */
1051         CSIDL_Type_NonExistent,
1052         NULL,
1053         NULL
1054     },
1055     { /* 0x3a - CSIDL_COMMON_OEM_LINKS */
1056         CSIDL_Type_NonExistent,
1057         NULL,
1058         NULL
1059     },
1060     { /* 0x3b - CSIDL_CDBURN_AREA */
1061         CSIDL_Type_User,
1062         CD_BurningW,
1063         MAKEINTRESOURCEW(IDS_CDBURN_AREA)
1064     },
1065     { /* 0x3c unassigned */
1066         CSIDL_Type_Disallowed,
1067         NULL,
1068         NULL
1069     },
1070     { /* 0x3d - CSIDL_COMPUTERSNEARME */
1071         CSIDL_Type_Disallowed, /* FIXME */
1072         NULL,
1073         NULL
1074     },
1075     { /* 0x3e - CSIDL_PROFILES */
1076         CSIDL_Type_Disallowed, /* oddly, this matches WinXP */
1077         NULL,
1078         NULL
1079     }
1080 };
1081
1082 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest);
1083
1084 /* Gets the value named value from the registry key
1085  * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
1086  * (or from rootKey\userPrefix\... if userPrefix is not NULL) into path, which
1087  * is assumed to be MAX_PATH WCHARs in length.
1088  * If it exists, expands the value and writes the expanded value to
1089  * rootKey\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
1090  * Returns successful error code if the value was retrieved from the registry,
1091  * and a failure otherwise.
1092  */
1093 static HRESULT _SHGetUserShellFolderPath(HKEY rootKey, LPCWSTR userPrefix,
1094  LPCWSTR value, LPWSTR path)
1095 {
1096     HRESULT hr;
1097     WCHAR shellFolderPath[MAX_PATH], userShellFolderPath[MAX_PATH];
1098     LPCWSTR pShellFolderPath, pUserShellFolderPath;
1099     DWORD dwDisp, dwType, dwPathLen = MAX_PATH;
1100     HKEY userShellFolderKey, shellFolderKey;
1101
1102     TRACE("%p,%s,%s,%p\n",rootKey, debugstr_w(userPrefix), debugstr_w(value),
1103      path);
1104
1105     if (userPrefix)
1106     {
1107         strcpyW(shellFolderPath, userPrefix);
1108         PathAddBackslashW(shellFolderPath);
1109         strcatW(shellFolderPath, szSHFolders);
1110         pShellFolderPath = shellFolderPath;
1111         strcpyW(userShellFolderPath, userPrefix);
1112         PathAddBackslashW(userShellFolderPath);
1113         strcatW(userShellFolderPath, szSHUserFolders);
1114         pUserShellFolderPath = userShellFolderPath;
1115     }
1116     else
1117     {
1118         pUserShellFolderPath = szSHUserFolders;
1119         pShellFolderPath = szSHFolders;
1120     }
1121
1122     if (RegCreateKeyExW(rootKey, pShellFolderPath, 0, NULL, 0, KEY_ALL_ACCESS,
1123      NULL, &shellFolderKey, &dwDisp))
1124     {
1125         TRACE("Failed to create %s\n", debugstr_w(pShellFolderPath));
1126         return E_FAIL;
1127     }
1128     if (RegCreateKeyExW(rootKey, pUserShellFolderPath, 0, NULL, 0,
1129      KEY_ALL_ACCESS, NULL, &userShellFolderKey, &dwDisp))
1130     {
1131         TRACE("Failed to create %s\n",
1132          debugstr_w(pUserShellFolderPath));
1133         RegCloseKey(shellFolderKey);
1134         return E_FAIL;
1135     }
1136
1137     if (!RegQueryValueExW(userShellFolderKey, value, NULL, &dwType,
1138      (LPBYTE)path, &dwPathLen) && (dwType == REG_EXPAND_SZ || dwType == REG_SZ))
1139     {
1140         path[dwPathLen / sizeof(WCHAR)] = '\0';
1141         if (dwType == REG_EXPAND_SZ && path[0] == '%')
1142         {
1143             WCHAR szTemp[MAX_PATH];
1144
1145             _SHExpandEnvironmentStrings(path, szTemp);
1146             strncpyW(path, szTemp, MAX_PATH);
1147         }
1148         RegSetValueExW(shellFolderKey, value, 0, REG_SZ, (LPBYTE)path,
1149          (strlenW(path) + 1) * sizeof(WCHAR));
1150         hr = S_OK;
1151     }
1152     else
1153         hr = E_FAIL;
1154     RegCloseKey(shellFolderKey);
1155     RegCloseKey(userShellFolderKey);
1156     TRACE("returning 0x%08lx\n", hr);
1157     return hr;
1158 }
1159
1160 /* Gets a 'semi-expanded' default value of the CSIDL with index folder into
1161  * pszPath, based on the entries in CSIDL_Data.  By semi-expanded, I mean:
1162  * - The entry's szDefaultPath may be either a string value or an integer
1163  *   resource identifier.  In the latter case, the string value of the resource
1164  *   is written.
1165  * - Depending on the entry's type, the path may begin with an (unexpanded)
1166  *   environment variable name.  The caller is responsible for expanding
1167  *   environment strings if so desired.
1168  *   The types that are prepended with environment variables are:
1169  *   CSIDL_Type_User:     %USERPROFILE%
1170  *   CSIDL_Type_AllUsers: %ALLUSERSPROFILE%
1171  *   CSIDL_Type_CurrVer:  %SystemDrive%
1172  *   (Others might make sense too, but as yet are unneeded.)
1173  * FIXME: there are two special cases for the default value:
1174  * - the "My Documents" (CSIDL_PERSONAL) entry should be $HOME
1175  * - the CSIDL_DESKTOP and CSIDL_DESKTOPDIRECTORY (which have the same path)
1176  *   should be $HOME/Desktop if it exists
1177  * But, $HOME doesn't seem to be inherited into the Wine environment.  I could
1178  * use getenv, but this returns me a UNIX path, which may or may not be
1179  * reachable from any currently mounted DOS drives.
1180  */
1181 static HRESULT _SHGetDefaultValue(BYTE folder, LPWSTR pszPath)
1182 {
1183     HRESULT hr;
1184     WCHAR resourcePath[MAX_PATH];
1185     LPCWSTR pDefaultPath;
1186
1187     TRACE("0x%02x,%p\n", folder, pszPath);
1188
1189     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1190         return E_INVALIDARG;
1191     if (!pszPath)
1192         return E_INVALIDARG;
1193
1194     if (CSIDL_Data[folder].szDefaultPath &&
1195      IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath))
1196     {
1197         if (LoadStringW(shell32_hInstance,
1198          (UINT)CSIDL_Data[folder].szDefaultPath, resourcePath, MAX_PATH))
1199         {
1200             hr = S_OK;
1201             pDefaultPath = resourcePath;
1202         }
1203         else
1204         {
1205             FIXME("LoadString failed, missing translation?\n");
1206             hr = E_FAIL;
1207         }
1208     }
1209     else
1210     {
1211         hr = S_OK;
1212         pDefaultPath = CSIDL_Data[folder].szDefaultPath;
1213     }
1214     if (SUCCEEDED(hr))
1215     {
1216         switch (CSIDL_Data[folder].type)
1217         {
1218             case CSIDL_Type_User:
1219                 strcpyW(pszPath, UserProfileW);
1220                 break;
1221             case CSIDL_Type_AllUsers:
1222                 strcpyW(pszPath, AllUsersProfileW);
1223                 break;
1224             case CSIDL_Type_CurrVer:
1225                 strcpyW(pszPath, SystemDriveW);
1226                 break;
1227             default:
1228                 ; /* no corresponding env. var, do nothing */
1229         }
1230         if (pDefaultPath)
1231         {
1232             PathAddBackslashW(pszPath);
1233             strcatW(pszPath, pDefaultPath);
1234         }
1235     }
1236     TRACE("returning 0x%08lx\n", hr);
1237     return hr;
1238 }
1239
1240 /* Gets the (unexpanded) value of the folder with index folder into pszPath.
1241  * The folder's type is assumed to be CSIDL_Type_CurrVer.  Its default value
1242  * can be overridden in the HKLM\\szCurrentVersion key.
1243  * If dwFlags has SHGFP_TYPE_DEFAULT set or if the value isn't overridden in
1244  * the registry, uses _SHGetDefaultValue to get the value.
1245  */
1246 static HRESULT _SHGetCurrentVersionPath(DWORD dwFlags, BYTE folder,
1247  LPWSTR pszPath)
1248 {
1249     HRESULT hr;
1250
1251     TRACE("0x%08lx,0x%02x,%p\n", dwFlags, folder, pszPath);
1252
1253     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1254         return E_INVALIDARG;
1255     if (CSIDL_Data[folder].type != CSIDL_Type_CurrVer)
1256         return E_INVALIDARG;
1257     if (!pszPath)
1258         return E_INVALIDARG;
1259
1260     if (dwFlags & SHGFP_TYPE_DEFAULT)
1261         hr = _SHGetDefaultValue(folder, pszPath);
1262     else
1263     {
1264         HKEY hKey;
1265         DWORD dwDisp;
1266
1267         if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, szCurrentVersion, 0,
1268          NULL, 0, KEY_ALL_ACCESS, NULL, &hKey, &dwDisp))
1269             hr = E_FAIL;
1270         else
1271         {
1272             DWORD dwType, dwPathLen = MAX_PATH * sizeof(WCHAR);
1273
1274             if (RegQueryValueExW(hKey, CSIDL_Data[folder].szValueName, NULL,
1275              &dwType, (LPBYTE)pszPath, &dwPathLen) ||
1276              (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
1277             {
1278                 hr = _SHGetDefaultValue(folder, pszPath);
1279                 dwType = REG_EXPAND_SZ;
1280                 RegSetValueExW(hKey, CSIDL_Data[folder].szValueName, 0, dwType,
1281                  (LPBYTE)pszPath, (strlenW(pszPath)+1)*sizeof(WCHAR));
1282             }
1283             else
1284             {
1285                 pszPath[dwPathLen / sizeof(WCHAR)] = '\0';
1286                 hr = S_OK;
1287             }
1288             RegCloseKey(hKey);
1289         }
1290     }
1291     TRACE("returning 0x%08lx (output path is %s)\n", hr, debugstr_w(pszPath));
1292     return hr;
1293 }
1294
1295 /* Gets the user's path (unexpanded) for the CSIDL with index folder:
1296  * If SHGFP_TYPE_DEFAULT is set, calls _SHGetDefaultValue for it.  Otherwise
1297  * calls _SHGetUserShellFolderPath for it.  Where it looks depends on hToken:
1298  * - if hToken is -1, looks in HKEY_USERS\.Default
1299  * - otherwise looks first in HKEY_CURRENT_USER, followed by HKEY_LOCAL_MACHINE
1300  *   if HKEY_CURRENT_USER doesn't contain any entries.  If both fail, finally
1301  *   calls _SHGetDefaultValue for it.
1302  */
1303 static HRESULT _SHGetUserProfilePath(HANDLE hToken, DWORD dwFlags, BYTE folder,
1304  LPWSTR pszPath)
1305 {
1306     HRESULT hr;
1307
1308     TRACE("%p,0x%08lx,0x%02x,%p\n", hToken, dwFlags, folder, pszPath);
1309
1310     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1311         return E_INVALIDARG;
1312     if (CSIDL_Data[folder].type != CSIDL_Type_User)
1313         return E_INVALIDARG;
1314     if (!pszPath)
1315         return E_INVALIDARG;
1316
1317     /* Only the current user and the default user are supported right now
1318      * I'm afraid.
1319      * FIXME: should be able to call GetTokenInformation on the token,
1320      * then call ConvertSidToStringSidW on it to get the user prefix.
1321      * But Wine's registry doesn't store user info by sid, it stores it
1322      * by user name (and I don't know how to convert from a token to a
1323      * user name).
1324      */
1325     if (hToken != NULL && hToken != (HANDLE)-1)
1326     {
1327         FIXME("unsupported for user other than current or default\n");
1328         return E_FAIL;
1329     }
1330
1331     if (dwFlags & SHGFP_TYPE_DEFAULT)
1332         hr = _SHGetDefaultValue(folder, pszPath);
1333     else
1334     {
1335         LPCWSTR userPrefix = NULL;
1336         HKEY hRootKey;
1337
1338         if (hToken == (HANDLE)-1)
1339         {
1340             hRootKey = HKEY_USERS;
1341             userPrefix = DefaultW;
1342         }
1343         else /* hToken == NULL, other values disallowed above */
1344             hRootKey = HKEY_CURRENT_USER;
1345         hr = _SHGetUserShellFolderPath(hRootKey, userPrefix,
1346          CSIDL_Data[folder].szValueName, pszPath);
1347         if (FAILED(hr) && hRootKey != HKEY_LOCAL_MACHINE)
1348             hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL,
1349              CSIDL_Data[folder].szValueName, pszPath);
1350         if (FAILED(hr))
1351             hr = _SHGetDefaultValue(folder, pszPath);
1352     }
1353     TRACE("returning 0x%08lx (output path is %s)\n", hr, debugstr_w(pszPath));
1354     return hr;
1355 }
1356
1357 /* Gets the (unexpanded) path for the CSIDL with index folder.  If dwFlags has
1358  * SHGFP_TYPE_DEFAULT set, calls _SHGetDefaultValue.  Otherwise calls
1359  * _SHGetUserShellFolderPath for it, looking only in HKEY_LOCAL_MACHINE.
1360  * If this fails, falls back to _SHGetDefaultValue.
1361  */
1362 static HRESULT _SHGetAllUsersProfilePath(DWORD dwFlags, BYTE folder,
1363  LPWSTR pszPath)
1364 {
1365     HRESULT hr;
1366
1367     TRACE("0x%08lx,0x%02x,%p\n", dwFlags, folder, pszPath);
1368
1369     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1370         return E_INVALIDARG;
1371     if (CSIDL_Data[folder].type != CSIDL_Type_AllUsers)
1372         return E_INVALIDARG;
1373     if (!pszPath)
1374         return E_INVALIDARG;
1375
1376     if (dwFlags & SHGFP_TYPE_DEFAULT)
1377         hr = _SHGetDefaultValue(folder, pszPath);
1378     else
1379     {
1380         hr = _SHGetUserShellFolderPath(HKEY_LOCAL_MACHINE, NULL,
1381          CSIDL_Data[folder].szValueName, pszPath);
1382         if (FAILED(hr))
1383             hr = _SHGetDefaultValue(folder, pszPath);
1384     }
1385     TRACE("returning 0x%08lx (output path is %s)\n", hr, debugstr_w(pszPath));
1386     return hr;
1387 }
1388
1389 static HRESULT _SHOpenProfilesKey(PHKEY pKey)
1390 {
1391     LONG lRet;
1392     DWORD disp;
1393
1394     lRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, ProfileListW, 0, NULL, 0,
1395      KEY_ALL_ACCESS, NULL, pKey, &disp);
1396     return HRESULT_FROM_WIN32(lRet);
1397 }
1398
1399 /* Reads the value named szValueName from the key profilesKey (assumed to be
1400  * opened by _SHOpenProfilesKey) into szValue, which is assumed to be MAX_PATH
1401  * WCHARs in length.  If it doesn't exist, returns szDefault (and saves
1402  * szDefault to the registry).
1403  */
1404 static HRESULT _SHGetProfilesValue(HKEY profilesKey, LPCWSTR szValueName,
1405  LPWSTR szValue, LPCWSTR szDefault)
1406 {
1407     HRESULT hr;
1408     DWORD type, dwPathLen = MAX_PATH * sizeof(WCHAR);
1409     LONG lRet;
1410
1411     TRACE("%p,%s,%p,%s\n", profilesKey, debugstr_w(szValueName), szValue,
1412      debugstr_w(szDefault));
1413     lRet = RegQueryValueExW(profilesKey, szValueName, NULL, &type,
1414      (LPBYTE)szValue, &dwPathLen);
1415     if (!lRet && (type == REG_SZ || type == REG_EXPAND_SZ) && dwPathLen
1416      && *szValue)
1417     {
1418         dwPathLen /= sizeof(WCHAR);
1419         szValue[dwPathLen] = '\0';
1420         hr = S_OK;
1421     }
1422     else
1423     {
1424         /* Missing or invalid value, set a default */
1425         strncpyW(szValue, szDefault, MAX_PATH);
1426         szValue[MAX_PATH - 1] = '\0';
1427         TRACE("Setting missing value %s to %s\n", debugstr_w(szValueName),
1428          debugstr_w(szValue));
1429         lRet = RegSetValueExW(profilesKey, szValueName, 0, REG_EXPAND_SZ,
1430          (LPBYTE)szValue, (strlenW(szValue) + 1) * sizeof(WCHAR));
1431         if (lRet)
1432             hr = HRESULT_FROM_WIN32(lRet);
1433         else
1434             hr = S_OK;
1435     }
1436     TRACE("returning 0x%08lx (output value is %s)\n", hr, debugstr_w(szValue));
1437     return hr;
1438 }
1439
1440 /* Attempts to expand environment variables from szSrc into szDest, which is
1441  * assumed to be MAX_PATH characters in length.  Before referring to the
1442  * environment, handles a few variables directly, because the environment
1443  * variables may not be set when this is called (as during Wine's installation
1444  * when default values are being written to the registry).
1445  * The directly handled environment variables, and their source, are:
1446  * - ALLUSERSPROFILE, USERPROFILE: reads from the registry
1447  * - SystemDrive: uses GetSystemDirectoryW and uses the drive portion of its
1448  *   path
1449  * If one of the directly handled environment variables is expanded, only
1450  * expands a single variable, and only in the beginning of szSrc.
1451  */
1452 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest)
1453 {
1454     HRESULT hr;
1455     WCHAR szTemp[MAX_PATH], szProfilesPrefix[MAX_PATH] = { 0 };
1456     HKEY key = NULL;
1457
1458     TRACE("%s, %p\n", debugstr_w(szSrc), szDest);
1459
1460     if (!szSrc || !szDest) return E_INVALIDARG;
1461
1462     /* short-circuit if there's nothing to expand */
1463     if (szSrc[0] != '%')
1464     {
1465         strcpyW(szDest, szSrc);
1466         hr = S_OK;
1467         goto end;
1468     }
1469     /* Get the profile prefix, we'll probably be needing it */
1470     hr = _SHOpenProfilesKey(&key);
1471     if (SUCCEEDED(hr))
1472     {
1473         WCHAR szDefaultProfilesPrefix[MAX_PATH];
1474
1475         strcpyW(szDefaultProfilesPrefix, SystemDriveW);
1476         PathAppendW(szDefaultProfilesPrefix, szDefaultProfileDirW);
1477         hr = _SHGetProfilesValue(key, ProfilesDirectoryW, szProfilesPrefix,
1478          szDefaultProfilesPrefix);
1479     }
1480
1481     *szDest = 0;
1482     strcpyW(szTemp, szSrc);
1483     while (SUCCEEDED(hr) && szTemp[0] == '%')
1484     {
1485         if (!strncmpiW(szTemp, AllUsersProfileW, strlenW(AllUsersProfileW)))
1486         {
1487             WCHAR szAllUsers[MAX_PATH];
1488
1489             strcpyW(szDest, szProfilesPrefix);
1490             hr = _SHGetProfilesValue(key, AllUsersProfileValueW,
1491              szAllUsers, AllUsersW);
1492             PathAppendW(szDest, szAllUsers);
1493             PathAppendW(szDest, szTemp + strlenW(AllUsersProfileW));
1494         }
1495         else if (!strncmpiW(szTemp, UserProfileW, strlenW(UserProfileW)))
1496         {
1497             WCHAR userName[MAX_PATH];
1498             DWORD userLen = MAX_PATH;
1499
1500             strcpyW(szDest, szProfilesPrefix);
1501             GetUserNameW(userName, &userLen);
1502             PathAppendW(szDest, userName);
1503             PathAppendW(szDest, szTemp + strlenW(UserProfileW));
1504         }
1505         else if (!strncmpiW(szTemp, SystemDriveW, strlenW(SystemDriveW)))
1506         {
1507             GetSystemDirectoryW(szDest, MAX_PATH);
1508             if (szDest[1] != ':')
1509             {
1510                 FIXME("non-drive system paths unsupported\n");
1511                 hr = E_FAIL;
1512             }
1513             else
1514             {
1515                 strcpyW(szDest + 3, szTemp + strlenW(SystemDriveW) + 1);
1516                 hr = S_OK;
1517             }
1518         }
1519         else
1520         {
1521             DWORD ret = ExpandEnvironmentStringsW(szSrc, szDest, MAX_PATH);
1522
1523             if (ret > MAX_PATH)
1524                 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
1525             else if (ret == 0)
1526                 hr = HRESULT_FROM_WIN32(GetLastError());
1527             else
1528                 hr = S_OK;
1529         }
1530         if (SUCCEEDED(hr) && szDest[0] == '%')
1531             strcpyW(szTemp, szDest);
1532         else
1533         {
1534             /* terminate loop */
1535             szTemp[0] = '\0';
1536         }
1537     }
1538 end:
1539     if (key)
1540         RegCloseKey(key);
1541     TRACE("returning 0x%08lx (input was %s, output is %s)\n", hr,
1542      debugstr_w(szSrc), debugstr_w(szDest));
1543     return hr;
1544 }
1545
1546 /*************************************************************************
1547  * SHGetFolderPathW                     [SHELL32.@]
1548  *
1549  * NOTES
1550  * Converts nFolder to path.  Most values can be overridden in either
1551  * HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
1552  * or in the same location in HKLM.
1553  * The registry usage is explained by the following tech note:
1554  * http://www.microsoft.com/windows2000/techinfo/reskit/en-us/default.asp?url=/windows2000/techinfo/reskit/en-us/regentry/36173.asp
1555  * The "Shell Folders" registry key was used in NT4 and earlier systems.
1556  * Beginning with Windows 2000, the "User Shell Folders" key is used, so
1557  * changes made to it are made to the former key too.  This synchronization is
1558  * done on-demand: not until someone requests the value of one of these paths
1559  * (by calling one of the SHGet functions) is the value synchronized.
1560  * Furthermore, as explained here:
1561  * http://www.microsoft.com/windows2000/techinfo/reskit/en-us/default.asp?url=/windows2000/techinfo/reskit/en-us/regentry/36276.asp
1562  * the HKCU paths take precedence over the HKLM paths.
1563  *
1564  **********************************************************************/
1565 HRESULT WINAPI SHGetFolderPathW(
1566         HWND hwndOwner,
1567         int nFolder,
1568         HANDLE hToken,
1569         DWORD dwFlags,
1570         LPWSTR pszPath)
1571 {
1572     HRESULT    hr;
1573     WCHAR      szBuildPath[MAX_PATH], szTemp[MAX_PATH];
1574     DWORD      folder = nFolder & CSIDL_FOLDER_MASK;
1575     CSIDL_Type type;
1576
1577     TRACE("%p,%p,nFolder=0x%04x\n", hwndOwner,pszPath,nFolder);
1578
1579     /* Windows always NULL-terminates the resulting path regardless of success
1580      * or failure, so do so first
1581      */
1582     if (pszPath)
1583         *pszPath = '\0';
1584     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1585         return E_INVALIDARG;
1586     szTemp[0] = 0;
1587     type = CSIDL_Data[folder].type;
1588     switch (type)
1589     {
1590         case CSIDL_Type_Disallowed:
1591             hr = E_INVALIDARG;
1592             break;
1593         case CSIDL_Type_NonExistent:
1594             hr = S_FALSE;
1595             break;
1596         case CSIDL_Type_WindowsPath:
1597             GetWindowsDirectoryW(szTemp, MAX_PATH);
1598             if (CSIDL_Data[folder].szDefaultPath &&
1599              !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
1600              *CSIDL_Data[folder].szDefaultPath)
1601             {
1602                 PathAddBackslashW(szTemp);
1603                 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
1604             }
1605             hr = S_OK;
1606             break;
1607         case CSIDL_Type_SystemPath:
1608             GetSystemDirectoryW(szTemp, MAX_PATH);
1609             if (CSIDL_Data[folder].szDefaultPath &&
1610              !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
1611              *CSIDL_Data[folder].szDefaultPath)
1612             {
1613                 PathAddBackslashW(szTemp);
1614                 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
1615             }
1616             hr = S_OK;
1617             break;
1618         case CSIDL_Type_CurrVer:
1619             hr = _SHGetCurrentVersionPath(dwFlags, folder, szTemp);
1620             break;
1621         case CSIDL_Type_User:
1622             hr = _SHGetUserProfilePath(hToken, dwFlags, folder, szTemp);
1623             break;
1624         case CSIDL_Type_AllUsers:
1625             hr = _SHGetAllUsersProfilePath(dwFlags, folder, szTemp);
1626             break;
1627         default:
1628             FIXME("bogus type %d, please fix\n", type);
1629             hr = E_INVALIDARG;
1630             break;
1631     }
1632
1633     /* Expand environment strings if necessary */
1634     if (*szTemp == '%')
1635         hr = _SHExpandEnvironmentStrings(szTemp, szBuildPath);
1636     else
1637         strcpyW(szBuildPath, szTemp);
1638     /* Copy the path if it's available before we might return */
1639     if (SUCCEEDED(hr) && pszPath)
1640         strcpyW(pszPath, szBuildPath);
1641
1642     if (FAILED(hr)) goto end;
1643
1644     /* if we don't care about existing directories we are ready */
1645     if(nFolder & CSIDL_FLAG_DONT_VERIFY) goto end;
1646
1647     if (PathFileExistsW(szBuildPath)) goto end;
1648
1649     /* not existing but we are not allowed to create it.  The return value
1650      * is verified against shell32 version 6.0.
1651      */
1652     if (!(nFolder & CSIDL_FLAG_CREATE))
1653     {
1654         hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
1655         goto end;
1656     }
1657
1658     /* create directory/directories */
1659     if (SHCreateDirectoryExW(hwndOwner, szBuildPath, NULL))
1660     {
1661         ERR("Failed to create directory '%s'.\n", debugstr_w(szBuildPath));
1662         hr = E_FAIL;
1663         goto end;
1664     }
1665
1666     TRACE("Created missing system directory '%s'\n", debugstr_w(szBuildPath));
1667 end:
1668     TRACE("returning 0x%08lx (final path is %s)\n", hr, debugstr_w(szBuildPath));
1669     return hr;
1670 }
1671
1672 /*************************************************************************
1673  * SHGetFolderPathA                     [SHELL32.@]
1674  */
1675 HRESULT WINAPI SHGetFolderPathA(
1676         HWND hwndOwner,
1677         int nFolder,
1678         HANDLE hToken,
1679         DWORD dwFlags,
1680         LPSTR pszPath)
1681 {
1682     WCHAR szTemp[MAX_PATH];
1683     HRESULT hr;
1684
1685     TRACE("%p,%p,nFolder=0x%04x\n",hwndOwner,pszPath,nFolder);
1686
1687     if (pszPath)
1688         *pszPath = '\0';
1689     hr = SHGetFolderPathW(hwndOwner, nFolder, hToken, dwFlags, szTemp);
1690     if (SUCCEEDED(hr) && pszPath)
1691         WideCharToMultiByte(CP_ACP, 0, szTemp, -1, pszPath, MAX_PATH, NULL,
1692          NULL);
1693
1694     return hr;
1695 }
1696
1697 /* For each folder in folders, if its value has not been set in the registry,
1698  * call _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the
1699  * folder's type) to get the unexpanded value first.
1700  * This will create the expanded value in the Shell Folders key, and
1701  * return the unexpanded value.
1702  * Write the unexpanded value to User Shell Folders, and query it with
1703  * SHGetFolderPath to force the creation of the directory if it doesn't
1704  * already exist.
1705  */
1706 static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken,
1707  LPCWSTR szUserShellFolderPath, const UINT folders[], UINT foldersLen)
1708 {
1709     UINT i;
1710     WCHAR path[MAX_PATH];
1711     HRESULT hr = S_OK;
1712     HKEY hKey = NULL;
1713     DWORD dwDisp, dwType, dwPathLen;
1714     LONG ret;
1715
1716     TRACE("%p,%p,%s,%p,%u\n", hRootKey, hToken,
1717      debugstr_w(szUserShellFolderPath), folders, foldersLen);
1718
1719     ret = RegCreateKeyExW(hRootKey, szUserShellFolderPath, 0, NULL, 0,
1720      KEY_ALL_ACCESS, NULL, &hKey, &dwDisp);
1721     if (ret)
1722         hr = HRESULT_FROM_WIN32(ret);
1723     for (i = 0; SUCCEEDED(hr) && i < foldersLen; i++)
1724     {
1725         dwPathLen = MAX_PATH * sizeof(WCHAR);
1726         if (RegQueryValueExW(hKey, CSIDL_Data[folders[i]].szValueName, NULL,
1727          &dwType, (LPBYTE)path, &dwPathLen) || (dwType != REG_SZ &&
1728          dwType != REG_EXPAND_SZ))
1729         {
1730             *path = '\0';
1731             if (CSIDL_Data[folders[i]].type == CSIDL_Type_User)
1732                 _SHGetUserProfilePath(hToken, SHGFP_TYPE_DEFAULT, folders[i],
1733                  path);
1734             else if (CSIDL_Data[folders[i]].type == CSIDL_Type_AllUsers)
1735                 _SHGetAllUsersProfilePath(SHGFP_TYPE_DEFAULT, folders[i], path);
1736             else
1737                 hr = E_FAIL;
1738             if (*path)
1739             {
1740                 ret = RegSetValueExW(hKey, CSIDL_Data[folders[i]].szValueName,
1741                  0, REG_EXPAND_SZ, (LPBYTE)path,
1742                  (strlenW(path) + 1) * sizeof(WCHAR));
1743                 if (ret)
1744                     hr = HRESULT_FROM_WIN32(ret);
1745                 else
1746                     hr = SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
1747                      hToken, SHGFP_TYPE_DEFAULT, NULL);
1748             }
1749         }
1750     }
1751     if (hKey)
1752         RegCloseKey(hKey);
1753
1754     TRACE("returning 0x%08lx\n", hr);
1755     return hr;
1756 }
1757
1758 static HRESULT _SHRegisterUserShellFolders(BOOL bDefault)
1759 {
1760     static const UINT folders[] = {
1761      CSIDL_PROGRAMS,
1762      CSIDL_PERSONAL,
1763      CSIDL_FAVORITES,
1764      CSIDL_STARTUP,
1765      CSIDL_RECENT,
1766      CSIDL_SENDTO,
1767      CSIDL_STARTMENU,
1768      CSIDL_DESKTOPDIRECTORY,
1769      CSIDL_NETHOOD,
1770      CSIDL_TEMPLATES,
1771      CSIDL_PRINTHOOD,
1772      CSIDL_COOKIES,
1773      CSIDL_HISTORY,
1774     };
1775     WCHAR userShellFolderPath[MAX_PATH];
1776     LPCWSTR pUserShellFolderPath;
1777     HRESULT hr = S_OK;
1778     HKEY hRootKey;
1779     HANDLE hToken;
1780
1781     TRACE("%s\n", bDefault ? "TRUE" : "FALSE");
1782     if (bDefault)
1783     {
1784         hToken = (HANDLE)-1;
1785         hRootKey = HKEY_USERS;
1786         strcpyW(userShellFolderPath, DefaultW);
1787         PathAddBackslashW(userShellFolderPath);
1788         strcatW(userShellFolderPath, szSHUserFolders);
1789         pUserShellFolderPath = userShellFolderPath;
1790     }
1791     else
1792     {
1793         hToken = NULL;
1794         hRootKey = HKEY_CURRENT_USER;
1795         pUserShellFolderPath = szSHUserFolders;
1796     }
1797
1798     hr = _SHRegisterFolders(hRootKey, hToken, pUserShellFolderPath,
1799      folders, sizeof(folders) / sizeof(folders[0]));
1800     TRACE("returning 0x%08lx\n", hr);
1801     return hr;
1802 }
1803
1804 static HRESULT _SHRegisterCommonShellFolders(void)
1805 {
1806     static const UINT folders[] = {
1807      CSIDL_COMMON_STARTMENU,
1808      CSIDL_COMMON_PROGRAMS,
1809      CSIDL_COMMON_STARTUP,
1810      CSIDL_COMMON_DESKTOPDIRECTORY,
1811      CSIDL_COMMON_FAVORITES,
1812      CSIDL_COMMON_APPDATA,
1813      CSIDL_COMMON_TEMPLATES,
1814      CSIDL_COMMON_DOCUMENTS,
1815     };
1816     HRESULT hr;
1817
1818     TRACE("\n");
1819     hr = _SHRegisterFolders(HKEY_LOCAL_MACHINE, NULL, szSHUserFolders,
1820      folders, sizeof(folders) / sizeof(folders[0]));
1821     TRACE("returning 0x%08lx\n", hr);
1822     return hr;
1823 }
1824
1825 /* Register the default values in the registry, as some apps seem to depend
1826  * on their presence.  The set registered was taken from Windows XP.
1827  */
1828 HRESULT SHELL_RegisterShellFolders(void)
1829 {
1830     HRESULT hr = _SHRegisterUserShellFolders(TRUE);
1831
1832     if (SUCCEEDED(hr))
1833         hr = _SHRegisterUserShellFolders(FALSE);
1834     if (SUCCEEDED(hr))
1835         hr = _SHRegisterCommonShellFolders();
1836     return hr;
1837 }
1838
1839 /*************************************************************************
1840  * SHGetSpecialFolderPathA [SHELL32.@]
1841  */
1842 BOOL WINAPI SHGetSpecialFolderPathA (
1843         HWND hwndOwner,
1844         LPSTR szPath,
1845         int nFolder,
1846         BOOL bCreate)
1847 {
1848         return (SHGetFolderPathA(
1849                 hwndOwner,
1850                 nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0),
1851                 NULL,
1852                 0,
1853                 szPath)) == S_OK ? TRUE : FALSE;
1854 }
1855
1856 /*************************************************************************
1857  * SHGetSpecialFolderPathW
1858  */
1859 BOOL WINAPI SHGetSpecialFolderPathW (
1860         HWND hwndOwner,
1861         LPWSTR szPath,
1862         int nFolder,
1863         BOOL bCreate)
1864 {
1865         return (SHGetFolderPathW(
1866                 hwndOwner,
1867                 nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0),
1868                 NULL,
1869                 0,
1870                 szPath)) == S_OK ? TRUE : FALSE;
1871 }
1872
1873 /*************************************************************************
1874  * SHGetSpecialFolderPath (SHELL32.175)
1875  */
1876 BOOL WINAPI SHGetSpecialFolderPathAW (
1877         HWND hwndOwner,
1878         LPVOID szPath,
1879         int nFolder,
1880         BOOL bCreate)
1881
1882 {
1883         if (SHELL_OsIsUnicode())
1884           return SHGetSpecialFolderPathW (hwndOwner, szPath, nFolder, bCreate);
1885         return SHGetSpecialFolderPathA (hwndOwner, szPath, nFolder, bCreate);
1886 }
1887
1888 /*************************************************************************
1889  * SHGetFolderLocation [SHELL32.@]
1890  *
1891  * NOTES
1892  * Gets the folder locations from the registry and creates a pidl.
1893  * Creates missing reg keys and directories.
1894  * Mostly forwards to SHGetFolderPathW, but a few values of nFolder return
1895  * virtual folders that are handled here.
1896  *
1897  * PARAMS
1898  *   hwndOwner  [I]
1899  *   nFolder    [I] CSIDL_xxxxx
1900  *   hToken     [I] token representing user, or NULL for current user, or -1 for
1901  *                  default user
1902  *   dwReserved [I] must be zero
1903  *   ppidl      [O] PIDL of a special folder
1904  *
1905  * NOTES
1906  */
1907 HRESULT WINAPI SHGetFolderLocation(
1908         HWND hwndOwner,
1909         int nFolder,
1910         HANDLE hToken,
1911         DWORD dwReserved,
1912         LPITEMIDLIST *ppidl)
1913 {
1914     HRESULT hr = E_INVALIDARG;
1915
1916     TRACE("%p 0x%08x %p 0x%08lx %p\n",
1917      hwndOwner, nFolder, hToken, dwReserved, ppidl);
1918     
1919     if (!ppidl)
1920         return E_INVALIDARG;
1921     if (dwReserved)
1922         return E_INVALIDARG;
1923
1924     /* The virtual folders' locations are not user-dependent */
1925     *ppidl = NULL;
1926     switch (nFolder)
1927     {
1928         case CSIDL_DESKTOP:
1929             *ppidl = _ILCreateDesktop();
1930             break;
1931
1932         case CSIDL_INTERNET:
1933             *ppidl = _ILCreateIExplore();
1934             break;
1935
1936         case CSIDL_CONTROLS:
1937             *ppidl = _ILCreateControlPanel();
1938             break;
1939
1940         case CSIDL_PRINTERS:
1941             *ppidl = _ILCreatePrinters();
1942             break;
1943
1944         case CSIDL_BITBUCKET:
1945             *ppidl = _ILCreateBitBucket();
1946             break;
1947
1948         case CSIDL_DRIVES:
1949             *ppidl = _ILCreateMyComputer();
1950             break;
1951
1952         case CSIDL_NETWORK:
1953             *ppidl = _ILCreateNetwork();
1954             break;
1955
1956         default:
1957         {
1958             WCHAR szPath[MAX_PATH];
1959
1960             hr = SHGetFolderPathW(hwndOwner, nFolder, hToken,
1961              SHGFP_TYPE_CURRENT, szPath);
1962             if (SUCCEEDED(hr))
1963             {
1964                 DWORD attributes=0;
1965
1966                 TRACE("Value=%s\n", debugstr_w(szPath));
1967                 hr = SHILCreateFromPathW(szPath, ppidl, &attributes);
1968             }
1969             else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
1970             {
1971                 /* unlike SHGetFolderPath, SHGetFolderLocation in shell32
1972                  * version 6.0 returns E_FAIL for non-existing paths
1973                  */
1974                 hr = E_FAIL;
1975             }
1976         }
1977     }
1978     if(*ppidl)
1979         hr = NOERROR;
1980
1981     TRACE("-- (new pidl %p)\n",*ppidl);
1982     return hr;
1983 }
1984
1985 /*************************************************************************
1986  * SHGetSpecialFolderLocation           [SHELL32.@]
1987  *
1988  * NOTES
1989  *   In NT5, SHGetSpecialFolderLocation needs the <winntdir>/Recent
1990  *   directory.
1991  */
1992 HRESULT WINAPI SHGetSpecialFolderLocation(
1993         HWND hwndOwner,
1994         INT nFolder,
1995         LPITEMIDLIST * ppidl)
1996 {
1997     HRESULT hr = E_INVALIDARG;
1998
1999     TRACE("(%p,0x%x,%p)\n", hwndOwner,nFolder,ppidl);
2000
2001     if (!ppidl)
2002         return E_INVALIDARG;
2003
2004     hr = SHGetFolderLocation(hwndOwner, nFolder, NULL, 0, ppidl);
2005     return hr;
2006 }