- the correct registry location to override is User Shell Folders, not
[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)
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 a subset of environment variables from szSrc into szDest,
1441  * which is assumed to be MAX_PATH characters in length.  Unlike the standard
1442  * ExpandEnvironmentStringsW, doesn't actually refer to the environment, for
1443  * two reasons:
1444  * - the environment variables may not be set when this is called (as during
1445  *   Wine's installation when default values are being written to the registry)
1446  * - the user perhaps shouldn't be able to override the location of certain
1447  *   directories through modifying the environment
1448  * The supported environment variables, and their source, are:
1449  * - ALLUSERSPROFILE, USERPROFILE: reads from the registry
1450  * - SystemDrive: uses GetSystemDirectoryW and uses the drive portion of its
1451  *   path
1452  * Also unlike ExpandEnvironmentStringsW, only expands a single variable, and
1453  * only in the beginning of szSrc.
1454  */
1455 static HRESULT _SHExpandEnvironmentStrings(LPCWSTR szSrc, LPWSTR szDest)
1456 {
1457     HRESULT hr;
1458     WCHAR szTemp[MAX_PATH], szProfilesPrefix[MAX_PATH] = { 0 };
1459     HKEY key = NULL;
1460
1461     TRACE("%s, %p\n", debugstr_w(szSrc), szDest);
1462
1463     if (!szSrc || !szDest) return E_INVALIDARG;
1464
1465     /* Get the profile prefix, we'll probably be needing it */
1466     hr = _SHOpenProfilesKey(&key);
1467     if (SUCCEEDED(hr))
1468     {
1469         WCHAR szDefaultProfilesPrefix[MAX_PATH];
1470
1471         strcpyW(szDefaultProfilesPrefix, SystemDriveW);
1472         PathAppendW(szDefaultProfilesPrefix, szDefaultProfileDirW);
1473         hr = _SHGetProfilesValue(key, ProfilesDirectoryW, szProfilesPrefix,
1474          szDefaultProfilesPrefix);
1475     }
1476
1477     *szDest = 0;
1478     strcpyW(szTemp, szSrc);
1479     while (SUCCEEDED(hr) && szTemp[0] == '%')
1480     {
1481         if (!strncmpiW(szTemp, AllUsersProfileW, strlenW(AllUsersProfileW)))
1482         {
1483             WCHAR szAllUsers[MAX_PATH];
1484
1485             strcpyW(szDest, szProfilesPrefix);
1486             hr = _SHGetProfilesValue(key, AllUsersProfileValueW,
1487              szAllUsers, AllUsersW);
1488             PathAppendW(szDest, szAllUsers);
1489             PathAppendW(szDest, szTemp + strlenW(AllUsersProfileW));
1490         }
1491         else if (!strncmpiW(szTemp, UserProfileW, strlenW(UserProfileW)))
1492         {
1493             WCHAR userName[MAX_PATH];
1494             DWORD userLen = MAX_PATH;
1495
1496             strcpyW(szDest, szProfilesPrefix);
1497             GetUserNameW(userName, &userLen);
1498             PathAppendW(szDest, userName);
1499             PathAppendW(szDest, szTemp + strlenW(UserProfileW));
1500         }
1501         else if (!strncmpiW(szTemp, SystemDriveW, strlenW(SystemDriveW)))
1502         {
1503             GetSystemDirectoryW(szDest, MAX_PATH);
1504             if (szDest[1] != ':')
1505             {
1506                 FIXME("non-drive system paths unsupported\n");
1507                 hr = E_FAIL;
1508             }
1509             else
1510             {
1511                 strcpyW(szDest + 3, szTemp + strlenW(SystemDriveW) + 1);
1512                 hr = S_OK;
1513             }
1514         }
1515         else
1516         {
1517             WARN("asked for unsupported environment variable in path %s\n",
1518              debugstr_w(szTemp));
1519             hr = E_INVALIDARG;
1520         }
1521         if (SUCCEEDED(hr) && szDest[0] == '%')
1522             strcpyW(szTemp, szDest);
1523         else
1524         {
1525             /* terminate loop */
1526             szTemp[0] = '\0';
1527         }
1528     }
1529     if (key)
1530         RegCloseKey(key);
1531     TRACE("returning 0x%08lx (input was %s, output is %s)\n", hr,
1532      debugstr_w(szSrc), debugstr_w(szDest));
1533     return hr;
1534 }
1535
1536 /*************************************************************************
1537  * SHGetFolderPathW                     [SHELL32.@]
1538  *
1539  * NOTES
1540  * Converts nFolder to path.  Most values can be overridden in either
1541  * HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
1542  * or in the same location in HKLM.
1543  * The registry usage is explained by the following tech note:
1544  * http://www.microsoft.com/windows2000/techinfo/reskit/en-us/default.asp?url=/windows2000/techinfo/reskit/en-us/regentry/36173.asp
1545  * The "Shell Folders" registry key was used in NT4 and earlier systems.
1546  * Beginning with Windows 2000, the "User Shell Folders" key is used, so
1547  * changes made to it are made to the former key too.  This synchronization is
1548  * done on-demand: not until someone requests the value of one of these paths
1549  * (by calling one of the SHGet functions) is the value synchronized.
1550  * Furthermore, as explained here:
1551  * http://www.microsoft.com/windows2000/techinfo/reskit/en-us/default.asp?url=/windows2000/techinfo/reskit/en-us/regentry/36276.asp
1552  * the HKCU paths take precedence over the HKLM paths.
1553  *
1554  **********************************************************************/
1555 HRESULT WINAPI SHGetFolderPathW(
1556         HWND hwndOwner,
1557         int nFolder,
1558         HANDLE hToken,
1559         DWORD dwFlags,
1560         LPWSTR pszPath)
1561 {
1562     HRESULT    hr;
1563     WCHAR      szBuildPath[MAX_PATH], szTemp[MAX_PATH];
1564     DWORD      folder = nFolder & CSIDL_FOLDER_MASK;
1565     CSIDL_Type type;
1566     WCHAR     *p;
1567
1568     TRACE("%p,%p,nFolder=0x%04x\n", hwndOwner,pszPath,nFolder);
1569
1570     /* Windows always NULL-terminates the resulting path regardless of success
1571      * or failure, so do so first
1572      */
1573     if (pszPath)
1574         *pszPath = '\0';
1575     if (folder >= sizeof(CSIDL_Data) / sizeof(CSIDL_Data[0]))
1576         return E_INVALIDARG;
1577     type = CSIDL_Data[folder].type;
1578     switch (type)
1579     {
1580         case CSIDL_Type_Disallowed:
1581             hr = E_INVALIDARG;
1582             break;
1583         case CSIDL_Type_NonExistent:
1584             hr = S_FALSE;
1585             break;
1586         case CSIDL_Type_WindowsPath:
1587             GetWindowsDirectoryW(szTemp, MAX_PATH);
1588             if (CSIDL_Data[folder].szDefaultPath &&
1589              !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
1590              *CSIDL_Data[folder].szDefaultPath)
1591             {
1592                 PathAddBackslashW(szTemp);
1593                 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
1594             }
1595             hr = S_OK;
1596             break;
1597         case CSIDL_Type_SystemPath:
1598             GetSystemDirectoryW(szTemp, MAX_PATH);
1599             if (CSIDL_Data[folder].szDefaultPath &&
1600              !IS_INTRESOURCE(CSIDL_Data[folder].szDefaultPath) &&
1601              *CSIDL_Data[folder].szDefaultPath)
1602             {
1603                 PathAddBackslashW(szTemp);
1604                 strcatW(szTemp, CSIDL_Data[folder].szDefaultPath);
1605             }
1606             hr = S_OK;
1607             break;
1608         case CSIDL_Type_CurrVer:
1609             hr = _SHGetCurrentVersionPath(dwFlags, folder, szTemp);
1610             break;
1611         case CSIDL_Type_User:
1612             hr = _SHGetUserProfilePath(hToken, dwFlags, folder, szTemp);
1613             break;
1614         case CSIDL_Type_AllUsers:
1615             hr = _SHGetAllUsersProfilePath(dwFlags, folder, szTemp);
1616             break;
1617         default:
1618             FIXME("bogus type %d, please fix\n", type);
1619             hr = E_INVALIDARG;
1620     }
1621
1622     /* Expand environment strings if necessary */
1623     if (*szTemp == '%')
1624         hr = _SHExpandEnvironmentStrings(szTemp, szBuildPath);
1625     else
1626         strcpyW(szBuildPath, szTemp);
1627     /* Copy the path if it's available before we might return */
1628     if (pszPath)
1629         strcpyW(pszPath, szBuildPath);
1630
1631     if (FAILED(hr)) goto end;
1632
1633     /* if we don't care about existing directories we are ready */
1634     if(nFolder & CSIDL_FLAG_DONT_VERIFY) goto end;
1635
1636     if (PathFileExistsW(szBuildPath)) goto end;
1637
1638     /* not existing but we are not allowed to create it.  The return value
1639      * is verified against shell32 version 6.0.
1640      */
1641     if (!(nFolder & CSIDL_FLAG_CREATE))
1642     {
1643         hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
1644         goto end;
1645     }
1646
1647     /* create directory/directories */
1648     p = strchrW(szBuildPath, '\\');
1649     while (p)
1650     {
1651         *p = 0;
1652         if (!PathFileExistsW(szBuildPath))
1653         {
1654             TRACE("Creating directory %s\n", debugstr_w(szBuildPath));
1655             if (!CreateDirectoryW(szBuildPath,NULL))
1656             {
1657                 ERR("Failed to create directory '%s'.\n",
1658                  debugstr_w(szBuildPath));
1659                 hr = E_FAIL;
1660                 goto end;
1661             }
1662         }
1663         *p = '\\';
1664         p = strchrW(p+1, '\\');
1665     }
1666     /* last component must be created too. */
1667     if (!PathFileExistsW(szBuildPath))
1668     {
1669         if (!CreateDirectoryW(szBuildPath,NULL))
1670         {
1671             ERR("Failed to create directory '%s'.\n", debugstr_w(szBuildPath));
1672             hr = E_FAIL;
1673             goto end;
1674         }
1675     }
1676
1677     TRACE("Created missing system directory '%s'\n", debugstr_w(szBuildPath));
1678 end:
1679     TRACE("returning 0x%08lx (final path is %s)\n", hr,
1680      debugstr_w(szBuildPath));
1681     return hr;
1682 }
1683
1684 /*************************************************************************
1685  * SHGetFolderPathA                     [SHELL32.@]
1686  */
1687 HRESULT WINAPI SHGetFolderPathA(
1688         HWND hwndOwner,
1689         int nFolder,
1690         HANDLE hToken,
1691         DWORD dwFlags,
1692         LPSTR pszPath)
1693 {
1694     WCHAR szTemp[MAX_PATH];
1695     HRESULT hr;
1696
1697     TRACE("%p,%p,nFolder=0x%04x\n",hwndOwner,pszPath,nFolder);
1698
1699     if (pszPath)
1700         *pszPath = '\0';
1701     hr = SHGetFolderPathW(hwndOwner, nFolder, hToken, dwFlags, szTemp);
1702     if (SUCCEEDED(hr) && pszPath)
1703         WideCharToMultiByte(CP_ACP, 0, szTemp, -1, pszPath, MAX_PATH, NULL,
1704          NULL);
1705
1706     return hr;
1707 }
1708
1709 /* For each folder in folders, if its value has not been set in the registry,
1710  * call _SHGetUserProfilePath or _SHGetAllUsersProfilePath (depending on the
1711  * folder's type) to get the unexpanded value first.
1712  * This will create the expanded value in the Shell Folders key, and
1713  * return the unexpanded value.
1714  * Write the unexpanded value to User Shell Folders, and query it with
1715  * SHGetFolderPath to force the creation of the directory if it doesn't
1716  * already exist.
1717  */
1718 static HRESULT _SHRegisterFolders(HKEY hRootKey, HANDLE hToken,
1719  LPCWSTR szUserShellFolderPath, const UINT folders[], UINT foldersLen)
1720 {
1721     UINT i;
1722     WCHAR path[MAX_PATH];
1723     HRESULT hr = S_OK;
1724     HKEY hKey = NULL;
1725     DWORD dwDisp, dwType, dwPathLen;
1726     LONG ret;
1727
1728     TRACE("%p,%p,%s,%p,%u\n", hRootKey, hToken,
1729      debugstr_w(szUserShellFolderPath), folders, foldersLen);
1730
1731     ret = RegCreateKeyExW(hRootKey, szUserShellFolderPath, 0, NULL, 0,
1732      KEY_ALL_ACCESS, NULL, &hKey, &dwDisp);
1733     if (ret)
1734         hr = HRESULT_FROM_WIN32(ret);
1735     for (i = 0; SUCCEEDED(hr) && i < foldersLen; i++)
1736     {
1737         dwPathLen = MAX_PATH * sizeof(WCHAR);
1738         if (RegQueryValueExW(hKey, CSIDL_Data[folders[i]].szValueName, NULL,
1739          &dwType, (LPBYTE)path, &dwPathLen) || (dwType != REG_SZ &&
1740          dwType != REG_EXPAND_SZ))
1741         {
1742             *path = '\0';
1743             if (CSIDL_Data[folders[i]].type == CSIDL_Type_User)
1744                 _SHGetUserProfilePath(hToken, SHGFP_TYPE_DEFAULT, folders[i],
1745                  path);
1746             else if (CSIDL_Data[folders[i]].type == CSIDL_Type_AllUsers)
1747                 _SHGetAllUsersProfilePath(SHGFP_TYPE_DEFAULT, folders[i], path);
1748             else
1749                 hr = E_FAIL;
1750             if (*path)
1751             {
1752                 ret = RegSetValueExW(hKey, CSIDL_Data[folders[i]].szValueName,
1753                  0, REG_EXPAND_SZ, (LPBYTE)path,
1754                  (strlenW(path) + 1) * sizeof(WCHAR));
1755                 if (ret)
1756                     hr = HRESULT_FROM_WIN32(ret);
1757                 else
1758                     hr = SHGetFolderPathW(NULL, folders[i] | CSIDL_FLAG_CREATE,
1759                      hToken, SHGFP_TYPE_DEFAULT, NULL);
1760             }
1761         }
1762     }
1763     if (hKey)
1764         RegCloseKey(hKey);
1765
1766     TRACE("returning 0x%08lx\n", hr);
1767     return hr;
1768 }
1769
1770 static HRESULT _SHRegisterUserShellFolders(BOOL bDefault)
1771 {
1772     static const UINT folders[] = {
1773      CSIDL_PROGRAMS,
1774      CSIDL_PERSONAL,
1775      CSIDL_FAVORITES,
1776      CSIDL_STARTUP,
1777      CSIDL_RECENT,
1778      CSIDL_SENDTO,
1779      CSIDL_STARTMENU,
1780      CSIDL_DESKTOPDIRECTORY,
1781      CSIDL_NETHOOD,
1782      CSIDL_TEMPLATES,
1783      CSIDL_PRINTHOOD,
1784      CSIDL_COOKIES,
1785      CSIDL_HISTORY,
1786     };
1787     WCHAR userShellFolderPath[MAX_PATH];
1788     LPCWSTR pUserShellFolderPath;
1789     HRESULT hr = S_OK;
1790     HKEY hRootKey;
1791     HANDLE hToken;
1792
1793     TRACE("%s\n", bDefault ? "TRUE" : "FALSE");
1794     if (bDefault)
1795     {
1796         hToken = (HANDLE)-1;
1797         hRootKey = HKEY_USERS;
1798         strcpyW(userShellFolderPath, DefaultW);
1799         PathAddBackslashW(userShellFolderPath);
1800         strcatW(userShellFolderPath, szSHUserFolders);
1801         pUserShellFolderPath = userShellFolderPath;
1802     }
1803     else
1804     {
1805         hToken = NULL;
1806         hRootKey = HKEY_CURRENT_USER;
1807         pUserShellFolderPath = szSHUserFolders;
1808     }
1809
1810     hr = _SHRegisterFolders(hRootKey, hToken, pUserShellFolderPath,
1811      folders, sizeof(folders) / sizeof(folders[0]));
1812     TRACE("returning 0x%08lx\n", hr);
1813     return hr;
1814 }
1815
1816 static HRESULT _SHRegisterCommonShellFolders(void)
1817 {
1818     static const UINT folders[] = {
1819      CSIDL_COMMON_STARTMENU,
1820      CSIDL_COMMON_PROGRAMS,
1821      CSIDL_COMMON_STARTUP,
1822      CSIDL_COMMON_DESKTOPDIRECTORY,
1823      CSIDL_COMMON_FAVORITES,
1824      CSIDL_COMMON_APPDATA,
1825      CSIDL_COMMON_TEMPLATES,
1826      CSIDL_COMMON_DOCUMENTS,
1827     };
1828     HRESULT hr;
1829
1830     TRACE("\n");
1831     hr = _SHRegisterFolders(HKEY_LOCAL_MACHINE, NULL, szSHUserFolders,
1832      folders, sizeof(folders) / sizeof(folders[0]));
1833     TRACE("returning 0x%08lx\n", hr);
1834     return hr;
1835 }
1836
1837 /* Register the default values in the registry, as some apps seem to depend
1838  * on their presence.  The set registered was taken from Windows XP.
1839  */
1840 HRESULT SHELL_RegisterShellFolders(void)
1841 {
1842     HRESULT hr = _SHRegisterUserShellFolders(TRUE);
1843
1844     if (SUCCEEDED(hr))
1845         hr = _SHRegisterUserShellFolders(FALSE);
1846     if (SUCCEEDED(hr))
1847         hr = _SHRegisterCommonShellFolders();
1848     return hr;
1849 }
1850
1851 /*************************************************************************
1852  * SHGetSpecialFolderPathA [SHELL32.@]
1853  */
1854 BOOL WINAPI SHGetSpecialFolderPathA (
1855         HWND hwndOwner,
1856         LPSTR szPath,
1857         int nFolder,
1858         BOOL bCreate)
1859 {
1860         return (SHGetFolderPathA(
1861                 hwndOwner,
1862                 nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0),
1863                 NULL,
1864                 0,
1865                 szPath)) == S_OK ? TRUE : FALSE;
1866 }
1867
1868 /*************************************************************************
1869  * SHGetSpecialFolderPathW
1870  */
1871 BOOL WINAPI SHGetSpecialFolderPathW (
1872         HWND hwndOwner,
1873         LPWSTR szPath,
1874         int nFolder,
1875         BOOL bCreate)
1876 {
1877         return (SHGetFolderPathW(
1878                 hwndOwner,
1879                 nFolder + (bCreate ? CSIDL_FLAG_CREATE : 0),
1880                 NULL,
1881                 0,
1882                 szPath)) == S_OK ? TRUE : FALSE;
1883 }
1884
1885 /*************************************************************************
1886  * SHGetSpecialFolderPath (SHELL32.175)
1887  */
1888 BOOL WINAPI SHGetSpecialFolderPathAW (
1889         HWND hwndOwner,
1890         LPVOID szPath,
1891         int nFolder,
1892         BOOL bCreate)
1893
1894 {
1895         if (SHELL_OsIsUnicode())
1896           return SHGetSpecialFolderPathW (hwndOwner, szPath, nFolder, bCreate);
1897         return SHGetSpecialFolderPathA (hwndOwner, szPath, nFolder, bCreate);
1898 }
1899
1900 /*************************************************************************
1901  * SHGetFolderLocation [SHELL32.@]
1902  *
1903  * NOTES
1904  * Gets the folder locations from the registry and creates a pidl.
1905  * Creates missing reg keys and directories.
1906  * Mostly forwards to SHGetFolderPathW, but a few values of nFolder return
1907  * virtual folders that are handled here.
1908  *
1909  * PARAMS
1910  *   hwndOwner  [I]
1911  *   nFolder    [I] CSIDL_xxxxx
1912  *   hToken     [I] token representing user, or NULL for current user, or -1 for
1913  *                  default user
1914  *   dwReserved [I] must be zero
1915  *   ppidl      [O] PIDL of a special folder
1916  *
1917  * NOTES
1918  */
1919 HRESULT WINAPI SHGetFolderLocation(
1920         HWND hwndOwner,
1921         int nFolder,
1922         HANDLE hToken,
1923         DWORD dwReserved,
1924         LPITEMIDLIST *ppidl)
1925 {
1926     HRESULT hr = E_INVALIDARG;
1927
1928     TRACE("%p 0x%08x %p 0x%08lx %p\n",
1929      hwndOwner, nFolder, hToken, dwReserved, ppidl);
1930     
1931     if (!ppidl)
1932         return E_INVALIDARG;
1933     if (dwReserved)
1934         return E_INVALIDARG;
1935
1936     /* The virtual folders' locations are not user-dependent */
1937     *ppidl = NULL;
1938     switch (nFolder)
1939     {
1940         case CSIDL_DESKTOP:
1941             *ppidl = _ILCreateDesktop();
1942             break;
1943
1944         case CSIDL_INTERNET:
1945             *ppidl = _ILCreateIExplore();
1946             break;
1947
1948         case CSIDL_CONTROLS:
1949             *ppidl = _ILCreateControlPanel();
1950             break;
1951
1952         case CSIDL_PRINTERS:
1953             *ppidl = _ILCreatePrinters();
1954             break;
1955
1956         case CSIDL_BITBUCKET:
1957             *ppidl = _ILCreateBitBucket();
1958             break;
1959
1960         case CSIDL_DRIVES:
1961             *ppidl = _ILCreateMyComputer();
1962             break;
1963
1964         case CSIDL_NETWORK:
1965             *ppidl = _ILCreateNetwork();
1966             break;
1967
1968         default:
1969         {
1970             WCHAR szPath[MAX_PATH];
1971
1972             hr = SHGetFolderPathW(hwndOwner, nFolder, hToken,
1973              SHGFP_TYPE_CURRENT, szPath);
1974             if (SUCCEEDED(hr))
1975             {
1976                 DWORD attributes=0;
1977
1978                 TRACE("Value=%s\n", debugstr_w(szPath));
1979                 hr = SHILCreateFromPathW(szPath, ppidl, &attributes);
1980             }
1981             else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
1982             {
1983                 /* unlike SHGetFolderPath, SHGetFolderLocation in shell32
1984                  * version 6.0 returns E_FAIL for non-existing paths
1985                  */
1986                 hr = E_FAIL;
1987             }
1988         }
1989     }
1990     if(*ppidl)
1991         hr = NOERROR;
1992
1993     TRACE("-- (new pidl %p)\n",*ppidl);
1994     return hr;
1995 }
1996
1997 /*************************************************************************
1998  * SHGetSpecialFolderLocation           [SHELL32.@]
1999  *
2000  * NOTES
2001  *   In NT5, SHGetSpecialFolderLocation needs the <winntdir>/Recent
2002  *   directory.
2003  */
2004 HRESULT WINAPI SHGetSpecialFolderLocation(
2005         HWND hwndOwner,
2006         INT nFolder,
2007         LPITEMIDLIST * ppidl)
2008 {
2009     HRESULT hr = E_INVALIDARG;
2010
2011     TRACE("(%p,0x%x,%p)\n", hwndOwner,nFolder,ppidl);
2012
2013     if (!ppidl)
2014         return E_INVALIDARG;
2015
2016     hr = SHGetFolderLocation(hwndOwner, nFolder, NULL, 0, ppidl);
2017     return hr;
2018 }